Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
242 changes: 221 additions & 21 deletions .github/workflows/build-baseline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,17 @@ permissions:
contents: read

jobs:
build-windows:
name: gate / build / windows
runs-on: windows-latest
build-windows-native:
name: build / windows / amd64
runs-on: windows-2025
strategy:
fail-fast: false
permissions:
contents: read
env:
BANDSCOPE_ARTIFACT_OS: windows
BANDSCOPE_ARTIFACT_ARCH: amd64
BANDSCOPE_TARGET_TRIPLE: x86_64-pc-windows-msvc
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
Expand All @@ -39,31 +45,71 @@ jobs:
with:
version: "0.8.6"
- name: Install Rust stable
shell: bash
run: rustup toolchain install stable --profile minimal
- name: Add Windows target
run: rustup target add $env:BANDSCOPE_TARGET_TRIPLE --toolchain stable
- name: Verify Windows antivirus baseline
shell: pwsh
run: |
function Write-AntivirusEvidence($message) {
Write-Host $message
if ($env:GITHUB_STEP_SUMMARY) {
Add-Content -Path $env:GITHUB_STEP_SUMMARY -Value $message
}
}

if (Get-Command Get-MpComputerStatus -ErrorAction SilentlyContinue) {
$status = Get-MpComputerStatus
Write-AntivirusEvidence "Antivirus check: Defender status AntivirusEnabled=$($status.AntivirusEnabled) RealTimeProtectionEnabled=$($status.RealTimeProtectionEnabled)."
if ($status.AntivirusEnabled) {
return
}
Write-AntivirusEvidence "Antivirus check: Defender telemetry is present but antivirus is not reported as enabled on this hosted runner."
}

$products = Get-CimInstance -Namespace root/SecurityCenter2 -ClassName AntiVirusProduct -ErrorAction SilentlyContinue
if ($products) {
Write-AntivirusEvidence "Antivirus check: SecurityCenter2 reported at least one antivirus product."
return
}

$defenderService = Get-Service -Name WinDefend -ErrorAction SilentlyContinue
if ($defenderService -and $defenderService.Status -in @('Running', 'StartPending')) {
Write-AntivirusEvidence "Antivirus check: WinDefend service is present and active."
return
}

Write-AntivirusEvidence "Antivirus check: no explicit antivirus telemetry was available on this hosted runner."
- name: Install node dependencies
run: npm ci
- name: Sync Python dependencies
run: uv sync --project services/analysis-engine --group dev --frozen
- name: Build frontend
run: npm run build --workspace @bandscope/desktop
- name: Build native shell
run: cargo +stable build --manifest-path apps/desktop/src-tauri/Cargo.toml --release --locked
- name: Package Windows artifact
run: cargo +stable build --manifest-path apps/desktop/src-tauri/Cargo.toml --release --locked --target $env:BANDSCOPE_TARGET_TRIPLE
- name: Package Windows amd64 artifact
run: python scripts/release/package_desktop_artifact.py
- name: Upload Windows artifact
- name: Upload Windows amd64 artifact
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: bandscope-windows-${{ github.sha }}
name: bandscope-windows-amd64-${{ github.sha }}
path: |
artifacts/*.zip
artifacts/*.sha256
artifacts/*.manifest.txt
build-macos:
name: gate / build / macos
runs-on: macos-latest

build-windows-arm64:
name: build / windows / arm64
runs-on: windows-11-arm
strategy:
fail-fast: false
permissions:
contents: read
env:
BANDSCOPE_ARTIFACT_OS: windows
BANDSCOPE_ARTIFACT_ARCH: arm64
BANDSCOPE_TARGET_TRIPLE: aarch64-pc-windows-msvc
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
Expand All @@ -80,39 +126,191 @@ jobs:
version: "0.8.6"
- name: Install Rust stable
run: rustup toolchain install stable --profile minimal
- name: Add Windows arm target
run: rustup target add $env:BANDSCOPE_TARGET_TRIPLE --toolchain stable
- name: Verify Windows antivirus baseline
shell: pwsh
run: |
function Write-AntivirusEvidence($message) {
Write-Host $message
if ($env:GITHUB_STEP_SUMMARY) {
Add-Content -Path $env:GITHUB_STEP_SUMMARY -Value $message
}
}

if (Get-Command Get-MpComputerStatus -ErrorAction SilentlyContinue) {
$status = Get-MpComputerStatus
Write-AntivirusEvidence "Antivirus check: Defender status AntivirusEnabled=$($status.AntivirusEnabled) RealTimeProtectionEnabled=$($status.RealTimeProtectionEnabled)."
if ($status.AntivirusEnabled) {
return
}
Write-AntivirusEvidence "Antivirus check: Defender telemetry is present but antivirus is not reported as enabled on this hosted runner."
}

$products = Get-CimInstance -Namespace root/SecurityCenter2 -ClassName AntiVirusProduct -ErrorAction SilentlyContinue
if ($products) {
Write-AntivirusEvidence "Antivirus check: SecurityCenter2 reported at least one antivirus product."
return
}

$defenderService = Get-Service -Name WinDefend -ErrorAction SilentlyContinue
if ($defenderService -and $defenderService.Status -in @('Running', 'StartPending')) {
Write-AntivirusEvidence "Antivirus check: WinDefend service is present and active."
return
}

Write-AntivirusEvidence "Antivirus check: no explicit antivirus telemetry was available on this hosted runner."
- name: Install node dependencies
run: npm ci
- name: Sync Python dependencies
run: uv sync --project services/analysis-engine --group dev --frozen
- name: Build frontend
run: npm run build --workspace @bandscope/desktop
- name: Build native shell
run: cargo +stable build --manifest-path apps/desktop/src-tauri/Cargo.toml --release --locked
- name: Package macOS artifact
run: cargo +stable build --manifest-path apps/desktop/src-tauri/Cargo.toml --release --locked --target $env:BANDSCOPE_TARGET_TRIPLE
- name: Package Windows arm64 artifact
run: python scripts/release/package_desktop_artifact.py
- name: Upload Windows arm64 artifact
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: bandscope-windows-arm64-${{ github.sha }}
path: |
artifacts/*.zip
artifacts/*.sha256
artifacts/*.manifest.txt

gate-windows:
name: gate / build / windows
runs-on: ubuntu-latest
needs:
- build-windows-native
- build-windows-arm64
steps:
- name: Confirm both Windows architectures built
run: true

build-macos-native:
name: build / macos / amd64
runs-on: macos-15-intel
strategy:
fail-fast: false
permissions:
contents: read
env:
BANDSCOPE_ARTIFACT_OS: macos
BANDSCOPE_ARTIFACT_ARCH: amd64
BANDSCOPE_TARGET_TRIPLE: x86_64-apple-darwin
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version: 22
cache: npm
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.12"
- uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5
with:
version: "0.8.6"
- name: Install Rust stable
run: rustup toolchain install stable --profile minimal
- name: Add macOS Intel target
run: rustup target add "$BANDSCOPE_TARGET_TRIPLE" --toolchain stable
- name: Install node dependencies
run: npm ci
- name: Sync Python dependencies
run: uv sync --project services/analysis-engine --group dev --frozen
- name: Build frontend
run: npm run build --workspace @bandscope/desktop
- name: Build native shell
run: cargo +stable build --manifest-path apps/desktop/src-tauri/Cargo.toml --release --locked --target "$BANDSCOPE_TARGET_TRIPLE"
- name: Package macOS amd64 artifact
run: python3 scripts/release/package_desktop_artifact.py
- name: Upload macOS artifact
- name: Upload macOS amd64 artifact
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: bandscope-macos-${{ github.sha }}
name: bandscope-macos-amd64-${{ github.sha }}
path: |
artifacts/*.zip
artifacts/*.sha256
artifacts/*.manifest.txt

build-macos-arm64:
name: build / macos / arm64
runs-on: macos-15
strategy:
fail-fast: false
permissions:
contents: read
env:
BANDSCOPE_ARTIFACT_OS: macos
BANDSCOPE_ARTIFACT_ARCH: arm64
BANDSCOPE_TARGET_TRIPLE: aarch64-apple-darwin
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version: 22
cache: npm
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.12"
- uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5
with:
version: "0.8.6"
- name: Install Rust stable
run: rustup toolchain install stable --profile minimal
- name: Add macOS arm target
run: rustup target add "$BANDSCOPE_TARGET_TRIPLE" --toolchain stable
- name: Install node dependencies
run: npm ci
- name: Sync Python dependencies
run: uv sync --project services/analysis-engine --group dev --frozen
- name: Build frontend
run: npm run build --workspace @bandscope/desktop
- name: Build native shell
run: cargo +stable build --manifest-path apps/desktop/src-tauri/Cargo.toml --release --locked --target "$BANDSCOPE_TARGET_TRIPLE"
- name: Package macOS arm64 artifact
run: python3 scripts/release/package_desktop_artifact.py
- name: Upload macOS arm64 artifact
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: bandscope-macos-arm64-${{ github.sha }}
path: |
artifacts/*.zip
artifacts/*.sha256
artifacts/*.manifest.txt

gate-macos:
name: gate / build / macos
runs-on: ubuntu-latest
needs:
- build-macos-native
- build-macos-arm64
steps:
- name: Confirm both macOS architectures built
run: true

attach-windows-release-artifact:
name: release-artifact / windows
if: github.event_name == 'release'
runs-on: ubuntu-latest
needs:
- build-windows
- build-windows-native
- build-windows-arm64
permissions:
contents: write
steps:
- uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
name: bandscope-windows-${{ github.sha }}
pattern: bandscope-windows-*-${{ github.sha }}
path: artifacts
- name: Attach Windows artifact to release
merge-multiple: true
- name: Attach Windows artifacts to release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RELEASE_TAG: ${{ github.event.release.tag_name }}
Expand All @@ -123,15 +321,17 @@ jobs:
if: github.event_name == 'release'
runs-on: ubuntu-latest
needs:
- build-macos
- build-macos-native
- build-macos-arm64
permissions:
contents: write
steps:
- uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
name: bandscope-macos-${{ github.sha }}
pattern: bandscope-macos-*-${{ github.sha }}
path: artifacts
- name: Attach macOS artifact to release
merge-multiple: true
- name: Attach macOS artifacts to release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RELEASE_TAG: ${{ github.event.release.tag_name }}
Expand Down
8 changes: 6 additions & 2 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# ARCHITECTURE.md

Last updated: 2026-03-10
Last updated: 2026-03-11

## Brand source

Expand All @@ -22,6 +22,8 @@ Last updated: 2026-03-10

- Windows and macOS build security policy lives in `docs/security/cross-platform-build-policy.md`.
- Target-OS builds are merge gates and release-validation controls, not optional compatibility checks.
- Windows amd64 + arm64 and macOS amd64 + arm64 are all part of the protected-branch and release-validation build baseline.
- Windows build runners should verify antivirus protection before native packaging begins.

## GitHub bootstrap source

Expand Down Expand Up @@ -86,6 +88,8 @@ Last updated: 2026-03-10
- Supply-chain controls are part of the bootstrap architecture, not a release-afterthought.
- Dependency review, audit, supply-chain inventory validation, and SBOM generation are expected protected-branch gates for both `develop` and `main`.
- Cross-platform Windows and macOS build coverage is part of the bootstrap security architecture.
- Release artifacts, checksums, and manifests should encode both OS and architecture so packaged binaries remain traceable.
- Exact Windows 10 and macOS 24/25 GitHub-hosted coverage is a platform-capability constraint today; the current hosted baseline uses the closest published explicit runner labels and must move to self-hosted or larger runners if exact-version enforcement becomes mandatory.
- GitHub-facing setup is staged: no-git -> local-git -> GitHub-connected -> protected-branches with required checks.
- Shared contracts live in `packages/shared-types` so the UI can evolve without importing Python internals.
- Shared contracts should ultimately model section, role, cue, confidence, and export artifacts explicitly enough that desktop UI and analysis outputs do not invent their own parallel schemas.
Expand All @@ -96,7 +100,7 @@ Last updated: 2026-03-10

- `scripts/harness/quickcheck.sh` is the primary local verification entrypoint.
- `scripts/checks/check_rust.sh` is an opt-in local Rust/Tauri gate used when the host has the native desktop toolchain ready.
- CI mirrors the default sequence for JS and Python, and adds a dedicated macOS Rust check job.
- CI mirrors the default sequence for JS and Python, and adds dedicated Windows/macOS native build coverage for both amd64 and arm64 runners.
- Smoke-grade app verification is currently the React shell render plus Python engine health report.
- Security docs and checks are part of the default quickcheck path so design drift is caught early.
- Supply-chain docs, workflow pinning, and lockfile verification are part of the default quickcheck path so dependency drift is caught early.
Expand Down
Loading
Loading