diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f50f4f89..25327fb2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -267,18 +267,19 @@ jobs: # push-to-main + workflow_dispatch (full coverage), and PRs that touch ide/** or this workflow (the # `changes.ide` path-gate) — a pure-Python PR can't change the isolated npm project's result, so it's # skipped there. Not on the nightly cron: the engine heavy legs need that safety net, an isolated npm - # build gated on its own paths does not. The per-repo os matrix below keeps the 2x-billed windows-latest - # electron leg off the private repo while still running it free on the mirror. + # build gated on its own paths does not. The per-repo os matrix below runs the 2x-billed + # windows-latest electron leg here (this repo is public, so hosted runners are free) and drops it on + # a fork, where those minutes come out of the fork owner's own allowance. needs: changes if: github.event_name == 'workflow_dispatch' || needs.changes.outputs.ide == 'true' runs-on: ${{ matrix.os }} strategy: fail-fast: false - # Per-repo os matrix (built in `changes`): ubuntu-only on the private repo; ubuntu + windows-latest - # on the mirror. The windows-latest leg is the 2x-billed one that runs the @vscode/test-electron - # integration suite — running it ONLY on the free public mirror keeps that Windows cost off the - # private repo without losing the coverage. `ide` is not a required check and ci-gate does not - # `needs` it, so dropping the windows leg here can't wedge anything. + # Per-repo os matrix (built in `changes`): ubuntu + windows-latest on THIS repo, ubuntu-only on a + # fork. The windows-latest leg is the 2x-billed one that runs the @vscode/test-electron integration + # suite; this repo is public so hosted minutes are free, while on a fork they are billed to the + # fork owner. `ide` is not a required check and ci-gate does not `needs` it, so dropping the + # windows leg on a fork can't wedge anything. matrix: ${{ fromJSON(needs.changes.outputs.ide_matrix) }} defaults: run: @@ -304,9 +305,9 @@ jobs: # The vscode-free suites (the pure model layer: the engine link state + its two frozen boundary # allowlists, the settings-scope SEC-005 invariant, the graph/steps/HL7 models, …). They need no - # Extension Host, so they run on EVERY leg — including ubuntu on the private repo, where PRs land. - # Before this step they ran NOWHERE here: `npm test` is Windows-only and the private repo's ide - # matrix is ubuntu-only, so the entire node-side estate was type-checked and never executed. + # Extension Host, so they run on EVERY leg — including the ubuntu-only leg a fork gets. + # Before this step they ran NOWHERE on an ubuntu-only matrix: `npm test` is Windows-only, so the + # entire node-side estate was type-checked and never executed there. # ADR 0110's allowlists are only "asserted in CI" because of this line. - name: Unit tests (node-side, no VS Code) run: npm run test:unit @@ -340,12 +341,12 @@ jobs: # `ide`: run the ide build job only when a PR touches ide/** or this workflow (true on # push/dispatch for full coverage; false on the nightly cron). Not a required check. ide: ${{ steps.f.outputs.ide }} - # `matrix`: the `test` job's include list, per-repo — ubuntu-only on the private repo (the - # self-hosted Windows runners are retired), full ubuntu+windows matrix on the mirror (free - # hosted). Consumed as `matrix: ${{ fromJSON(needs.changes.outputs.matrix) }}` in `test`. + # `matrix`: the `test` job's include list, per-repo — full ubuntu+windows matrix HERE (this repo is + # public, so hosted runners are free; the self-hosted Windows runners are retired), ubuntu-only on + # a fork. Consumed as `matrix: ${{ fromJSON(needs.changes.outputs.matrix) }}` in `test`. matrix: ${{ steps.f.outputs.matrix }} - # `ide_matrix`: the `ide` job's os matrix, per-repo — ubuntu-only on the private repo; ubuntu + - # windows-latest on the mirror, where the 2x-billed VS Code electron leg runs FREE. Consumed as + # `ide_matrix`: the `ide` job's os matrix, per-repo — ubuntu + windows-latest HERE, where the + # 2x-billed VS Code electron leg runs free; ubuntu-only on a fork. Consumed as # `matrix: ${{ fromJSON(needs.changes.outputs.ide_matrix) }}` in `ide`. ide_matrix: ${{ steps.f.outputs.ide_matrix }} steps: @@ -360,9 +361,13 @@ jobs: BASE_SHA: ${{ github.event.pull_request.base.sha }} run: | # test matrix, per-repo — computed FIRST (independent of the event, before any early exit). - # Mirror (MEFORORG): full ubuntu + windows-2022 + windows-2025 on FREE hosted runners. - # Private repo (wshallwshall): the UBUNTU leg ONLY — the self-hosted Windows NucBox runners - # are retired. `hosted` = the runner labels (an array; see the `test` job's runs-on). + # THIS repo (MEFORORG/MessageFoundry, the source since the cutover): full ubuntu + windows-2022 + # + windows-2025 on FREE hosted runners — free because the repo is public. + # Anywhere else (a FORK): the UBUNTU leg ONLY, so a contributor's own minutes are not spent on + # the 2x-billed Windows legs. This branch used to mean "the private source repo"; that repo is + # now an inactive archive with Actions disabled, so a fork is the only thing that reaches it. + # The self-hosted Windows NucBox runners are retired either way. + # `hosted` = the runner labels (an array; see the `test` job's runs-on). # $GITHUB_REPOSITORY is a built-in runner env var, read here as plain shell (NOT a workflow- # expression interpolation into the run body), so it is zizmor-safe and cannot be misparsed as # an Actions expression the way a literal double-brace token in a run: block would be. @@ -375,7 +380,7 @@ jobs: echo "matrix={\"include\":[$U]}" >> "$GITHUB_OUTPUT" fi # ide matrix, per-repo (computed here with the test matrix, before any early exit): the - # 2x-billed windows-latest electron leg runs ONLY on the FREE public mirror; the private repo + # 2x-billed windows-latest electron leg runs only HERE, where hosted minutes are free; a fork # builds + type-checks the extension on ubuntu only. Consumed as # matrix: fromJSON(needs.changes.outputs.ide_matrix) in the ide job. (No shell vars here, so a # single-quoted literal JSON needs no escaping and carries no run-block workflow expression.) @@ -1075,9 +1080,10 @@ jobs: # re-validates a specific merge on demand. windows-service-smoke: name: windows service smoke (${{ matrix.os }}, py${{ matrix.python-version }}) - # Mirror-only: this spins up 2x-billed hosted Windows runners on BOTH Server SKUs, so run it FREE on - # the public mirror's nightly schedule and never on the private repo. ci-gate `needs` this job, but a - # `skipped` leg counts as a pass there, so gating it off the private repo cannot wedge the gate. + # This repo only: it spins up 2x-billed hosted Windows runners on BOTH Server SKUs, which is free + # here (public) and billed to the owner of a fork. Nightly schedule + manual dispatch only. ci-gate + # `needs` this job, but a `skipped` leg counts as a pass there, so gating it off a fork cannot wedge + # the gate. if: (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && github.repository == 'MEFORORG/MessageFoundry' runs-on: ${{ matrix.os }} strategy: diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index c5d17e54..8ee0669e 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -4,19 +4,21 @@ name: CodeQL # rules in security.yml — it finds vulnerable flows those pattern matchers can't, e.g. untrusted # HL7/config data reaching a sink across function boundaries. # -# WHY THIS IS GUARDED TO THE MIRROR: CodeQL uploads results to GitHub code scanning, which on a -# PRIVATE repo requires paid GitHub Advanced Security (see security.yml's header note). This repo's -# source of truth is private, but the OSS mirror MEFORORG/MessageFoundry is PUBLIC, where CodeQL + -# code scanning are FREE. So the analyze job is gated on the repo slug: it is SKIPPED (a no-op) on the -# private source repo and runs only once publish.ps1 mirrors this file to MEFORORG/MessageFoundry. -# Nothing to pay, nothing to fail on the private side. +# WHY THIS IS GUARDED TO THE REPO SLUG: CodeQL uploads results to GitHub code scanning, which on a +# PRIVATE repo requires paid GitHub Advanced Security (see security.yml's header note). THIS repo is +# PUBLIC, so CodeQL + code scanning are FREE here. The analyze job is gated on the slug so it stays a +# skipped no-op anywhere the upload would fail or be billed — a fork, or the retired private archive. +# +# (Pre-cutover this described MEFORORG as "the OSS mirror" of a private source repo, and said the job +# ran "once publish.ps1 mirrors this file". Both premises are gone: MEFORORG is the source, and the +# publish machinery was deleted. The GUARD was already right — only its rationale had rotted.) # # PINNING CAVEAT: this repo SHA-pins every Action for supply-chain integrity. actions/checkout below # reuses the repo's existing v7.0.0 pin; the github/codeql-action steps are on the v3 tag for now # because the live commit SHA must be looked up at authoring — SHA-pin them before treating this as a # hardened gate (OSSF Scorecard / zizmor would flag the unpinned tags). # -# ALTERNATIVE (no file): on the public mirror you can instead enable CodeQL "default setup" from +# ALTERNATIVE (no file): you can instead enable CodeQL "default setup" from # Settings -> Security -> Code scanning (one toggle, auto-pinned, auto-updated). Default setup and this # advanced workflow are mutually exclusive — if you turn default setup ON, delete this file. on: @@ -36,8 +38,9 @@ permissions: jobs: analyze: name: CodeQL (${{ matrix.language }}) - # ONLY on the public mirror — code scanning is free there; on the private source repo it would need - # paid GHAS, so this is a skipped no-op there. (GitHub Actions string == is case-insensitive.) + # ONLY on this repo — it is public, so code scanning is free; anywhere else (a fork, or the retired + # private archive) it would need paid GHAS, so this stays a skipped no-op there. (GitHub Actions + # string == is case-insensitive.) if: github.repository == 'MEFORORG/MessageFoundry' runs-on: ubuntu-latest permissions: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d1938f35..cc9f868f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -228,9 +228,10 @@ jobs: # gh attestation verify dist/ --repo MEFORORG/MessageFoundry # GATED to PUBLIC repos: the GitHub attestation API (actions/attest-build-provenance) is "not # available for user-owned private repositories" and FAILS there, aborting the job BEFORE the PyPI - # publish below. While this repo is private the step is SKIPPED (skip != fail, so the release - # proceeds); provenance is still covered by the Sigstore bundle (above) and the PyPI-side PEP 740 - # attestations (publish step below). Auto-resumes if a release ever runs from the public mirror. + # publish below. THIS repo is public since the cutover, so the step now RUNS and every release + # carries SLSA provenance. The gate remains for a private fork, where it SKIPS rather than fails + # (skip != fail, so the release proceeds) and provenance is still covered by the Sigstore bundle + # above plus the PyPI-side PEP 740 attestations from the publish step. # `github.event.repository.private` is populated on the tag-push event. - name: Attest build provenance (SLSA) for sdist + wheel if: ${{ !github.event.repository.private }} diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index e534b481..09e187d8 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -5,12 +5,14 @@ name: Scorecard # repo already does (SHA-pinned Actions, least-privilege tokens) and catches regressions, and yields a # public OpenSSF badge. # -# WHY GUARDED TO THE MIRROR: like CodeQL, Scorecard uploads SARIF to GitHub code scanning (paid GHAS on -# a private repo) and `publish_results: true` requires a PUBLIC repo. So this is gated to the public -# mirror MEFORORG/MessageFoundry — a skipped no-op on the private source repo, free on the mirror after -# publish.ps1 mirrors this file. NOTE: the Branch-Protection check then reflects the MIRROR's settings, -# not the private repo's; the workflow-hardening checks (Pinned-Dependencies / Dangerous-Workflow / -# Token-Permissions) assess the shared source and are the signal that transfers. +# WHY GUARDED TO THE REPO SLUG: like CodeQL, Scorecard uploads SARIF to GitHub code scanning (paid GHAS +# on a private repo) and `publish_results: true` requires a PUBLIC repo. THIS repo is public, so both +# are free here; the slug gate keeps it a skipped no-op anywhere else — a fork, or the retired private +# archive. Every check now assesses this repo directly, including Branch-Protection. +# +# (Pre-cutover MEFORORG was the published mirror, so the Branch-Protection score reflected the MIRROR's +# settings rather than the private source's, and this file only ran once publish.ps1 copied it across. +# Both caveats are obsolete: there is one repo, and the publish machinery was deleted.) # # PINNING CAVEAT: actions/checkout + actions/upload-artifact reuse this repo's existing SHA pins; the # ossf/scorecard-action and github/codeql-action/upload-sarif steps are on version tags pending a @@ -28,7 +30,7 @@ permissions: read-all jobs: analysis: name: Scorecard analysis - # ONLY on the public mirror (free code scanning + public badge); skipped no-op on the private repo. + # ONLY on this repo — public, so code scanning and the badge are free; skipped no-op on a fork. if: github.repository == 'MEFORORG/MessageFoundry' runs-on: ubuntu-latest permissions: