Skip to content

ci: align shared security and release tooling#121

Merged
acgetchell merged 2 commits into
mainfrom
ci/117-shared-security-release-tooling
Jun 3, 2026
Merged

ci: align shared security and release tooling#121
acgetchell merged 2 commits into
mainfrom
ci/117-shared-security-release-tooling

Conversation

@acgetchell
Copy link
Copy Markdown
Owner

@acgetchell acgetchell commented Jun 3, 2026

  • Run CI through pinned uv and cached Cargo tooling across all supported platforms.
  • Add repository Semgrep rules, action allowlist coverage, CodeQL, zizmor, and SARIF workflows.
  • Raise the Rust and Python tooling floors to Rust 1.96 and Python 3.12.
  • Add archive-aware changelog generation, post-processing, and tag-release tooling.
  • Preserve exact arithmetic overflow reporting without non-finite sentinel defaults.

Closes #117

Summary by CodeRabbit

  • New Features

    • Added repository security analysis workflows (CodeQL, Semgrep, Zizmor) and a changelog archiving CLI.
  • Documentation

    • Added SECURITY.md; reorganized and archived CHANGELOG content; refreshed README and reference docs; updated changelog generation templates.
  • Chores

    • Bumped Python to 3.12 and Rust to 1.96; added Dependabot cooldown; consolidated and pinned CI/tooling workflows; added dprint YAML formatting; small benchmark accumulation improvements.

- Run CI through pinned uv and cached Cargo tooling across all supported platforms.
- Add repository Semgrep rules, action allowlist coverage, CodeQL, zizmor, and SARIF workflows.
- Raise the Rust and Python tooling floors to Rust 1.96 and Python 3.12.
- Add archive-aware changelog generation, post-processing, and tag-release tooling.
- Preserve exact arithmetic overflow reporting without non-finite sentinel defaults.

Closes #117
@acgetchell acgetchell self-assigned this Jun 3, 2026
@acgetchell acgetchell enabled auto-merge June 3, 2026 00:58
@github-advanced-security
Copy link
Copy Markdown

You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.

What Enabling Code Scanning Means:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 3, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

Modernizes CI/security workflows and tooling, pins and caches cargo-installed tools, adds Semgrep/CodeQL/zizmor runs and SECURITY.md, implements changelog postprocessing + archival CLI with tests, tightens exact→f64 overflow handling, and updates docs/metadata and benchmarks.

Changes

CI/Tooling Modernization & Changelog Automation

Layer / File(s) Summary
Security workflows & rules
.github/workflows/codeql.yml, .github/workflows/semgrep-sarif.yml, .github/workflows/zizmor.yml, semgrep.yaml, SECURITY.md
Add CodeQL, Semgrep SARIF upload, zizmor job, repository Semgrep rules, and project SECURITY.md.
Workflow hardening & cached installs
.github/dependabot.yml, .github/workflows/audit.yml, .github/workflows/benchmarks.yml, .github/workflows/codacy.yml, .github/workflows/codecov.yml, .github/workflows/rust-clippy.yml
Disable credential persistence on checkout, add Dependabot cooldowns, pin action SHAs, and replace raw cargo installs with cached cargo-install action usage.
CI infrastructure & developer tooling
justfile, pyproject.toml, dprint.json, Cargo.toml, rust-toolchain.toml, .python-version, ty.toml
Centralize pinned tool versions, switch to uv/cargo-installs, adopt cargo-nextest and dprint YAML formatting, and bump Python/Rust toolchain pins.
Changelog archival & postprocess
scripts/archive_changelog.py, scripts/postprocess_changelog.py, scripts/tag_release.py, cliff.toml, CHANGELOG.md, docs/archive/changelog/*
Add archival script, extensive postprocessing (PR summaries, reflow, list/heading normalization), integrate archive-aware tag extraction, and update git-cliff templates and changelog layout.
Python scripts & subprocess utils
scripts/subprocess_utils.py, scripts/bench_compare.py, scripts/criterion_dim_plot.py, scripts/tests/*
Add hardened subprocess helpers (run_cargo_command, run_safe_command, git helpers, project-root discovery), switch scripts to tomllib parsing, and expand tests for archival/postprocessing and helpers.
Exact arithmetic & benchmarks
src/exact.rs, tests/proptest_exact.rs, benches/vs_linalg.rs
Treat BigRational::to_f64() None as Overflow error (with index info) and switch accumulation to fused mul_add in benchmarks/tests.
Docs & metadata tweaks
AGENTS.md, README.md, REFERENCES.md, .gitignore, CHANGELOG.md
Reflow and formatting-only documentation changes; remove uv.lock from .gitignore; add archived changelog files and update changelog layout and metadata.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

documentation, enhancement, rust, testing

Poem

🐰 I hopped through CI, lint, and test,

archives tucked tidy, commits at rest.
Tools pinned, rules guarding every path,
changelogs split with archive math.
A carrot for reviewers — quick glance, then jest.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/117-shared-security-release-tooling

@acgetchell
Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 3, 2026

✅ Action performed

Full review finished.

@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 3, 2026

Codecov Report

❌ Patch coverage is 42.85714% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 99.62%. Comparing base (9359256) to head (19b10d5).

Files with missing lines Patch % Lines
src/exact.rs 42.85% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #121      +/-   ##
==========================================
- Coverage   99.81%   99.62%   -0.19%     
==========================================
  Files           5        5              
  Lines        2146     2150       +4     
==========================================
  Hits         2142     2142              
- Misses          4        8       +4     
Flag Coverage Δ
unittests 99.62% <42.85%> (-0.19%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
scripts/tag_release.py (1)

1-1: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Switch scripts/tag_release.py entrypoint from python3 to uv run
File: scripts/tag_release.py still uses #!/usr/bin/env python3, which violates the **/*.py guideline to use #!/usr/bin/env -S uv run (to avoid drifting from the pinned CI/type-check environment). scripts/subprocess_utils.py has the same shebang issue.

Suggested fix
-#!/usr/bin/env python3
+#!/usr/bin/env -S uv run
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/tag_release.py` at line 1, Update the shebang lines in the scripts to
use the pinned CI/type-check runtime: replace the current "#!/usr/bin/env
python3" in scripts/tag_release.py (and the same occurrence in
scripts/subprocess_utils.py) with "#!/usr/bin/env -S uv run" so the scripts
execute under the pinned uv environment instead of the system python3; ensure
only the shebang line is changed and nothing else in the files (no function or
behavior modifications).
scripts/subprocess_utils.py (1)

1-20: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Switch scripts/subprocess_utils.py shebang to uv run to avoid Python 3.12 syntax failures

scripts/subprocess_utils.py uses Python 3.12-only type syntax (type RunKwargs = ...) and the repo declares requires-python = ">=3.12", but its shebang is #!/usr/bin/env python3, which can still resolve to an older 3.x on PATH—leading to import/runtime failure. Use uv run so the correct interpreter/environment is selected.

Suggested fix
-#!/usr/bin/env python3
+#!/usr/bin/env -S uv run
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/subprocess_utils.py` around lines 1 - 20, The shebang at the top of
scripts/subprocess_utils.py can invoke an older python3 on PATH causing the
newer 3.12-only syntax (the type alias RunKwargs) to fail; update the first line
to use the environment launcher "uv run" (e.g., replace "#!/usr/bin/env python3"
with a shebang that invokes "uv run" so the repo's declared Python >=3.12 is
used), ensure the file remains executable, and verify the module imports/type
alias RunKwargs load correctly under the new interpreter.
🧹 Nitpick comments (2)
justfile (1)

750-755: 💤 Low value

Clarify the yaml-lint alias purpose.

The yaml-lint recipe now aliases yaml-check (dprint-based YAML formatting check), while zizmor is a separate recipe for GitHub Actions security analysis. This is semantically correct but may surprise users expecting yaml-lint to perform structural validation rather than formatting checks.

Consider adding a brief comment above yaml-lint noting it's now an alias for format checking.

📝 Suggested clarification
+# yaml-lint now delegates to yaml-check (dprint format verification).
+# For GitHub Actions security scanning, use: just zizmor
 yaml-lint: yaml-check
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@justfile` around lines 750 - 755, Add a short comment above the yaml-lint
alias clarifying it is an alias for the formatting/format-checking recipe
yaml-check (dprint-based), e.g., "yaml-lint is a formatting check alias for
yaml-check (dprint) — not a structural/validation linter"; leave the alias
definition (yaml-lint: yaml-check) unchanged and ensure the zendizmor/zizmor
recipe reference remains separate.
scripts/tests/test_tag_release.py (1)

119-152: ⚡ Quick win

Add one archive-fallback test for extract_changelog_section().

The changed behavior here is archive-aware extraction, but these cases still only cover sections found in the root CHANGELOG.md. Please add a tmp-path case that writes docs/archive/changelog/0.1.md, extracts an archived version, and asserts the returned source points at the archive file.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/tests/test_tag_release.py` around lines 119 - 152, Add a new test
method in TestExtractChangelogSection that creates an archived changelog file
under tmp_path / "docs" / "archive" / "changelog" / "0.1.md", writes a changelog
entry for the target version (e.g., header and some unique text), calls
extract_changelog_section(...) with that version string, and asserts the
returned section contains the unique text and that the returned source equals
the archive file path; reference extract_changelog_section and the
TestExtractChangelogSection class when adding the test.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cliff.toml`:
- Around line 95-98: The current generic message regex "(?i)^bump " in the TOML
entry will misclassify human-authored "Bump ..." commits; update that message
pattern to be more specific so only dependency/version bump commits match (for
example require "bump" to be followed by explicit dependency indicators such as
"deps", "dependencies", "packages", "versions", or a dependency
identifier/version token like an @, /, or a semantic version number), replacing
the existing "(?i)^bump " pattern in the message field so ordinary prose "Bump
MSRV..." subjects no longer land in the Dependencies group.

In `@scripts/tests/test_subprocess_utils.py`:
- Around line 134-141: The test test_git_convenience_helpers is brittle because
it calls real git and can fail in shallow/stripped checkouts; update the test to
mock the underlying git interactions instead of calling the real repo: patch the
helper used by subprocess_utils (e.g., monkeypatch subprocess_utils.git or
subprocess.run used by get_git_commit_hash, get_git_remote_url, check_git_repo,
check_git_history) to return deterministic stdout/returncodes that simulate a
normal repo, then assert the helpers behave as expected; alternatively move
these assertions into an integration test; ensure you reference the functions
get_git_commit_hash, get_git_remote_url, check_git_repo, and check_git_history
when locating where to apply the mocks.

In `@semgrep.yaml`:
- Around line 104-117: The Semgrep rule
la-stack.github-actions.external-action-version-comment currently only matches
when the SHA is the last token on the line (pattern-regex) and therefore allows
lines like "uses: owner/action@<sha> # pinned" to bypass the check; tighten the
rule by changing the pattern-regex (or replacing it with a structured Semgrep
pattern) so it requires a trailing comment that looks like a version (e.g.,
match "#\s*v?\d+(\.\d+)*" or similar after the 40‑char SHA) and treat lines that
lack such a version-shaped comment as violations; update the rule's
pattern-regex reference to include the SHA followed by optional whitespace and
then a version-style comment and ensure
la-stack.github-actions.external-action-version-comment flags missing or
non-version comments.

---

Outside diff comments:
In `@scripts/subprocess_utils.py`:
- Around line 1-20: The shebang at the top of scripts/subprocess_utils.py can
invoke an older python3 on PATH causing the newer 3.12-only syntax (the type
alias RunKwargs) to fail; update the first line to use the environment launcher
"uv run" (e.g., replace "#!/usr/bin/env python3" with a shebang that invokes "uv
run" so the repo's declared Python >=3.12 is used), ensure the file remains
executable, and verify the module imports/type alias RunKwargs load correctly
under the new interpreter.

In `@scripts/tag_release.py`:
- Line 1: Update the shebang lines in the scripts to use the pinned
CI/type-check runtime: replace the current "#!/usr/bin/env python3" in
scripts/tag_release.py (and the same occurrence in scripts/subprocess_utils.py)
with "#!/usr/bin/env -S uv run" so the scripts execute under the pinned uv
environment instead of the system python3; ensure only the shebang line is
changed and nothing else in the files (no function or behavior modifications).

---

Nitpick comments:
In `@justfile`:
- Around line 750-755: Add a short comment above the yaml-lint alias clarifying
it is an alias for the formatting/format-checking recipe yaml-check
(dprint-based), e.g., "yaml-lint is a formatting check alias for yaml-check
(dprint) — not a structural/validation linter"; leave the alias definition
(yaml-lint: yaml-check) unchanged and ensure the zendizmor/zizmor recipe
reference remains separate.

In `@scripts/tests/test_tag_release.py`:
- Around line 119-152: Add a new test method in TestExtractChangelogSection that
creates an archived changelog file under tmp_path / "docs" / "archive" /
"changelog" / "0.1.md", writes a changelog entry for the target version (e.g.,
header and some unique text), calls extract_changelog_section(...) with that
version string, and asserts the returned section contains the unique text and
that the returned source equals the archive file path; reference
extract_changelog_section and the TestExtractChangelogSection class when adding
the test.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 00997127-53f5-43c0-aaed-e8a373693a7c

📥 Commits

Reviewing files that changed from the base of the PR and between 9359256 and b303509.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (42)
  • .github/dependabot.yml
  • .github/workflows/audit.yml
  • .github/workflows/benchmarks.yml
  • .github/workflows/ci.yml
  • .github/workflows/codacy.yml
  • .github/workflows/codecov.yml
  • .github/workflows/codeql.yml
  • .github/workflows/rust-clippy.yml
  • .github/workflows/semgrep-sarif.yml
  • .github/workflows/zizmor.yml
  • .gitignore
  • .python-version
  • AGENTS.md
  • CHANGELOG.md
  • Cargo.toml
  • README.md
  • REFERENCES.md
  • SECURITY.md
  • benches/vs_linalg.rs
  • cliff.toml
  • docs/archive/changelog/0.1.md
  • docs/archive/changelog/0.2.md
  • docs/archive/changelog/0.3.md
  • dprint.json
  • justfile
  • pyproject.toml
  • rust-toolchain.toml
  • scripts/archive_changelog.py
  • scripts/bench_compare.py
  • scripts/criterion_dim_plot.py
  • scripts/postprocess_changelog.py
  • scripts/subprocess_utils.py
  • scripts/tag_release.py
  • scripts/tests/test_archive_changelog.py
  • scripts/tests/test_criterion_dim_plot.py
  • scripts/tests/test_postprocess_changelog.py
  • scripts/tests/test_subprocess_utils.py
  • scripts/tests/test_tag_release.py
  • semgrep.yaml
  • src/exact.rs
  • tests/proptest_exact.rs
  • ty.toml
💤 Files with no reviewable changes (2)
  • .gitignore
  • scripts/tests/test_criterion_dim_plot.py

Comment thread cliff.toml
Comment on lines +95 to +98
# Dependency bumps: keep all bump commits so archived release history
# preserves CI/action/tooling dependency updates as well as Rust crates.
{ message = "^chore\\(deps\\): bump ", group = "Dependencies" },
{ message = "(?i)^bump ", group = "Dependencies" },
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Narrow the generic bump parser before it swallows non-dependency commits.

(?i)^bump will also catch human-authored subjects like Bump MSRV to Rust 1.95..., so those entries will start landing under Dependencies instead of Maintenance or Changed.

Suggested adjustment
-    { message = "(?i)^bump ", group = "Dependencies" },
+    { message = '(?i)^bump (?:the dependencies group\b|[A-Za-z0-9_.-]+(?:/[A-Za-z0-9_.-]+)? from )', group = "Dependencies" },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cliff.toml` around lines 95 - 98, The current generic message regex
"(?i)^bump " in the TOML entry will misclassify human-authored "Bump ..."
commits; update that message pattern to be more specific so only
dependency/version bump commits match (for example require "bump" to be followed
by explicit dependency indicators such as "deps", "dependencies", "packages",
"versions", or a dependency identifier/version token like an @, /, or a semantic
version number), replacing the existing "(?i)^bump " pattern in the message
field so ordinary prose "Bump MSRV..." subjects no longer land in the
Dependencies group.

Comment on lines +134 to +141
def test_git_convenience_helpers(self) -> None:
assert get_git_commit_hash()
assert get_git_remote_url()
assert check_git_repo() is True
assert check_git_history() is True

def test_find_project_root(self) -> None:
assert (find_project_root() / "Cargo.toml").is_file()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Make the git-helper test independent of the checkout state.

This test currently depends on the sandbox having a configured remote and enough git history. get_git_remote_url() and check_git_history() will fail in shallow clones, source archives, or stripped CI checkouts even if subprocess_utils is correct. Please mock the underlying git calls here the same way Lines 118-132 already do, or move this to an explicit integration test.

Suggested direction
-    def test_git_convenience_helpers(self) -> None:
-        assert get_git_commit_hash()
-        assert get_git_remote_url()
-        assert check_git_repo() is True
-        assert check_git_history() is True
+    `@patch`("subprocess_utils.run_git_command")
+    def test_git_convenience_helpers(self, mock_run: MagicMock) -> None:
+        mock_run.side_effect = [
+            MagicMock(stdout="abc1234\n"),
+            MagicMock(stdout="https://example.invalid/acgetchell/la-stack.git\n"),
+            MagicMock(returncode=0),
+            MagicMock(returncode=0),
+        ]
+
+        assert get_git_commit_hash() == "abc1234"
+        assert get_git_remote_url() == "https://example.invalid/acgetchell/la-stack.git"
+        assert check_git_repo() is True
+        assert check_git_history() is True
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/tests/test_subprocess_utils.py` around lines 134 - 141, The test
test_git_convenience_helpers is brittle because it calls real git and can fail
in shallow/stripped checkouts; update the test to mock the underlying git
interactions instead of calling the real repo: patch the helper used by
subprocess_utils (e.g., monkeypatch subprocess_utils.git or subprocess.run used
by get_git_commit_hash, get_git_remote_url, check_git_repo, check_git_history)
to return deterministic stdout/returncodes that simulate a normal repo, then
assert the helpers behave as expected; alternatively move these assertions into
an integration test; ensure you reference the functions get_git_commit_hash,
get_git_remote_url, check_git_repo, and check_git_history when locating where to
apply the mocks.

Comment thread semgrep.yaml
Comment on lines +104 to +117
- id: la-stack.github-actions.external-action-version-comment
languages:
- regex
severity: WARNING
message: "Keep a readable version comment next to external GitHub Action SHA pins."
metadata:
category: maintainability
rationale: "Version comments make Dependabot updates and human review manageable."
paths:
include:
- "/.github/workflows/**/*.yml"
- "/.github/workflows/**/*.yaml"
patterns:
- pattern-regex: '(?m)^\s*uses:\s*(?!\./)(?!docker://)[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+(?:/[A-Za-z0-9_.-]+)?@[a-fA-F0-9]{40}\s*$'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rule='^\s*uses:\s*(?!\./)(?!docker://)[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+(?:/[A-Za-z0-9_.-]+)?@[a-fA-F0-9]{40}\s*$'

bad='uses: codacy/codacy-analysis-cli-action@562ee3e92b8e92df8b67e0a5ff8aa8e261919c08 # pinned'
good='uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2'

printf '%s\n' "$bad"  | grep -P "$rule" >/dev/null && echo "bad matched"  || echo "bad not matched"
printf '%s\n' "$good" | grep -P "$rule" >/dev/null && echo "good matched" || echo "good not matched"

Repository: acgetchell/la-stack

Length of output: 96


🏁 Script executed:

#!/bin/bash
set -euo pipefail

nl -ba semgrep.yaml | sed -n '80,150p'

Repository: acgetchell/la-stack

Length of output: 106


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the referenced rule area with an available line-numbering tool
cat -n semgrep.yaml | sed -n '90,140p'

Repository: acgetchell/la-stack

Length of output: 1726


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rule='^\s*uses:\s*(?!\./)(?!docker://)[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+(?:/[A-Za-z0-9_.-]+)?@[a-fA-F0-9]{40}\s*$'
no_comment='uses: codacy/codacy-analysis-cli-action@562ee3e92b8e92df8b67e0a5ff8aa8e261919c08'

printf '%s\n' "$no_comment" | grep -P "$rule" >/dev/null && echo "no_comment matched" || echo "no_comment not matched"

Repository: acgetchell/la-stack

Length of output: 82


Tighten Semgrep rule to require a version-shaped comment (not just “no trailing text”).

la-stack.github-actions.external-action-version-comment only matches when the SHA is the last token on the line (...@[a-fA-F0-9]{40}\s*$). So uses: owner/action@<sha> # pinned (or # v6.0.2) avoids the finding entirely—meaning the rule enforces “missing trailing comment” rather than “readable version comment exists,” weakening the intended supply-chain policy.

Update the regex (or use a structured Semgrep pattern) to require a # ... version suffix after the SHA (e.g., # v1.2.3 / # 1.2.3) and flag when it’s missing or doesn’t look like a version.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@semgrep.yaml` around lines 104 - 117, The Semgrep rule
la-stack.github-actions.external-action-version-comment currently only matches
when the SHA is the last token on the line (pattern-regex) and therefore allows
lines like "uses: owner/action@<sha> # pinned" to bypass the check; tighten the
rule by changing the pattern-regex (or replacing it with a structured Semgrep
pattern) so it requires a trailing comment that looks like a version (e.g.,
match "#\s*v?\d+(\.\d+)*" or similar after the 40‑char SHA) and treat lines that
lack such a version-shaped comment as violations; update the rule's
pattern-regex reference to include the SHA followed by optional whitespace and
then a version-style comment and ensure
la-stack.github-actions.external-action-version-comment flags missing or
non-version comments.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
scripts/tag_release.py (1)

