194 lines
7.2 KiB
YAML
194 lines
7.2 KiB
YAML
# reverse-skill CI:路由回归 + 结构一致性 + 供应链 pin gate + 冒烟
|
||
# 矩阵:windows-latest(原生 powershell 5.1) + ubuntu-latest(pwsh + powershell shim)
|
||
# 触发:所有分支(含 fork 的改进分支),PR 也触发
|
||
name: CI
|
||
|
||
on:
|
||
push:
|
||
pull_request:
|
||
|
||
jobs:
|
||
routing-tests:
|
||
name: routing tests (${{ matrix.os }})
|
||
strategy:
|
||
fail-fast: false
|
||
matrix:
|
||
os: [windows-latest, ubuntu-latest]
|
||
runs-on: ${{ matrix.os }}
|
||
steps:
|
||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||
|
||
# 脚本内部以 `powershell` 调用子进程;Linux runner 只有 pwsh,做个 shim
|
||
- name: powershell shim (linux)
|
||
if: runner.os == 'Linux'
|
||
shell: bash
|
||
run: sudo ln -sf "$(command -v pwsh)" /usr/local/bin/powershell
|
||
|
||
- name: Routing regression (163 cases)
|
||
shell: pwsh
|
||
run: ./skills/scripts/test-routing.ps1
|
||
|
||
- name: Routing coherence + supply-chain pin gate
|
||
shell: pwsh
|
||
run: ./skills/scripts/verify-routing-coherence.ps1
|
||
|
||
- name: Smoke (verify + parse + quick route)
|
||
shell: pwsh
|
||
run: ./skills/scripts/smoke.ps1
|
||
|
||
- name: INDEX.md up-to-date check
|
||
shell: pwsh
|
||
run: ./skills/scripts/extract-summaries.ps1 -Check
|
||
|
||
- name: All JSON manifests valid
|
||
shell: pwsh
|
||
run: |
|
||
Get-Content skills/scripts/bootstrap-manifest.json -Raw -Encoding UTF8 | ConvertFrom-Json | Out-Null
|
||
Get-Content kali/scripts/bootstrap-manifest.json -Raw -Encoding UTF8 | ConvertFrom-Json | Out-Null
|
||
Get-Content skills/config/routing.json -Raw -Encoding UTF8 | ConvertFrom-Json | Out-Null
|
||
Get-Content skills/tests/routing-benchmark.json -Raw -Encoding UTF8 | ConvertFrom-Json | Out-Null
|
||
Write-Host "All JSON valid"
|
||
|
||
- name: Doc fact tables vs manifest (verify-doc-facts)
|
||
shell: pwsh
|
||
run: ./skills/scripts/verify-doc-facts.ps1
|
||
|
||
sh-syntax:
|
||
name: shell script syntax check
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||
- name: bash -n all .sh
|
||
shell: bash
|
||
run: |
|
||
set -e
|
||
while IFS= read -r f; do
|
||
bash -n "$f"
|
||
echo "syntax OK: $f"
|
||
done < <(git ls-files '*.sh')
|
||
|
||
- name: Structured routing parity (Bash)
|
||
shell: bash
|
||
run: |
|
||
set -euo pipefail
|
||
bash skills/scripts/test-routing.sh
|
||
|
||
scratch="$(mktemp -d)"
|
||
trap 'rm -rf "$scratch"' EXIT
|
||
|
||
if bash skills/scripts/case-init.sh \
|
||
--hint "offline apk" \
|
||
--case-name "../case-escape" \
|
||
--package-root "$scratch/project" \
|
||
--preset offline-sample \
|
||
--sample "$scratch/sample.apk"; then
|
||
echo "case-init accepted an unsafe case name" >&2
|
||
exit 1
|
||
fi
|
||
|
||
if bash skills/scripts/case-init.sh \
|
||
--hint "authorized web review" \
|
||
--case-name "invalid-network" \
|
||
--package-root "$scratch/project" \
|
||
--auth-granted \
|
||
--network-profile "internet" \
|
||
--target-url "https://example.test/"; then
|
||
echo "case-init accepted an unsupported network profile" >&2
|
||
exit 1
|
||
fi
|
||
|
||
bash skills/scripts/case-init.sh \
|
||
--hint "authorized web review" \
|
||
--case-name "network-default" \
|
||
--package-root "$scratch/project" \
|
||
--auth-granted \
|
||
--target-url "https://example.test/"
|
||
grep -Eq '^- mode: authorized_target_only$' "$scratch/project/work/network-default/scope.md"
|
||
grep -Eq '^- ready_for_act: true$' "$scratch/project/work/network-default/scope.md"
|
||
bash skills/scripts/case-guard.sh --case-root "$scratch/project/work/network-default"
|
||
|
||
bash skills/scripts/case-init.sh \
|
||
--hint "authorized web review" \
|
||
--case-name "uppercase-network" \
|
||
--package-root "$scratch/project" \
|
||
--auth-granted \
|
||
--network-profile "AUTHORIZED_TARGET_ONLY" \
|
||
--target-url "https://example.test/"
|
||
grep -Eq '^- mode: authorized_target_only$' "$scratch/project/work/uppercase-network/scope.md"
|
||
|
||
bash skills/scripts/case-init.sh \
|
||
--hint "pending review" \
|
||
--case-name "guard-section" \
|
||
--package-root "$scratch/project" \
|
||
--target-url "https://example.test/"
|
||
cat >> "$scratch/project/work/guard-section/scope.md" <<'FAKE_FIELDS'
|
||
|
||
## notes
|
||
- status: granted
|
||
- mode: authorized_target_only
|
||
- ready_for_act: true
|
||
FAKE_FIELDS
|
||
if bash skills/scripts/case-guard.sh --case-root "$scratch/project/work/guard-section"; then
|
||
echo "case-guard accepted fields outside their contract sections" >&2
|
||
exit 1
|
||
fi
|
||
|
||
- name: PowerShell syntax check (PSParser, all tracked .ps1)
|
||
shell: pwsh
|
||
run: |
|
||
$files = git ls-files '*.ps1'
|
||
if (-not $files) { Write-Host 'No .ps1 files tracked'; exit 0 }
|
||
$failed = 0
|
||
foreach ($f in $files) {
|
||
$tokens = $null
|
||
$errors = $null
|
||
[System.Management.Automation.Language.Parser]::ParseFile((Resolve-Path $f), [ref]$tokens, [ref]$errors) | Out-Null
|
||
if ($errors -and $errors.Count -gt 0) {
|
||
$failed++
|
||
Write-Host "::error file=$f::$($errors.Count) parse error(s)"
|
||
foreach ($e in $errors) {
|
||
Write-Host (" L{0}:{1} {2}" -f $e.Extent.StartLineNumber, $e.Extent.StartColumnNumber, $e.Message)
|
||
}
|
||
} else {
|
||
Write-Host "syntax OK: $f"
|
||
}
|
||
}
|
||
if ($failed -gt 0) { exit 1 }
|
||
|
||
leak-scan:
|
||
name: field-journal leak scan
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||
- name: Scan field-journal for un-anonymized secrets
|
||
shell: pwsh
|
||
run: ./skills/scripts/scan-leaks.ps1 -Path skills/field-journal
|
||
|
||
case-contract:
|
||
name: case contract test (ctf-demo)
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||
- name: Review examples/ctf-demo under strict contract
|
||
shell: bash
|
||
run: python3 skills/case-review/scripts/review_case.py examples/ctf-demo --verify-hashes --strict
|
||
|
||
version-check:
|
||
name: version consistency
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||
- name: VERSION matches latest CHANGELOG release
|
||
shell: pwsh
|
||
run: |
|
||
$v = (Get-Content VERSION -Raw).Trim()
|
||
$cl = Get-Content CHANGELOG.md -Raw
|
||
$m = [regex]::Match($cl, '(?m)^## \[(\d+\.\d+\.\d+)\]')
|
||
if (-not $m.Success) { Write-Error 'No version header found in CHANGELOG'; exit 1 }
|
||
$latest = $m.Groups[1].Value
|
||
if ($v -ne $latest) {
|
||
Write-Error "VERSION ($v) does not match latest CHANGELOG release ($latest)"
|
||
exit 1
|
||
}
|
||
Write-Host "Version OK: $v"
|