Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
03b1084
phase1: normalize ci workflow orchestration
Feb 9, 2026
3d4f58d
phase2: confirm canonical version-policy PR check
Feb 9, 2026
27a63c7
phase3: harden qodana gating and scoped excludes
Feb 9, 2026
0d88974
phase4: enforce artifact contract for all required ci checks
Feb 9, 2026
b7f2de5
phase5: add sscc error ux and structured diagnostics
Feb 9, 2026
d6e9ff0
phase6: keep current duplication boundaries for safety
Feb 9, 2026
31e4540
phase4: include pack artifact in summary contract aggregation
Feb 9, 2026
f270da2
ci: make diag.json valid JSON and document CI DAG/contracts
Feb 9, 2026
9a1e618
Update tools/ci/lib/result.sh
tomtastisch Feb 9, 2026
9ab6e35
fix(ci): apply computed PR labels and verify outcome
Feb 9, 2026
1eca61b
phase1(ci): normalize orchestration shape in ci.yml without context c…
Feb 9, 2026
0c33ba3
phase2(version-policy): no-change deconfliction (single canonical PR …
Feb 9, 2026
1e13d55
phase3(qodana): no-change on branch (contract already green, threshol…
Feb 9, 2026
379ba8b
phase4(result): enforce fail-closed result schema validation for all …
Feb 9, 2026
3ce53ef
phase5(error-ux): resolve artifact deep-link via GitHub API with 9901…
Feb 9, 2026
f477731
phase6(ci): eliminate flaky GH API duplication with deterministic ret…
Feb 9, 2026
3e41bac
phase7(verify): finalize evidence pack and DoD verification
Feb 9, 2026
62a1e54
phase6(ci): switch pr-labeling to single REST PUT label sync for dete…
Feb 9, 2026
254acdb
phase1(ci): remove redundant job name and permissions
github-actions[bot] Feb 10, 2026
1be81ca
chore: update FileTypeOptions
github-actions[bot] Feb 10, 2026
890475b
ci: fix version-policy artifact + fail-closed finalize
github-actions[bot] Feb 10, 2026
2f8a562
style: format FileTypeOptions
github-actions[bot] Feb 10, 2026
e85bac8
ci: fix result schema path (no more tools/tools)
github-actions[bot] Feb 10, 2026
3e53c51
ci: finalize before exit for fail-closed (no set +e)
github-actions[bot] Feb 10, 2026
6e3d709
ci: require+verify ci-versioning-svt artifact in version-policy
github-actions[bot] Feb 10, 2026
9bff1a7
ci: use curl+python for version-policy artifact verify (no gh)
github-actions[bot] Feb 10, 2026
ad8287c
ci: fetch tags for versioning-svt (checkout hardening)
github-actions[bot] Feb 10, 2026
f615f04
ci: shrink version-policy verify run block (preflight CI-SHELL-004)
github-actions[bot] Feb 10, 2026
4e75369
chore(ci): replace gh api with curl+python
github-actions[bot] Feb 10, 2026
1fcba4f
chore(ci): address review threads
github-actions[bot] Feb 10, 2026
5fc241c
ci(qodana): grant checks/pr write permissions for annotations
github-actions[bot] Feb 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 56 additions & 13 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,13 @@ concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

defaults:
run:
shell: bash

jobs:
pr-labeling:
name: pr-labeling
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
permissions:
Expand All @@ -40,7 +45,9 @@ jobs:
- name: Run Entry Check
env:
GH_TOKEN: ${{ github.token }}
run: bash tools/ci/bin/run.sh pr-labeling
run: |
set -euo pipefail
bash tools/ci/bin/run.sh pr-labeling
- name: Upload Artifact
if: always()
uses: actions/upload-artifact@v4
Expand All @@ -50,6 +57,7 @@ jobs:
if-no-files-found: error

preflight:
name: preflight
runs-on: ubuntu-latest
steps:
- name: Checkout
Expand All @@ -67,7 +75,9 @@ jobs:
with:
node-version: "20"
- name: Run Entry Check
run: bash tools/ci/bin/run.sh preflight
run: |
set -euo pipefail
bash tools/ci/bin/run.sh preflight
- name: Upload Artifact
if: always()
uses: actions/upload-artifact@v4
Expand All @@ -77,6 +87,7 @@ jobs:
if-no-files-found: error

docs-links-full:
name: docs-links-full
runs-on: ubuntu-latest
needs: preflight
steps:
Expand All @@ -89,7 +100,9 @@ jobs:
8.0.x
10.0.102
- name: Run Entry Check
run: bash tools/ci/bin/run.sh docs-links-full
run: |
set -euo pipefail
bash tools/ci/bin/run.sh docs-links-full
- name: Upload Artifact
if: always()
uses: actions/upload-artifact@v4
Expand All @@ -99,6 +112,7 @@ jobs:
if-no-files-found: error

versioning-svt:
name: versioning-svt
runs-on: ubuntu-latest
needs: preflight
steps:
Expand All @@ -111,7 +125,9 @@ jobs:
8.0.x
10.0.102
- name: Run Entry Check
run: bash tools/ci/bin/run.sh versioning-svt
run: |
set -euo pipefail
bash tools/ci/bin/run.sh versioning-svt
- name: Upload Artifact
if: always()
uses: actions/upload-artifact@v4
Expand All @@ -121,6 +137,7 @@ jobs:
if-no-files-found: error

naming-snt:
name: naming-snt
runs-on: ubuntu-latest
needs: preflight
steps:
Expand All @@ -133,7 +150,9 @@ jobs:
8.0.x
10.0.102
- name: Run Entry Check
run: bash tools/ci/bin/run.sh naming-snt
run: |
set -euo pipefail
bash tools/ci/bin/run.sh naming-snt
- name: Upload Artifact
if: always()
uses: actions/upload-artifact@v4
Expand All @@ -143,6 +162,7 @@ jobs:
if-no-files-found: error

build:
name: build
runs-on: ubuntu-latest
needs: [preflight, versioning-svt, naming-snt]
steps:
Expand All @@ -155,7 +175,9 @@ jobs:
8.0.x
10.0.102
- name: Run Entry Check
run: bash tools/ci/bin/run.sh build
run: |
set -euo pipefail
bash tools/ci/bin/run.sh build
- name: Upload Artifact
if: always()
uses: actions/upload-artifact@v4
Expand All @@ -165,6 +187,7 @@ jobs:
if-no-files-found: error

api-contract:
name: api-contract
runs-on: ubuntu-latest
needs: build
steps:
Expand All @@ -177,7 +200,9 @@ jobs:
8.0.x
10.0.102
- name: Run Entry Check
run: bash tools/ci/bin/run.sh api-contract
run: |
set -euo pipefail
bash tools/ci/bin/run.sh api-contract
- name: Upload Artifact
if: always()
uses: actions/upload-artifact@v4
Expand All @@ -187,6 +212,7 @@ jobs:
if-no-files-found: error

pack:
name: pack
runs-on: ubuntu-latest
needs: [build, versioning-svt]
steps:
Expand All @@ -199,7 +225,9 @@ jobs:
8.0.x
10.0.102
- name: Run Entry Check
run: bash tools/ci/bin/run.sh pack
run: |
set -euo pipefail
bash tools/ci/bin/run.sh pack
- name: Upload Artifact
if: always()
uses: actions/upload-artifact@v4
Expand All @@ -209,6 +237,7 @@ jobs:
if-no-files-found: error

consumer-smoke:
name: consumer-smoke
runs-on: ubuntu-latest
needs: pack
steps:
Expand All @@ -226,7 +255,9 @@ jobs:
name: ci-pack
path: artifacts/ci/pack
- name: Run Entry Check
run: bash tools/ci/bin/run.sh consumer-smoke
run: |
set -euo pipefail
bash tools/ci/bin/run.sh consumer-smoke
- name: Upload Artifact
if: always()
uses: actions/upload-artifact@v4
Expand All @@ -236,6 +267,7 @@ jobs:
if-no-files-found: error

package-backed-tests:
name: package-backed-tests
runs-on: ubuntu-latest
needs: pack
steps:
Expand All @@ -253,7 +285,9 @@ jobs:
name: ci-pack
path: artifacts/ci/pack
- name: Run Entry Check
run: bash tools/ci/bin/run.sh package-backed-tests
run: |
set -euo pipefail
bash tools/ci/bin/run.sh package-backed-tests
- name: Upload Artifact
if: always()
uses: actions/upload-artifact@v4
Expand All @@ -263,6 +297,7 @@ jobs:
if-no-files-found: error

security-nuget:
name: security-nuget
runs-on: ubuntu-latest
needs: build
steps:
Expand All @@ -275,7 +310,9 @@ jobs:
8.0.x
10.0.102
- name: Run Entry Check
run: bash tools/ci/bin/run.sh security-nuget
run: |
set -euo pipefail
bash tools/ci/bin/run.sh security-nuget
- name: Upload Artifact
if: always()
uses: actions/upload-artifact@v4
Expand All @@ -285,6 +322,7 @@ jobs:
if-no-files-found: error

tests-bdd-coverage:
name: tests-bdd-coverage
runs-on: ubuntu-latest
needs: build
steps:
Expand All @@ -297,7 +335,9 @@ jobs:
8.0.x
10.0.102
- name: Run Entry Check
run: bash tools/ci/bin/run.sh tests-bdd-coverage
run: |
set -euo pipefail
bash tools/ci/bin/run.sh tests-bdd-coverage
- name: Upload Artifact
if: always()
uses: actions/upload-artifact@v4
Expand All @@ -307,6 +347,7 @@ jobs:
if-no-files-found: error

summary:
name: summary
runs-on: ubuntu-latest
needs: [docs-links-full, naming-snt, versioning-svt, api-contract, consumer-smoke, package-backed-tests, security-nuget, tests-bdd-coverage]
steps:
Expand Down Expand Up @@ -369,7 +410,9 @@ jobs:
name: ci-tests-bdd-coverage
path: artifacts/ci/tests-bdd-coverage
- name: Run Entry Check
run: bash tools/ci/bin/run.sh summary
run: |
set -euo pipefail
bash tools/ci/bin/run.sh summary
- name: Upload Artifact
if: always()
uses: actions/upload-artifact@v4
Expand Down
6 changes: 1 addition & 5 deletions .github/workflows/qodana.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,19 +44,15 @@ jobs:
if [ -f .editorconfig.qodana.bak ]; then
mv .editorconfig.qodana.bak .editorconfig
fi
- name: Skip Qodana (missing token)
if: env.QODANA_TOKEN == ''
run: echo "QODANA_TOKEN is not set; skipping Qodana scan."
- name: Run Entry Check
if: env.QODANA_TOKEN != ''
run: bash tools/ci/bin/run.sh qodana
Comment thread
tomtastisch marked this conversation as resolved.
- name: Upload SARIF To Code Scanning
if: github.event_name != 'pull_request' && env.QODANA_TOKEN != ''
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: artifacts/ci/qodana/qodana.sarif.json
- name: Upload Artifact
if: always() && env.QODANA_TOKEN != ''
if: always()
uses: actions/upload-artifact@v4
Comment thread
tomtastisch marked this conversation as resolved.
with:
name: ci-qodana
Expand Down
14 changes: 13 additions & 1 deletion qodana.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,16 @@ version: "1.0"
linter: jetbrains/qodana-dotnet:2025.3

profile:
path: .qodana/profiles/fileclassifier.yaml
path: .qodana/profiles/fileclassifier.yaml

# Scope-limited excludes for known non-security false positives on the VB codebase.
exclude:
- name: CheckNamespace
paths:
- src/FileTypeDetection/**
- name: VBWarnings__BC40056
paths:
- src/FileTypeDetection/**
- name: UnusedImportClause
paths:
- src/FileTypeDetection/**
22 changes: 9 additions & 13 deletions tools/ci/bin/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,6 @@ fi

OUT_DIR="artifacts/ci/${CHECK_ID}"

if [[ "$CHECK_ID" == "artifact_contract" || "$CHECK_ID" == "summary" ]]; then
cd "$ROOT_DIR"
dotnet restore --locked-mode "${ROOT_DIR}/tools/ci/checks/ResultSchemaValidator/ResultSchemaValidator.csproj"
dotnet build -c Release "${ROOT_DIR}/tools/ci/checks/ResultSchemaValidator/ResultSchemaValidator.csproj"
dotnet restore --locked-mode "${ROOT_DIR}/tools/ci/checks/PolicyRunner/PolicyRunner.csproj"
dotnet build -c Release "${ROOT_DIR}/tools/ci/checks/PolicyRunner/PolicyRunner.csproj"
exec dotnet "${ROOT_DIR}/tools/ci/checks/PolicyRunner/bin/Release/net10.0/PolicyRunner.dll" --check-id "${CHECK_ID}" --repo-root "${ROOT_DIR}" --out-dir "${OUT_DIR}"
fi

ci_result_init "$CHECK_ID" "$OUT_DIR"

finalized=0
Expand Down Expand Up @@ -406,6 +397,14 @@ run_qodana_contract() {
ci_result_append_summary "Qodana contract validation completed."
}

run_policy_contract() {
build_validators
if ! run_policy_runner_bridge "$CHECK_ID" "$OUT_DIR" "Policy contract check (${CHECK_ID})" "tools/ci/policies/rules"; then
return 1
fi
ci_result_append_summary "Policy contract check '${CHECK_ID}' completed."
}

main() {
cd "$ROOT_DIR"
case "$CHECK_ID" in
Expand All @@ -420,10 +419,7 @@ main() {
build) run_build ;;
security-nuget) run_security_nuget ;;
tests-bdd-coverage) run_tests_bdd_coverage ;;
summary)
ci_result_add_violation "CI-RUNNER-001" "fail" "summary must be executed via PolicyRunner bridge" "tools/ci/bin/run.sh"
return 2
;;
summary|artifact_contract) run_policy_contract ;;
pr-labeling) run_pr_labeling ;;
qodana) run_qodana_contract ;;
*)
Expand Down
6 changes: 6 additions & 0 deletions tools/ci/errors/classes.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[classes]
missing_input = "01"
command_failed = "02"
policy_violation = "03"
schema_failure = "04"
blocking_findings = "05"
16 changes: 16 additions & 0 deletions tools/ci/errors/errors.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[errors]
E0102 = "CI setup command failed for check '{check_id}' ({rule_id})."
E1002 = "Policy execution command failed for check '{check_id}' ({rule_id})."
E1003 = "Policy violation detected for check '{check_id}' ({rule_id})."
E2002 = "Build command failed for check '{check_id}' ({rule_id})."
E3002 = "Test execution failed for check '{check_id}' ({rule_id})."
E4002 = "Packaging command failed for check '{check_id}' ({rule_id})."
E5002 = "Security scan command failed for check '{check_id}' ({rule_id})."
E5005 = "Security blocking findings detected for check '{check_id}' ({rule_id})."
E6001 = "Qodana required input missing for check '{check_id}' ({rule_id})."
E6002 = "Qodana contract execution failed for check '{check_id}' ({rule_id})."
E6005 = "Qodana blocking findings detected for check '{check_id}' ({rule_id})."
E7001 = "Runner invocation is invalid for check '{check_id}' ({rule_id})."
E8004 = "Result schema validation failed for check '{check_id}' ({rule_id})."
E9002 = "CI command failed for check '{check_id}' ({rule_id})."
E9901 = "Error mapping/artifact-link failure for check '{check_id}' ({reason})."
12 changes: 12 additions & 0 deletions tools/ci/errors/steps.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[steps]
setup = "01"
policy = "10"
build = "20"
test = "30"
pack = "40"
security = "50"
qodana = "60"
runner = "70"
schema = "80"
generic = "90"
error_ux = "99"
Loading
Loading