1-1: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Update shebang to use uv run per repository guidelines.

As per coding guidelines, all Python scripts under scripts/**/*.py should use uv run; never invoke python3 or python directly. The other scripts in this PR (postprocess_changelog.py, archive_changelog.py) use the correct shebang.

Proposed fix
-#!/usr/bin/env python3
+#!/usr/bin/env -S uv run

Based on learnings: Repository Python scripts should use uv run for execution. As per coding guidelines: Use uv run for all Python scripts; never invoke python3 or python directly.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/tag_release.py` at line 1, Replace the shebang in
scripts/tag_release.py that currently invokes python3 with the
repository-standard uv run shebang used by the other scripts (i.e., change the
"#!/usr/bin/env python3" line to the equivalent that runs via uv run), and
ensure the file remains executable; locate the shebang at the top of the file to
make this one-line change so the script uses uv run instead of calling python
directly.
justfile (1)

539-554: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

setup-tools verifies jq but never installs or guides it.

The required-commands loop includes jq, yet (unlike the cargo/uv tools above) nothing in setup-tools installs or points to a jq install. On a host without jq, this fails with ✗ jq and the generic "Fix the installs above" message, even though there is no jq step above to fix. Consider adding a guidance line (or install path) for jq, consistent with _ensure-jq's hint.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@justfile` around lines 539 - 554, The required-commands check in setup-tools
currently lists "jq" but setup-tools never installs or guides how to install it;
update setup-tools to either invoke the existing _ensure-jq helper or add a
clear guidance/install step for jq so the "✗ jq" case can be resolved.
Specifically, modify the setup-tools recipe to call _ensure-jq (or echo the same
installation hint used by _ensure-jq) before the verification loop so jq is
installed or a direct instruction is printed when missing, ensuring the
required-commands loop's jq entry is actionable.
♻️ Duplicate comments (2)
cliff.toml (1)

98-98: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Narrow the generic bump parser before it swallows non-dependency commits.

The past review comment on lines 95-98 flagged this same issue: (?i)^bump will also catch human-authored subjects like Bump MSRV to Rust 1.95..., causing those entries to land under Dependencies instead of Maintenance or Changed.

Suggested adjustment
-    { message = "(?i)^bump ", group = "Dependencies" },
+    { message = '(?i)^bump (?:the dependencies group\b|[A-Za-z0-9_.-]+(?:/[A-Za-z0-9_.-]+)? from )', group = "Dependencies" },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cliff.toml` at line 98, The current generic parser message pattern message =
"(?i)^bump " catches human-authored subjects; tighten it by replacing that
pattern with a regex that requires either an explicit "deps"/"dependencies"
keyword or a package identifier after "bump" (for example using a word boundary
and requiring bump\s+(deps?|dependencies|[A-Za-z0-9_\-\/]+)), so update the
message value used for grouping "Dependencies" accordingly to avoid matching
generic "Bump MSRV..." subjects.
scripts/tests/test_subprocess_utils.py (1)

134-138: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Mock the git helpers to avoid CI brittleness.

This test still calls real git commands (get_git_commit_hash(), get_git_remote_url(), check_git_repo(), check_git_history()) and will fail in shallow clones, source archives, or stripped CI checkouts. Mock the underlying run_git_command calls to return deterministic results, as already done in lines 118-132.

🔒 Mock pattern for git helpers
+    `@patch`("subprocess_utils.run_git_command")
-    def test_git_convenience_helpers(self) -> None:
+    def test_git_convenience_helpers(self, mock_run: MagicMock) -> None:
+        mock_run.side_effect = [
+            MagicMock(stdout="abc1234\n"),
+            MagicMock(stdout="https://example.invalid/acgetchell/la-stack.git\n"),
+            MagicMock(returncode=0),
+            MagicMock(returncode=0),
+        ]
+
-        assert get_git_commit_hash()
-        assert get_git_remote_url()
+        assert get_git_commit_hash() == "abc1234"
+        assert get_git_remote_url() == "https://example.invalid/acgetchell/la-stack.git"
         assert check_git_repo() is True
         assert check_git_history() is True
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/tests/test_subprocess_utils.py` around lines 134 - 138, The test
test_git_convenience_helpers currently invokes real git and is brittle; change
it to mock the underlying run_git_command calls used by get_git_commit_hash,
get_git_remote_url, check_git_repo, and check_git_history so they return
deterministic values (e.g. a fake commit hash, a fake remote URL, and success
outputs) instead of running git. Use the same mocking pattern used earlier in
the file (patch/monkeypatch run_git_command) and ensure each helper is asserted
against the mocked return values rather than relying on the real environment.
🧹 Nitpick comments (3)
.github/workflows/ci.yml (1)

73-76: 💤 Low value

Consider sourcing the Python version from .python-version.

.python-version already pins 3.12; hardcoding "3.12" here creates a second source of truth that can silently drift. actions/setup-python supports python-version-file.

♻️ Single source of truth
       - name: Set up Python
         uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
         with:
-          python-version: "3.12"
+          python-version-file: ".python-version"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 73 - 76, Update the "Set up Python" GH
Actions step that uses actions/setup-python (the step with name "Set up Python"
and input key python-version) to read the interpreter version from the
repository .python-version file instead of hardcoding "3.12": replace the
python-version input with python-version-file and point it to ".python-version"
so the workflow uses that single source of truth; keep the same action/commit
reference (actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405).
.github/workflows/codacy.yml (1)

86-86: 💤 Low value

Include the resolved version in the pin comment.

Every other action in these workflows uses a readable # vX.Y.Z annotation alongside the SHA. The bare # pinned comment here gives no hint of which codacy-analysis-cli-action release 562ee3e corresponds to, which hurts auditability and Dependabot review.

♻️ Suggested annotation
-        uses: codacy/codacy-analysis-cli-action@562ee3e92b8e92df8b67e0a5ff8aa8e261919c08 # pinned
+        uses: codacy/codacy-analysis-cli-action@562ee3e92b8e92df8b67e0a5ff8aa8e261919c08 # v4.4.5

(replace with the actual release that SHA maps to)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/codacy.yml at line 86, The pinned GitHub Action reference
uses a SHA-only comment on the uses line
("codacy/codacy-analysis-cli-action@562ee3e92b8e92df8b67e0a5ff8aa8e261919c08")
which lacks a readable release tag; update the trailing comment on that uses
entry to include the resolved release (e.g. "# vX.Y.Z") alongside or replacing
"# pinned" so the line reads like "uses:
codacy/codacy-analysis-cli-action@562ee3e... # vX.Y.Z" — look for the exact uses
entry for codacy/codacy-analysis-cli-action and add the human-readable version
string that the SHA corresponds to.
justfile (1)

28-36: ⚡ Quick win

grep … | head -1 in a bare assignment can abort the recipe under set -e/pipefail.

These _ensure-* helpers run installed_version="$(… | grep -oE … | head -1)" as a standalone assignment with set -euo pipefail. If --version output contains no semver (grep exits 1) or grep is killed by SIGPIPE when head closes early, the pipeline returns non-zero and set -e aborts the recipe immediately — so the intended ❌ … not found. Install with: … message never prints. This pattern repeats across all _ensure-* helpers and the inline checks in setup-tools.

♻️ Make the version capture non-fatal (apply to each occurrence)
-        installed_version="$(cargo llvm-cov --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1)"
+        installed_version="$(cargo llvm-cov --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || true)"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@justfile` around lines 28 - 36, The pipeline used to extract versions (e.g.,
installed_version="$(cargo llvm-cov --version 2>/dev/null | grep -oE
'[0-9]+\.[0-9]+\.[0-9]+' | head -1)") can fail under set -euo pipefail and abort
the recipe; make the capture non-fatal by allowing the pipeline to return zero
even if grep/head fail — for example, append "|| true" to the command
substitution or wrap only the pipeline in a subshell with "|| true" so
installed_version becomes empty instead of causing exit; apply this change to
every occurrence in the _ensure-* helpers and the inline checks in setup-tools
(references: installed_version assignment lines, _ensure-* helper functions, and
setup-tools check blocks).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pyproject.toml`:
- Around line 140-148: The dev dependencies list currently uses "pytest>=9.0.3"
which should be tightened to the released PyPI version to avoid uv sync
failures; update the dependency in the [dependency-groups] dev array from
"pytest>=9.0.3" to "pytest==9.0.3" and then sanity-check that the other pinned
dev tools ("ruff>=0.15.14", "semgrep==1.164.0", "actionlint-py==1.7.12.24",
"ty>=0.0.40") exist on PyPI (or pin them to exact released versions if you want
the same stability) before running uv sync --group dev.

---

Outside diff comments:
In `@justfile`:
- Around line 539-554: The required-commands check in setup-tools currently
lists "jq" but setup-tools never installs or guides how to install it; update
setup-tools to either invoke the existing _ensure-jq helper or add a clear
guidance/install step for jq so the "✗ jq" case can be resolved. Specifically,
modify the setup-tools recipe to call _ensure-jq (or echo the same installation
hint used by _ensure-jq) before the verification loop so jq is installed or a
direct instruction is printed when missing, ensuring the required-commands
loop's jq entry is actionable.

In `@scripts/tag_release.py`:
- Line 1: Replace the shebang in scripts/tag_release.py that currently invokes
python3 with the repository-standard uv run shebang used by the other scripts
(i.e., change the "#!/usr/bin/env python3" line to the equivalent that runs via
uv run), and ensure the file remains executable; locate the shebang at the top
of the file to make this one-line change so the script uses uv run instead of
calling python directly.

---

Duplicate comments:
In `@cliff.toml`:
- Line 98: The current generic parser message pattern message = "(?i)^bump "
catches human-authored subjects; tighten it by replacing that pattern with a
regex that requires either an explicit "deps"/"dependencies" keyword or a
package identifier after "bump" (for example using a word boundary and requiring
bump\s+(deps?|dependencies|[A-Za-z0-9_\-\/]+)), so update the message value used
for grouping "Dependencies" accordingly to avoid matching generic "Bump MSRV..."
subjects.

In `@scripts/tests/test_subprocess_utils.py`:
- Around line 134-138: The test test_git_convenience_helpers currently invokes
real git and is brittle; change it to mock the underlying run_git_command calls
used by get_git_commit_hash, get_git_remote_url, check_git_repo, and
check_git_history so they return deterministic values (e.g. a fake commit hash,
a fake remote URL, and success outputs) instead of running git. Use the same
mocking pattern used earlier in the file (patch/monkeypatch run_git_command) and
ensure each helper is asserted against the mocked return values rather than
relying on the real environment.

---

Nitpick comments:
In @.github/workflows/ci.yml:
- Around line 73-76: Update the "Set up Python" GH Actions step that uses
actions/setup-python (the step with name "Set up Python" and input key
python-version) to read the interpreter version from the repository
.python-version file instead of hardcoding "3.12": replace the python-version
input with python-version-file and point it to ".python-version" so the workflow
uses that single source of truth; keep the same action/commit reference
(actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405).

In @.github/workflows/codacy.yml:
- Line 86: The pinned GitHub Action reference uses a SHA-only comment on the
uses line
("codacy/codacy-analysis-cli-action@562ee3e92b8e92df8b67e0a5ff8aa8e261919c08")
which lacks a readable release tag; update the trailing comment on that uses
entry to include the resolved release (e.g. "# vX.Y.Z") alongside or replacing
"# pinned" so the line reads like "uses:
codacy/codacy-analysis-cli-action@562ee3e... # vX.Y.Z" — look for the exact uses
entry for codacy/codacy-analysis-cli-action and add the human-readable version
string that the SHA corresponds to.

In `@justfile`:
- Around line 28-36: The pipeline used to extract versions (e.g.,
installed_version="$(cargo llvm-cov --version 2>/dev/null | grep -oE
'[0-9]+\.[0-9]+\.[0-9]+' | head -1)") can fail under set -euo pipefail and abort
the recipe; make the capture non-fatal by allowing the pipeline to return zero
even if grep/head fail — for example, append "|| true" to the command
substitution or wrap only the pipeline in a subshell with "|| true" so
installed_version becomes empty instead of causing exit; apply this change to
every occurrence in the _ensure-* helpers and the inline checks in setup-tools
(references: installed_version assignment lines, _ensure-* helper functions, and
setup-tools check blocks).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9751823a-419b-40b6-bf03-6cc44d392212

📥 Commits

Reviewing files that changed from the base of the PR and between 9359256 and 19b10d5.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (42)
  • .github/dependabot.yml
  • .github/workflows/audit.yml
  • .github/workflows/benchmarks.yml
  • .github/workflows/ci.yml
  • .github/workflows/codacy.yml
  • .github/workflows/codecov.yml
  • .github/workflows/codeql.yml
  • .github/workflows/rust-clippy.yml
  • .github/workflows/semgrep-sarif.yml
  • .github/workflows/zizmor.yml
  • .gitignore
  • .python-version
  • AGENTS.md
  • CHANGELOG.md
  • Cargo.toml
  • README.md
  • REFERENCES.md
  • SECURITY.md
  • benches/vs_linalg.rs
  • cliff.toml
  • docs/archive/changelog/0.1.md
  • docs/archive/changelog/0.2.md
  • docs/archive/changelog/0.3.md
  • dprint.json
  • justfile
  • pyproject.toml
  • rust-toolchain.toml
  • scripts/archive_changelog.py
  • scripts/bench_compare.py
  • scripts/criterion_dim_plot.py
  • scripts/postprocess_changelog.py
  • scripts/subprocess_utils.py
  • scripts/tag_release.py
  • scripts/tests/test_archive_changelog.py
  • scripts/tests/test_criterion_dim_plot.py
  • scripts/tests/test_postprocess_changelog.py
  • scripts/tests/test_subprocess_utils.py
  • scripts/tests/test_tag_release.py
  • semgrep.yaml
  • src/exact.rs
  • tests/proptest_exact.rs
  • ty.toml
💤 Files with no reviewable changes (2)
  • scripts/tests/test_criterion_dim_plot.py
  • .gitignore

Comment thread pyproject.toml
Comment on lines 140 to 148
[dependency-groups]
dev = [
"actionlint-py==1.7.12.24",
"mypy>=1.19.0",
"pytest>=8.0.0",
"ruff>=0.12.11",
"ty>=0.0.8",
"pytest>=9.0.3",
"ruff>=0.15.14",
"semgrep==1.164.0",
"ty>=0.0.40",
]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

What is the latest released version of pytest on PyPI?

💡 Result:

The latest released version of pytest on PyPI as of June 3, 2026, is 9.0.3, which was released on April 7, 2026 [1][2][3].

Citations:


Update the concern: pytest>=9.0.3 is a released PyPI version.

pytest==9.0.3 is the latest released version on PyPI (released Apr 7, 2026), so the “9.0.x not published → uv sync --group dev fails” risk for pytest specifically is unlikely. Still worth sanity-checking the other pinned dev tools (ruff, semgrep, actionlint-py, ty) for availability to fully guarantee uv sync --group dev resolves.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pyproject.toml` around lines 140 - 148, The dev dependencies list currently
uses "pytest>=9.0.3" which should be tightened to the released PyPI version to
avoid uv sync failures; update the dependency in the [dependency-groups] dev
array from "pytest>=9.0.3" to "pytest==9.0.3" and then sanity-check that the
other pinned dev tools ("ruff>=0.15.14", "semgrep==1.164.0",
"actionlint-py==1.7.12.24", "ty>=0.0.40") exist on PyPI (or pin them to exact
released versions if you want the same stability) before running uv sync --group
dev.

@acgetchell acgetchell merged commit 06efa6d into main Jun 3, 2026
18 of 19 checks passed
@acgetchell acgetchell deleted the ci/117-shared-security-release-tooling branch June 3, 2026 01:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Align CI and security tooling with shared Rust workflow

2 participants