-
Notifications
You must be signed in to change notification settings - Fork 0
fix(ci): restore L9 analysis and settle Protocol body + Scorecard GHCR #284
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,186 @@ | ||
| # L9 Governed Analysis Pipeline — Python Preset (LOCKED) | ||
| # | ||
| # DO NOT EDIT — this file is managed by l9-ci-core presets/python. | ||
| # To update, pull the latest preset from Quantum-L9/l9-ci-core. | ||
| # | ||
| # This workflow runs the full L9 analysis pipeline: | ||
| # 1. Resolve governance config from .github/governance/ | ||
| # 2. Run semgrep with Python rulesets | ||
| # 3. Provision the SDK (immutable, pinned) | ||
| # 4. Normalize → Validate → Project → Route → Manifest → Upload | ||
| # 5. Publish results as GitHub Checks | ||
| name: L9 Analysis | ||
| on: | ||
| pull_request: | ||
| push: | ||
| branches: [main] | ||
| workflow_dispatch: | ||
|
|
||
| env: | ||
| L9_CORE_REF: "f88116503430aa18992b70d8d31063e34ff97ef1" | ||
| L9_PROFILE: "pr_fast" | ||
| L9_MATRIX_ID: "pr-semgrep" | ||
|
|
||
| permissions: | ||
| contents: read | ||
| checks: write | ||
|
Check warning on line 26 in .github/workflows/l9-analysis.yml
|
||
|
|
||
| concurrency: | ||
| group: l9-analysis-${{ github.workflow }}-${{ github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| jobs: | ||
| analyze: | ||
| name: Governed Semgrep Analysis | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 15 | ||
| outputs: | ||
| enabled: ${{ steps.gov.outputs.enabled }} | ||
| mode: ${{ steps.gov.outputs.mode }} | ||
| governance-digest: ${{ steps.gov.outputs.governance-digest }} | ||
| artifact-name: ${{ steps.names.outputs.artifact-name }} | ||
| permissions: | ||
| contents: read | ||
| checks: write | ||
|
cryptoxdog marked this conversation as resolved.
|
||
| steps: | ||
| - name: Checkout immutable event revision | ||
| env: | ||
| REPOSITORY: ${{ github.repository }} | ||
| REVISION: ${{ github.sha }} | ||
| TOKEN: ${{ github.token }} | ||
| run: | | ||
| set -euo pipefail | ||
| git init . | ||
| git remote add origin \ | ||
| "https://x-access-token:${TOKEN}@github.com/${REPOSITORY}.git" | ||
| git -c protocol.version=2 fetch --depth=1 origin "${REVISION}" | ||
| git checkout --detach FETCH_HEAD | ||
| git remote set-url origin "https://github.com/${REPOSITORY}.git" | ||
|
|
||
| - id: gov | ||
| name: Resolve governance | ||
| uses: Quantum-L9/l9-ci-core/.github/actions/resolve-governance@555d577eb805851b624cf7b0b8fc4df75a225d9f | ||
| with: | ||
| profile: ${{ env.L9_PROFILE }} | ||
| provider: semgrep | ||
| event-name: ${{ github.event_name }} | ||
| repository: ${{ github.repository }} | ||
| ref: ${{ github.ref }} | ||
|
|
||
| - id: names | ||
| name: Compute artifact names | ||
| env: | ||
| MATRIX_ID: ${{ env.L9_MATRIX_ID }} | ||
| run: | | ||
| set -euo pipefail | ||
| echo "artifact-name=l9-semgrep-${MATRIX_ID}-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" >> "$GITHUB_OUTPUT" | ||
|
|
||
| - name: Run semgrep | ||
| if: steps.gov.outputs.enabled == 'true' | ||
| run: | | ||
| set -euo pipefail | ||
| pip install --upgrade pip semgrep | ||
|
Check warning on line 82 in .github/workflows/l9-analysis.yml
|
||
|
cryptoxdog marked this conversation as resolved.
|
||
| mkdir -p "artifacts/raw/semgrep/${L9_MATRIX_ID}" | ||
| # No `|| true` (Baseline Ratchet rejects fail-open). Also no | ||
| # `--error`: findings must reach normalize/publish so governance | ||
| # can decide blocking vs advisory; `--error` exits 1 before that. | ||
| semgrep scan \ | ||
| --config p/python \ | ||
| --json \ | ||
| --output "artifacts/raw/semgrep/${L9_MATRIX_ID}/report.json" \ | ||
| --quiet | ||
| env: | ||
| L9_MATRIX_ID: ${{ env.L9_MATRIX_ID }} | ||
|
|
||
| - id: sdk | ||
| name: Provision immutable SDK | ||
| if: steps.gov.outputs.enabled == 'true' | ||
| uses: Quantum-L9/l9-ci-core/.github/actions/provision-sdk@0d28395428426853c44825c4645c23ee8ace23b1 | ||
|
|
||
| - name: Normalize provider report | ||
| if: steps.gov.outputs.enabled == 'true' | ||
| uses: Quantum-L9/l9-ci-core/.github/actions/invoke-sdk@f88116503430aa18992b70d8d31063e34ff97ef1 | ||
| with: | ||
| executable: ${{ steps.sdk.outputs.executable }} | ||
| operation: semgrep-normalize | ||
| input: artifacts/raw/semgrep/${{ env.L9_MATRIX_ID }}/report.json | ||
| output: .l9/runtime/${{ env.L9_MATRIX_ID }}/finding-bundle.json | ||
| root: . | ||
| snapshot-id: ${{ github.sha }} | ||
| revision: ${{ github.sha }} | ||
| strict: ${{ steps.gov.outputs.strict }} | ||
| required: ${{ steps.gov.outputs.required-provider }} | ||
| policy: ${{ steps.gov.outputs.sdk-policy }} | ||
| identity-map: .github/governance/semgrep-identity-map.yaml | ||
|
|
||
| - name: Validate canonical bundle | ||
| if: steps.gov.outputs.enabled == 'true' | ||
| uses: Quantum-L9/l9-ci-core/.github/actions/validate-bundle@84375ed2bc9e005048dfb6f74076fc420b4bc01c | ||
| with: | ||
| executable: ${{ steps.sdk.outputs.executable }} | ||
| bundle: .l9/runtime/${{ env.L9_MATRIX_ID }}/finding-bundle.json | ||
|
|
||
| - name: Project agent-review payload | ||
| if: steps.gov.outputs.enabled == 'true' | ||
| uses: Quantum-L9/l9-ci-core/.github/actions/invoke-sdk@f88116503430aa18992b70d8d31063e34ff97ef1 | ||
| with: | ||
| executable: ${{ steps.sdk.outputs.executable }} | ||
| operation: bundle-project-agent-payload | ||
| input: .l9/runtime/${{ env.L9_MATRIX_ID }}/finding-bundle.json | ||
| output: .l9/runtime/${{ env.L9_MATRIX_ID }}/agent-review-payload.json | ||
| strict: ${{ steps.gov.outputs.strict }} | ||
|
|
||
| - id: route | ||
| name: Route artifacts | ||
| if: steps.gov.outputs.enabled == 'true' | ||
| uses: Quantum-L9/l9-ci-core/.github/actions/route-artifacts@84375ed2bc9e005048dfb6f74076fc420b4bc01c | ||
| with: | ||
| provider: semgrep | ||
| matrix-id: ${{ env.L9_MATRIX_ID }} | ||
| raw-report: artifacts/raw/semgrep/${{ env.L9_MATRIX_ID }}/report.json | ||
| bundle: .l9/runtime/${{ env.L9_MATRIX_ID }}/finding-bundle.json | ||
| agent-payload: .l9/runtime/${{ env.L9_MATRIX_ID }}/agent-review-payload.json | ||
| destination-root: artifacts | ||
|
|
||
| - name: Build artifact manifest | ||
| if: steps.gov.outputs.enabled == 'true' | ||
| uses: Quantum-L9/l9-ci-core/.github/actions/build-artifact-manifest@555d577eb805851b624cf7b0b8fc4df75a225d9f | ||
| with: | ||
| provider: semgrep | ||
| matrix-id: ${{ env.L9_MATRIX_ID }} | ||
| sdk-revision: ${{ steps.sdk.outputs.sdk-revision }} | ||
| bundle: ${{ steps.route.outputs.bundle }} | ||
| agent-payload: ${{ steps.route.outputs.agent-payload }} | ||
| raw-directory: ${{ steps.route.outputs.raw-directory }} | ||
| output: artifacts/metadata/${{ env.L9_MATRIX_ID }}/artifact-manifest.json | ||
|
|
||
| - name: Upload analysis artifact set | ||
| if: steps.gov.outputs.enabled == 'true' | ||
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | ||
| with: | ||
| name: ${{ steps.names.outputs.artifact-name }} | ||
| path: | | ||
| artifacts/raw/semgrep/${{ env.L9_MATRIX_ID }}/ | ||
| artifacts/l9/${{ env.L9_MATRIX_ID }}/ | ||
| artifacts/metadata/${{ env.L9_MATRIX_ID }}/ | ||
| if-no-files-found: error | ||
| retention-days: 14 | ||
|
|
||
| publish: | ||
| name: Publish analysis (Core) | ||
| needs: analyze | ||
| if: needs.analyze.outputs.enabled == 'true' | ||
| uses: Quantum-L9/l9-ci-core/.github/workflows/publish-analysis.yml@0d28395428426853c44825c4645c23ee8ace23b1 | ||
| permissions: | ||
| actions: read | ||
| checks: write | ||
|
cryptoxdog marked this conversation as resolved.
|
||
| contents: read | ||
| with: | ||
| artifact-name: ${{ needs.analyze.outputs.artifact-name }} | ||
| profile: pr_fast | ||
| mode: ${{ needs.analyze.outputs.mode }} | ||
| provider: semgrep | ||
| matrix-id: pr-semgrep | ||
| governance-digest: ${{ needs.analyze.outputs.governance-digest }} | ||
| repository-revision: ${{ github.sha }} | ||
| workflow-result: ${{ needs.analyze.result }} | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| """Protocol method bodies must stay lint-clean on every in-repo gate (CEG#267).""" | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import ast | ||
| from pathlib import Path | ||
|
|
||
| import pytest | ||
|
|
||
| ENGINE = Path(__file__).resolve().parents[2] / "engine" | ||
|
|
||
|
|
||
| def _protocol_methods(path: Path) -> list[tuple[str, str, ast.AST]]: | ||
| tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path)) | ||
| found: list[tuple[str, str, ast.AST]] = [] | ||
| for node in ast.walk(tree): | ||
| if not isinstance(node, ast.ClassDef): | ||
| continue | ||
| if not any( | ||
| (isinstance(base, ast.Name) and base.id == "Protocol") | ||
| or (isinstance(base, ast.Attribute) and base.attr == "Protocol") | ||
| for base in node.bases | ||
| ): | ||
| continue | ||
| for item in node.body: | ||
| if isinstance(item, (ast.FunctionDef, ast.AsyncFunctionDef)): | ||
| found.append((path.as_posix(), f"{node.name}.{item.name}", item)) | ||
| return found | ||
|
|
||
|
|
||
| def _engine_protocol_methods() -> list[tuple[str, str, ast.AST]]: | ||
| rows: list[tuple[str, str, ast.AST]] = [] | ||
| for path in sorted(ENGINE.rglob("*.py")): | ||
| rows.extend(_protocol_methods(path)) | ||
| return rows | ||
|
|
||
|
|
||
| @pytest.mark.unit | ||
| def test_engine_has_protocol_methods() -> None: | ||
| assert _engine_protocol_methods(), "expected at least one engine Protocol method" | ||
|
|
||
|
|
||
| @pytest.mark.unit | ||
| def test_protocol_methods_have_no_executable_stub() -> None: | ||
| failures: list[str] = [] | ||
| for rel, qualname, fn in _engine_protocol_methods(): | ||
| executable = [] | ||
| for stmt in fn.body: | ||
| if ( | ||
| isinstance(stmt, ast.Expr) | ||
| and isinstance(stmt.value, ast.Constant) | ||
| and isinstance(stmt.value.value, str) | ||
| ): | ||
| continue | ||
| executable.append(stmt) | ||
| for stmt in executable: | ||
| if isinstance(stmt, ast.Pass): | ||
| failures.append(f"{rel}:{qualname} uses pass (ruff PIE790)") | ||
| elif isinstance(stmt, ast.Raise) and isinstance(stmt.exc, ast.Call): | ||
| func = stmt.exc.func | ||
| name = func.id if isinstance(func, ast.Name) else "" | ||
| if name == "NotImplementedError": | ||
| failures.append(f"{rel}:{qualname} raises NotImplementedError (STUB-001)") | ||
| elif isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.Constant) and stmt.value.value is Ellipsis: | ||
| failures.append(f"{rel}:{qualname} uses ... (github-code-quality no-op)") | ||
|
cryptoxdog marked this conversation as resolved.
|
||
| assert failures == [], "\n".join(failures) | ||
Uh oh!
There was an error while loading. Please reload this page.