[codex] Record Operator96 low-noise rerun tranche 03 (#305) #2239
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # RegProbe CI/CD Pipeline | |
| # Split into explicit build, unit, integration, code-quality, publish, and release jobs. | |
| name: CI/CD | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| release: | |
| types: [published] | |
| workflow_dispatch: | |
| concurrency: | |
| group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| env: | |
| DOTNET_VERSION: "8.0.x" | |
| PYTHON_VERSION: "3.12" | |
| CONFIGURATION: Release | |
| RUNTIME: win-x64 | |
| SOLUTION: RegProbe.sln | |
| UNIT_TEST_PROJECT: tests/tests.csproj | |
| INTEGRATION_TEST_PROJECT: tests.integration/tests.integration.csproj | |
| APP_PROJECT: app/app.csproj | |
| CLI_PROJECT: cli/cli.csproj | |
| ELEVATED_HOST_PROJECT: elevated-host/elevated-host.csproj | |
| COVERAGE_LINE_THRESHOLD: "31" | |
| COVERAGE_BRANCH_THRESHOLD: "17" | |
| COVERAGE_REPORT_PATH: tests/TestResults/Coverage/coverage.cobertura.xml | |
| jobs: | |
| build: | |
| name: Build | |
| runs-on: windows-latest | |
| steps: | |
| - name: Enable long paths for checkout | |
| run: git config --system core.longpaths true | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: ${{ env.DOTNET_VERSION }} | |
| - name: Setup Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| - name: Cache .NET packages | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.nuget/packages | |
| key: ${{ runner.os }}-dotnet-packages-${{ hashFiles('**/*.csproj') }} | |
| restore-keys: | | |
| ${{ runner.os }}-dotnet-packages- | |
| - name: Restore dependencies | |
| run: dotnet restore ${{ env.SOLUTION }} | |
| - name: Build solution | |
| run: dotnet build ${{ env.SOLUTION }} -c ${{ env.CONFIGURATION }} --no-restore | |
| unit-tests: | |
| name: Unit Tests | |
| needs: build | |
| runs-on: windows-latest | |
| steps: | |
| - name: Enable long paths for checkout | |
| run: git config --system core.longpaths true | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: ${{ env.DOTNET_VERSION }} | |
| - name: Setup Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| - name: Restore dependencies | |
| run: dotnet restore ${{ env.SOLUTION }} | |
| - name: Run unit tests with coverage gates | |
| run: > | |
| dotnet test ${{ env.UNIT_TEST_PROJECT }} | |
| -c ${{ env.CONFIGURATION }} | |
| --no-restore | |
| --logger trx | |
| --results-directory TestResults/Unit | |
| --collect:"XPlat Code Coverage" | |
| -- | |
| DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=cobertura | |
| - name: Normalize coverage report and enforce thresholds | |
| shell: bash | |
| run: | | |
| python - <<'PY' | |
| import os | |
| import shutil | |
| import sys | |
| from pathlib import Path | |
| from xml.etree import ElementTree as ET | |
| source_root = Path("TestResults/Unit") | |
| matches = sorted(source_root.glob("**/coverage.cobertura.xml")) | |
| if not matches: | |
| raise SystemExit("coverage.cobertura.xml was not produced by the collector") | |
| source_path = matches[0] | |
| dest_path = Path(os.environ["COVERAGE_REPORT_PATH"]) | |
| dest_path.parent.mkdir(parents=True, exist_ok=True) | |
| shutil.copyfile(source_path, dest_path) | |
| root = ET.fromstring(dest_path.read_bytes()) | |
| line_rate = float(root.attrib.get("line-rate", "0")) | |
| branch_rate = float(root.attrib.get("branch-rate", "0")) | |
| line_percent = round(line_rate * 100, 2) | |
| branch_percent = round(branch_rate * 100, 2) | |
| line_threshold = float(os.environ["COVERAGE_LINE_THRESHOLD"]) | |
| branch_threshold = float(os.environ["COVERAGE_BRANCH_THRESHOLD"]) | |
| failures = [] | |
| if line_percent < line_threshold: | |
| failures.append( | |
| f"line coverage {line_percent:.2f}% is below threshold {line_threshold:.2f}%" | |
| ) | |
| if branch_percent < branch_threshold: | |
| failures.append( | |
| f"branch coverage {branch_percent:.2f}% is below threshold {branch_threshold:.2f}%" | |
| ) | |
| if failures: | |
| raise SystemExit("; ".join(failures)) | |
| PY | |
| - name: Upload unit test results | |
| uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: unit-test-results | |
| path: TestResults/Unit/*.trx | |
| - name: Upload coverage report | |
| uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: coverage-report | |
| path: ${{ env.COVERAGE_REPORT_PATH }} | |
| - name: Write coverage summary | |
| if: always() | |
| shell: bash | |
| run: | | |
| python - <<'PY' | |
| import json | |
| import os | |
| from pathlib import Path | |
| from xml.etree import ElementTree as ET | |
| baseline_path = Path("Docs/metrics/baseline.json") | |
| coverage_path = Path(os.environ["COVERAGE_REPORT_PATH"]) | |
| if not baseline_path.exists() or not coverage_path.exists(): | |
| raise SystemExit(0) | |
| baseline = json.loads(baseline_path.read_text(encoding="utf-8")) | |
| current = ET.fromstring(coverage_path.read_bytes()) | |
| current_line = round(float(current.attrib.get("line-rate", "0")) * 100, 2) | |
| current_branch = round(float(current.attrib.get("branch-rate", "0")) * 100, 2) | |
| baseline_line = baseline["coverage"]["totals"]["line_percent"] | |
| baseline_branch = baseline["coverage"]["totals"]["branch_percent"] | |
| threshold_line = os.environ["COVERAGE_LINE_THRESHOLD"] | |
| threshold_branch = os.environ["COVERAGE_BRANCH_THRESHOLD"] | |
| lines = [ | |
| "## Coverage Summary", | |
| "", | |
| f"- Line coverage: `{current_line:.2f}%` (baseline `{baseline_line:.2f}%`, delta `{current_line - baseline_line:+.2f}`)", | |
| f"- Branch coverage: `{current_branch:.2f}%` (baseline `{baseline_branch:.2f}%`, delta `{current_branch - baseline_branch:+.2f}`)", | |
| f"- Thresholds: line `{threshold_line}%`, branch `{threshold_branch}%`", | |
| ] | |
| summary_path = Path(os.environ["GITHUB_STEP_SUMMARY"]) | |
| summary_path.write_text("\n".join(lines) + "\n", encoding="utf-8") | |
| PY | |
| integration-tests: | |
| name: Integration Tests | |
| needs: build | |
| runs-on: windows-latest | |
| if: github.event_name != 'release' | |
| steps: | |
| - name: Enable long paths for checkout | |
| run: git config --system core.longpaths true | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: ${{ env.DOTNET_VERSION }} | |
| - name: Restore dependencies | |
| run: dotnet restore ${{ env.SOLUTION }} | |
| - name: Run SAFE flow integration tests | |
| run: > | |
| dotnet test ${{ env.INTEGRATION_TEST_PROJECT }} | |
| -c ${{ env.CONFIGURATION }} | |
| --no-restore | |
| --logger trx | |
| --results-directory TestResults/Integration | |
| - name: Upload integration test results | |
| uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: integration-test-results | |
| path: TestResults/Integration/*.trx | |
| code-quality: | |
| name: Code Quality | |
| needs: build | |
| runs-on: windows-latest | |
| steps: | |
| - name: Enable long paths for checkout | |
| run: git config --system core.longpaths true | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: ${{ env.DOTNET_VERSION }} | |
| - name: Setup Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| - name: Restore dependencies | |
| run: dotnet restore ${{ env.SOLUTION }} | |
| - name: Build with warnings-as-errors | |
| run: dotnet build ${{ env.SOLUTION }} -c ${{ env.CONFIGURATION }} --no-restore /p:TreatWarningsAsErrors=true | |
| - name: Run analyzers with warnings-as-errors | |
| run: dotnet build ${{ env.SOLUTION }} -c ${{ env.CONFIGURATION }} --no-restore /p:RunAnalyzers=true /p:TreatWarningsAsErrors=true | |
| - name: Run Python pipeline tests | |
| run: > | |
| python -m unittest | |
| tests.python.test_architecture_invariants | |
| tests.python.test_release_asset_contract | |
| tests.python.test_public_repo_hygiene | |
| tests.python.test_repo_metrics | |
| -q | |
| - name: Check public repo hygiene | |
| run: python registry-research-framework/scripts/check_public_repo_hygiene.py | |
| - name: Ghidra autotrigger smoke gate | |
| if: runner.os != 'Windows' | |
| run: > | |
| python registry-research-framework/scripts/run_ghidra_autotrigger_smoke.py | |
| --output-root "${{ runner.temp }}/ghidra-autotrigger-smoke" | |
| --output registry-research-framework/audit/ghidra-autotrigger-smoke.json | |
| --markdown-output registry-research-framework/audit/ghidra-autotrigger-smoke.md | |
| - name: Ghidra autotrigger smoke gate skipped on Windows | |
| if: runner.os == 'Windows' | |
| run: echo "Skipping Ghidra autotrigger smoke gate on Windows; it requires python3/curl/virsh host tooling." | |
| - name: Research quality gate summary | |
| continue-on-error: true | |
| run: python registry-research-framework/scripts/run_research_quality_gate.py --skip-python-tests --skip-ghidra-smoke | |
| - name: Run PowerShell pipeline smoke checks | |
| run: pwsh -NoProfile -ExecutionPolicy Bypass -File tests/Invoke-PipelineQualitySmoke.ps1 | |
| - name: Upload research quality gate artifacts | |
| uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: research-quality-gate | |
| path: | | |
| registry-research-framework/audit/research-quality-gate.json | |
| registry-research-framework/audit/research-quality-gate.md | |
| registry-research-framework/audit/public-repo-hygiene-check.json | |
| registry-research-framework/audit/public-repo-hygiene-check.md | |
| registry-research-framework/audit/ghidra-autotrigger-smoke.json | |
| registry-research-framework/audit/ghidra-autotrigger-smoke.md | |
| registry-research-framework/audit/ghidra-autotrigger-smoke-check.json | |
| registry-research-framework/audit/ghidra-autotrigger-smoke-check.md | |
| registry-research-framework/audit/ghidra-autotrigger-smoke/** | |
| registry-research-framework/audit/etw-stackwalk-capture-plan.json | |
| registry-research-framework/audit/etw-stackwalk-capture-plan.md | |
| registry-research-framework/audit/etw-stackwalk-capture-plan-check.json | |
| registry-research-framework/audit/etw-stackwalk-capture-plan-check.md | |
| publish: | |
| name: Publish Application | |
| needs: [unit-tests, integration-tests, code-quality] | |
| runs-on: windows-latest | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/main' | |
| steps: | |
| - name: Enable long paths for checkout | |
| run: git config --global core.longpaths true | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: ${{ env.DOTNET_VERSION }} | |
| - name: Setup Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| - name: Restore dependencies | |
| run: dotnet restore ${{ env.SOLUTION }} | |
| - name: Build ElevatedHost | |
| run: dotnet publish ${{ env.ELEVATED_HOST_PROJECT }} -c ${{ env.CONFIGURATION }} -r ${{ env.RUNTIME }} --self-contained -o publish/ElevatedHost | |
| - name: Publish main application | |
| run: dotnet publish ${{ env.APP_PROJECT }} -c ${{ env.CONFIGURATION }} -r ${{ env.RUNTIME }} --self-contained -o publish | |
| - name: Publish CLI | |
| run: dotnet publish ${{ env.CLI_PROJECT }} -c ${{ env.CONFIGURATION }} -r ${{ env.RUNTIME }} --self-contained false -o publish-cli | |
| - name: Copy documentation | |
| run: | | |
| if (Test-Path "Docs") { | |
| Copy-Item -Path "Docs" -Destination "publish/Docs" -Recurse -Force | |
| } | |
| if (Test-Path "Docs/product/cli.md") { | |
| New-Item -ItemType Directory -Path "publish-cli/Docs/product" -Force | Out-Null | |
| Copy-Item -Path "Docs/product/cli.md" -Destination "publish-cli/Docs/product/cli.md" -Force | |
| } | |
| if (Test-Path "README.md") { | |
| Copy-Item -Path "README.md" -Destination "publish-cli/README.md" -Force | |
| } | |
| if (Test-Path "LICENSE") { | |
| Copy-Item -Path "LICENSE" -Destination "publish-cli/LICENSE" -Force | |
| } | |
| - name: Verify ElevatedHost packaging | |
| run: | | |
| $hostPath = "publish/ElevatedHost/RegProbe.ElevatedHost.exe" | |
| if (!(Test-Path $hostPath)) { | |
| Write-Error "CRITICAL: ElevatedHost.exe not found at $hostPath" | |
| exit 1 | |
| } | |
| - name: Verify main application | |
| run: | | |
| $appPath = "publish/RegProbe.App.exe" | |
| if (!(Test-Path $appPath)) { | |
| Write-Error "CRITICAL: Main application not found at $appPath" | |
| exit 1 | |
| } | |
| - name: Verify CLI packaging | |
| run: | | |
| $cliPath = "publish-cli/RegProbe.CLI.exe" | |
| if (!(Test-Path $cliPath)) { | |
| Write-Error "CRITICAL: CLI not found at $cliPath" | |
| exit 1 | |
| } | |
| - name: Package publish artifacts | |
| run: > | |
| pwsh -NoProfile -ExecutionPolicy Bypass -File scripts/package_release_assets.ps1 | |
| -VersionLabel "${{ github.sha }}" | |
| -Runtime "${{ env.RUNTIME }}" | |
| -AppPublishDir "publish" | |
| -CliPublishDir "publish-cli" | |
| -OutputDir "release-assets" | |
| - name: Validate publish release assets | |
| run: > | |
| python scripts/check_release_assets.py | |
| --asset-dir release-assets | |
| --version-label "${{ github.sha }}" | |
| --runtime "${{ env.RUNTIME }}" | |
| --write-report release-assets/release-asset-check.json | |
| - name: Write publish package summary | |
| shell: bash | |
| run: | | |
| python - <<'PY' | |
| import json | |
| import os | |
| from pathlib import Path | |
| report = json.loads(Path("release-assets/release-asset-check.json").read_text(encoding="utf-8")) | |
| lines = [ | |
| "## Publish Packages", | |
| "", | |
| f"- `RegProbe-Portable-{os.environ['GITHUB_SHA']}-{os.environ['RUNTIME']}.zip`", | |
| f"- `RegProbe-Cli-{os.environ['GITHUB_SHA']}-{os.environ['RUNTIME']}.zip`", | |
| f"- `RegProbe-{os.environ['GITHUB_SHA']}-{os.environ['RUNTIME']}-sha256.txt`", | |
| f"- Release asset validation: `{report['status']}`", | |
| ] | |
| summary_path = Path(os.environ["GITHUB_STEP_SUMMARY"]) | |
| summary_path.write_text("\n".join(lines) + "\n", encoding="utf-8") | |
| PY | |
| - name: Upload publish artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: RegProbe-${{ github.sha }} | |
| path: | | |
| publish/ | |
| publish-cli/ | |
| release-assets/ | |
| retention-days: 30 | |
| release: | |
| name: Create Release | |
| needs: [unit-tests, integration-tests, code-quality] | |
| runs-on: windows-latest | |
| if: github.event_name == 'release' | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Enable long paths for checkout | |
| run: git config --global core.longpaths true | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: false | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: ${{ env.DOTNET_VERSION }} | |
| - name: Setup Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| - name: Restore dependencies | |
| run: dotnet restore ${{ env.SOLUTION }} | |
| - name: Build ElevatedHost | |
| run: dotnet publish ${{ env.ELEVATED_HOST_PROJECT }} -c ${{ env.CONFIGURATION }} -r ${{ env.RUNTIME }} --self-contained -o release/ElevatedHost | |
| - name: Publish main application | |
| run: dotnet publish ${{ env.APP_PROJECT }} -c ${{ env.CONFIGURATION }} -r ${{ env.RUNTIME }} --self-contained -o release | |
| - name: Publish CLI | |
| run: dotnet publish ${{ env.CLI_PROJECT }} -c ${{ env.CONFIGURATION }} -r ${{ env.RUNTIME }} --self-contained false -o release-cli | |
| - name: Copy documentation | |
| run: | | |
| if (Test-Path "Docs") { | |
| Copy-Item -Path "Docs" -Destination "release/Docs" -Recurse -Force | |
| } | |
| if (Test-Path "Docs/product/cli.md") { | |
| New-Item -ItemType Directory -Path "release-cli/Docs/product" -Force | Out-Null | |
| Copy-Item -Path "Docs/product/cli.md" -Destination "release-cli/Docs/product/cli.md" -Force | |
| } | |
| if (Test-Path "README.md") { | |
| Copy-Item -Path "README.md" -Destination "release-cli/README.md" -Force | |
| } | |
| if (Test-Path "LICENSE") { | |
| Copy-Item -Path "LICENSE" -Destination "release-cli/LICENSE" -Force | |
| } | |
| - name: Package release assets | |
| run: > | |
| pwsh -NoProfile -ExecutionPolicy Bypass -File scripts/package_release_assets.ps1 | |
| -VersionLabel "${{ github.event.release.tag_name }}" | |
| -Runtime "${{ env.RUNTIME }}" | |
| -AppPublishDir "release" | |
| -CliPublishDir "release-cli" | |
| -OutputDir "release-assets" | |
| - name: Validate release assets | |
| run: > | |
| python scripts/check_release_assets.py | |
| --asset-dir release-assets | |
| --version-label "${{ github.event.release.tag_name }}" | |
| --runtime "${{ env.RUNTIME }}" | |
| --write-report release-assets/release-asset-check.json | |
| - name: Write release package summary | |
| shell: bash | |
| env: | |
| RELEASE_TAG: ${{ github.event.release.tag_name }} | |
| run: | | |
| python - <<'PY' | |
| import json | |
| import os | |
| from pathlib import Path | |
| report = json.loads(Path("release-assets/release-asset-check.json").read_text(encoding="utf-8")) | |
| release_tag = os.environ["RELEASE_TAG"] | |
| lines = [ | |
| "## Release Packages", | |
| "", | |
| f"- `RegProbe-Portable-{release_tag}-{os.environ['RUNTIME']}.zip`", | |
| f"- `RegProbe-Cli-{release_tag}-{os.environ['RUNTIME']}.zip`", | |
| f"- `RegProbe-{release_tag}-{os.environ['RUNTIME']}-sha256.txt`", | |
| f"- Release asset validation: `{report['status']}`", | |
| ] | |
| summary_path = Path(os.environ["GITHUB_STEP_SUMMARY"]) | |
| summary_path.write_text("\n".join(lines) + "\n", encoding="utf-8") | |
| PY | |
| - name: Upload release asset | |
| uses: softprops/action-gh-release@v3 | |
| with: | |
| files: | | |
| release-assets/RegProbe-Portable-${{ github.event.release.tag_name }}-${{ env.RUNTIME }}.zip | |
| release-assets/RegProbe-Cli-${{ github.event.release.tag_name }}-${{ env.RUNTIME }}.zip | |
| release-assets/RegProbe-${{ github.event.release.tag_name }}-${{ env.RUNTIME }}-sha256.txt | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |