Skip to content

[codex] Record Operator96 low-noise rerun tranche 03 (#305) #2239

[codex] Record Operator96 low-noise rerun tranche 03 (#305)

[codex] Record Operator96 low-noise rerun tranche 03 (#305) #2239

Workflow file for this run

# 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 }}