Skip to content

Commit fe43e33

Browse files
committed
fix: unblock CI checks and harden local hooks
Normalize analyzer tool paths across platforms so Windows test expectations stay stable, and rewrite workflow secret gating so GitHub can parse eval and release workflows. Add repo-managed hook scripts and docs so the same workflow and Rust checks run locally before commit and push. Made-with: Cursor
1 parent fcc452c commit fe43e33

File tree

12 files changed

+235
-86
lines changed

12 files changed

+235
-86
lines changed

.githooks/pre-commit

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,23 @@
11
#!/usr/bin/env bash
2-
set -e
2+
set -euo pipefail
3+
4+
repo_root="$(git rev-parse --show-toplevel)"
5+
cd "$repo_root"
6+
7+
echo "==> Validating GitHub workflows..."
8+
bash "./scripts/check-workflows.sh"
39

410
echo "==> Running cargo fmt --check..."
5-
cargo fmt --check || {
6-
echo "ERROR: cargo fmt found formatting issues. Run 'cargo fmt' to fix."
7-
exit 1
8-
}
11+
cargo fmt --check
912

10-
echo "==> Running cargo clippy..."
11-
cargo clippy --all-targets -- -D warnings || {
12-
echo "ERROR: cargo clippy found warnings. Fix them before committing."
13-
exit 1
14-
}
13+
echo "==> Running cargo clippy --all-targets..."
14+
cargo clippy --all-targets -- -D warnings
1515

16-
# Tests are slower — run by default but skip with SKIP_TESTS=1
1716
if [ "${SKIP_TESTS:-0}" = "1" ]; then
18-
echo "==> Skipping tests (SKIP_TESTS=1). CI will catch failures."
17+
echo "==> Skipping cargo test (SKIP_TESTS=1). Pre-push still enforces the full Rust gate."
1918
else
2019
echo "==> Running cargo test..."
21-
cargo test || {
22-
echo "ERROR: Tests failed. Fix them before committing."
23-
echo "Tip: SKIP_TESTS=1 git commit ... to skip tests locally (CI will still run them)."
24-
exit 1
25-
}
20+
cargo test
2621
fi
2722

28-
echo "==> All pre-commit checks passed."
23+
echo "==> Pre-commit checks passed."

.githooks/pre-push

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
repo_root="$(git rev-parse --show-toplevel)"
5+
cd "$repo_root"
6+
7+
echo "==> Validating GitHub workflows..."
8+
bash "./scripts/check-workflows.sh"
9+
10+
echo "==> Running cargo fmt --check..."
11+
cargo fmt --check
12+
13+
echo "==> Running cargo clippy --all-targets..."
14+
cargo clippy --all-targets -- -D warnings
15+
16+
echo "==> Running cargo test..."
17+
cargo test
18+
19+
echo "==> Pre-push checks passed."

.github/workflows/ci.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ jobs:
1414
runs-on: ubuntu-latest
1515
steps:
1616
- uses: actions/checkout@v4
17+
- name: Lint GitHub Actions
18+
uses: rhysd/actionlint@v1
1719
- uses: actions/setup-node@v4
1820
with:
1921
node-version: '20'
@@ -26,7 +28,7 @@ jobs:
2628
- name: Format
2729
run: cargo fmt -- --check
2830
- name: Clippy
29-
run: cargo clippy -- -D warnings
31+
run: cargo clippy --all-targets -- -D warnings
3032

3133
security:
3234
runs-on: ubuntu-latest

.github/workflows/eval.yml

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,36 @@ env:
1818

1919
jobs:
2020
eval:
21-
if: ${{ secrets.OPENAI_API_KEY != '' }}
2221
runs-on: ubuntu-latest
2322
timeout-minutes: 60
2423
steps:
24+
- name: Check eval secret
25+
id: secret-check
26+
env:
27+
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
28+
run: |
29+
if [ -n "${OPENAI_API_KEY}" ]; then
30+
echo "configured=true" >> "$GITHUB_OUTPUT"
31+
else
32+
echo "configured=false" >> "$GITHUB_OUTPUT"
33+
fi
34+
2535
- uses: actions/checkout@v4
36+
if: ${{ steps.secret-check.outputs.configured == 'true' }}
2637
with:
2738
fetch-depth: 0
2839

2940
- uses: dtolnay/rust-toolchain@stable
41+
if: ${{ steps.secret-check.outputs.configured == 'true' }}
3042
- uses: Swatinem/rust-cache@v2
43+
if: ${{ steps.secret-check.outputs.configured == 'true' }}
3144

3245
- name: Build current branch binary
46+
if: ${{ steps.secret-check.outputs.configured == 'true' }}
3347
run: cargo build --release
3448

3549
- name: Build baseline report from origin/main
50+
if: ${{ steps.secret-check.outputs.configured == 'true' }}
3651
env:
3752
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
3853
run: |
@@ -47,6 +62,7 @@ jobs:
4762
--output /tmp/eval-baseline.json
4863
4964
- name: Run eval thresholds on current branch
65+
if: ${{ steps.secret-check.outputs.configured == 'true' }}
5066
env:
5167
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
5268
run: |
@@ -64,17 +80,14 @@ jobs:
6480
--max-rule-f1-drop reliability.unwrap_panic=0.25
6581
6682
- name: Upload eval reports
67-
if: always()
83+
if: ${{ always() && steps.secret-check.outputs.configured == 'true' }}
6884
uses: actions/upload-artifact@v4
6985
with:
7086
name: eval-reports
7187
path: |
7288
eval-current.json
7389
/tmp/eval-baseline.json
7490
75-
eval-skipped:
76-
if: ${{ secrets.OPENAI_API_KEY == '' }}
77-
runs-on: ubuntu-latest
78-
steps:
7991
- name: Skip message
92+
if: ${{ steps.secret-check.outputs.configured != 'true' }}
8093
run: echo "Skipping eval workflow because OPENAI_API_KEY secret is not configured."

.github/workflows/release.yml

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ jobs:
2525

2626
- name: Extract version
2727
id: get_version
28-
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
28+
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
2929

3030
- name: Create Release
3131
id: create_release
32-
uses: softprops/action-gh-release@v1
32+
uses: softprops/action-gh-release@v2
3333
with:
3434
tag_name: ${{ github.ref }}
3535
name: Release ${{ steps.get_version.outputs.VERSION }}
@@ -129,8 +129,8 @@ jobs:
129129
- name: Set up macOS cross-compilation
130130
if: matrix.os == 'macos-latest' && matrix.target == 'aarch64-apple-darwin'
131131
run: |
132-
echo "SDKROOT=$(xcrun -sdk macosx --show-sdk-path)" >> $GITHUB_ENV
133-
echo "MACOSX_DEPLOYMENT_TARGET=$(xcrun -sdk macosx --show-sdk-platform-version)" >> $GITHUB_ENV
132+
echo "SDKROOT=$(xcrun -sdk macosx --show-sdk-path)" >> "$GITHUB_ENV"
133+
echo "MACOSX_DEPLOYMENT_TARGET=$(xcrun -sdk macosx --show-sdk-platform-version)" >> "$GITHUB_ENV"
134134
135135
- name: Build
136136
run: cargo build --release --target ${{ matrix.target }}
@@ -158,9 +158,9 @@ jobs:
158158
cd target/${{ matrix.target }}/release/
159159
if [ -f "${{ matrix.artifact_name }}" ]; then
160160
if [[ "$RUNNER_OS" == "macOS" ]]; then
161-
shasum -a 256 ${{ matrix.artifact_name }} > ${{ matrix.asset_name }}.sha256
161+
shasum -a 256 "${{ matrix.artifact_name }}" > "${{ matrix.asset_name }}.sha256"
162162
else
163-
sha256sum ${{ matrix.artifact_name }} > ${{ matrix.asset_name }}.sha256
163+
sha256sum "${{ matrix.artifact_name }}" > "${{ matrix.asset_name }}.sha256"
164164
fi
165165
else
166166
echo "Binary not found, build may have failed"
@@ -169,6 +169,7 @@ jobs:
169169
170170
- name: Create checksum (Windows)
171171
if: matrix.os == 'windows-latest'
172+
shell: pwsh
172173
run: |
173174
cd target/${{ matrix.target }}/release/
174175
(Get-FileHash -Algorithm SHA256 ${{ matrix.artifact_name }}).Hash + " " + "${{ matrix.artifact_name }}" | Out-File -Encoding ASCII ${{ matrix.asset_name }}.sha256
@@ -195,6 +196,7 @@ jobs:
195196
if: matrix.os == 'windows-latest'
196197
env:
197198
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
199+
shell: pwsh
198200
run: |
199201
cd target/${{ matrix.target }}/release/
200202
# Upload binary
@@ -227,7 +229,7 @@ jobs:
227229

228230
- name: Extract version from tag
229231
id: get_version
230-
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
232+
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
231233

232234
- name: Build and push Docker image
233235
id: build-and-push

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,6 +1002,16 @@ The summary includes:
10021002

10031003
Contributions are welcome! Please open an issue first to discuss what you would like to change.
10041004

1005+
### Local Development Checks
1006+
1007+
Enable the repository-managed git hooks after cloning:
1008+
1009+
```bash
1010+
bash scripts/install-hooks.sh
1011+
```
1012+
1013+
The hooks validate GitHub Actions workflows and run `cargo fmt --check`, `cargo clippy --all-targets -- -D warnings`, and `cargo test` before code leaves your machine. Install `actionlint` if you want full local workflow linting; otherwise the fallback check still blocks invalid `secrets.*` usage inside workflow `if:` expressions.
1014+
10051015
## Supported Platforms
10061016

10071017
DiffScope provides pre-built binaries for the following platforms:

scripts/check-workflows.sh

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
repo_root="$(git rev-parse --show-toplevel)"
5+
cd "$repo_root"
6+
7+
if [ ! -d ".github/workflows" ]; then
8+
exit 0
9+
fi
10+
11+
if command -v actionlint >/dev/null 2>&1; then
12+
actionlint
13+
exit 0
14+
fi
15+
16+
python3 - <<'PY'
17+
from pathlib import Path
18+
import re
19+
import sys
20+
21+
workflow_dir = Path(".github/workflows")
22+
paths = sorted(workflow_dir.glob("*.yml")) + sorted(workflow_dir.glob("*.yaml"))
23+
secret_if_pattern = re.compile(r"^\s*if:\s*\$\{\{\s*secrets\.", re.MULTILINE)
24+
25+
failures = []
26+
for path in paths:
27+
contents = path.read_text(encoding="utf-8")
28+
if secret_if_pattern.search(contents):
29+
failures.append(str(path))
30+
31+
if failures:
32+
print("ERROR: GitHub Actions does not allow the `secrets` context directly in `if:` expressions.")
33+
print("Use a prior step to expose a boolean output instead.")
34+
print("")
35+
print("Affected workflow files:")
36+
for path in failures:
37+
print(f" - {path}")
38+
print("")
39+
print("Install `actionlint` for full local workflow lint coverage.")
40+
sys.exit(1)
41+
PY

scripts/install-hooks.sh

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
repo_root="$(git rev-parse --show-toplevel)"
5+
cd "$repo_root"
6+
7+
git config core.hooksPath .githooks
8+
chmod +x .githooks/pre-commit .githooks/pre-push
9+
10+
echo "Installed repository git hooks from .githooks."
11+
echo "Pre-commit and pre-push will now run local workflow and Rust checks."

src/plugins/builtin/eslint.rs

Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ use serde_json::Value;
77
use std::collections::HashMap;
88
use std::path::{Path, PathBuf};
99

10+
use super::path_utils::normalize_tool_path;
11+
1012
pub struct EslintAnalyzer;
1113

1214
impl EslintAnalyzer {
@@ -181,33 +183,6 @@ fn build_context_chunk(tool_name: &str, findings: &[AnalyzerFinding]) -> String
181183
format!("{tool_name} findings:\n{details}")
182184
}
183185

184-
fn normalize_tool_path(repo_root: &Path, raw: &str) -> PathBuf {
185-
let path = PathBuf::from(raw);
186-
let relative = if path.is_absolute() {
187-
path.strip_prefix(repo_root)
188-
.map(|value| value.to_path_buf())
189-
.unwrap_or(path)
190-
} else {
191-
path
192-
};
193-
194-
normalize_relative_path(relative)
195-
}
196-
197-
fn normalize_relative_path(path: PathBuf) -> PathBuf {
198-
let mut normalized = PathBuf::new();
199-
for component in path.components() {
200-
match component {
201-
std::path::Component::CurDir => {}
202-
std::path::Component::ParentDir => {
203-
normalized.pop();
204-
}
205-
other => normalized.push(other.as_os_str()),
206-
}
207-
}
208-
normalized
209-
}
210-
211186
#[cfg(test)]
212187
mod tests {
213188
use super::*;

src/plugins/builtin/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
mod duplicate_filter;
22
mod eslint;
3+
mod path_utils;
34
mod secret_scanner;
45
mod semgrep;
56
mod supply_chain;

0 commit comments

Comments
 (0)