From f394e67c8036d9bc0b0b97f2e52020f8e8faabe5 Mon Sep 17 00:00:00 2001 From: Eddie Knight Date: Mon, 31 Aug 2026 00:13:52 -0500 Subject: [PATCH 1/5] fix(ci): stamp baseline target version and draft flag at publish time Published artifacts for v2026.08.28 shipped with their osps-baseline mapping-reference still reading 'draft' and metadata.draft still true, because only metadata.version was stamped at publish time. The committed YAML is correct as-is (in-repo it tracks devel), so extend the existing publish-time stamp instead of editing the documents each release: - pin each mapping document's osps-baseline reference to RELEASE_VERSION - set metadata.draft from ARTIFACT_DRAFT: false for explicitly versioned publishes (release tag or dispatch version input), true for the dev-SHA fallback; only applied where a draft flag already exists All 15 stamped artifacts verified against the CUE schema locally. Signed-off-by: Eddie Knight --- .github/workflows/publish.yaml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 14b97058..97c47244 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -42,6 +42,9 @@ jobs: cancel-in-progress: false env: RELEASE_VERSION: ${{ github.event.release.tag_name || inputs.version || format('v0.0.0-dev-{0}', github.sha) }} + # Explicitly versioned artifacts (release tag or dispatch version input) + # are published as non-draft; dev-SHA fallback publishes stay draft. + ARTIFACT_DRAFT: ${{ (github.event.release.tag_name || inputs.version) && 'false' || 'true' }} HUB_URL: https://hub.grc.store DRY_RUN_FLAGS: ${{ inputs.dry_run && '--dry-run --no-sign' || '' }} steps: @@ -64,15 +67,22 @@ jobs: sudo install grcli /usr/local/bin/grcli # One grcli invocation per artifact: positional args to `publish` describe - # ONE artifact, they are not a batch. metadata.version is stamped onto a - # staged copy (grcli uses it as the OCI tag); committed YAML is untouched. + # ONE artifact, they are not a batch. Release metadata is stamped onto a + # staged copy (grcli uses metadata.version as the OCI tag); committed YAML + # is untouched — in-repo it tracks devel, so its osps-baseline reference + # says "draft" and metadata.draft is true. The stamp pins the mapping + # documents' osps-baseline reference to the version being published and + # applies ARTIFACT_DRAFT wherever a draft flag exists. # Each artifact gets its own --output so dry-run layouts don't collide on # the shared tag. - name: Publish catalog and mapping documents to grc.store run: | for f in baseline.gemara.yaml baseline/mappings/*.yaml; do name=$(basename "$f" .yaml) - yq '.metadata.version = strenv(RELEASE_VERSION)' "$f" > "$RUNNER_TEMP/$name.yaml" + yq '.metadata.version = strenv(RELEASE_VERSION) | + (.metadata.mapping-references[] | select(.id == "osps-baseline")).version = strenv(RELEASE_VERSION) | + with(select(.metadata | has("draft")); .metadata.draft = env(ARTIFACT_DRAFT))' \ + "$f" > "$RUNNER_TEMP/$name.yaml" grcli publish "$RUNNER_TEMP/$name.yaml" --url "$HUB_URL" --license Apache-2.0 \ --output "grcli-out/$name" $DRY_RUN_FLAGS done From 238ea36141063b511802f575be119a2c61c4ef1f Mon Sep 17 00:00:00 2001 From: Eddie Knight Date: Mon, 31 Aug 2026 07:26:56 -0500 Subject: [PATCH 2/5] fix(ci): assert the publish-time stamp landed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A drifted osps-baseline reference id would make the version pin a silent no-op, and `cue vet` would still pass because "draft" is a valid version string — the same failure class that shipped v2026.08.28 unstamped. Assert the pin took effect on each staged mapping document before grcli publishes it; the catalog has no osps-baseline self-reference and is exempt. Also drop the `with(select(has("draft")))` guard: all 15 artifacts carry metadata.draft, so it only served to let a future artifact that forgot the flag ship without one instead of getting the correct value. Verified locally by running the publish loop verbatim with grcli stubbed: both trigger paths stamp all 15 artifacts and pass `cue vet`, and renaming the reference id aborts the loop before any mapping is published. Signed-off-by: Eddie Knight --- .github/workflows/publish.yaml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 97c47244..0f9e74a8 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -72,7 +72,7 @@ jobs: # is untouched — in-repo it tracks devel, so its osps-baseline reference # says "draft" and metadata.draft is true. The stamp pins the mapping # documents' osps-baseline reference to the version being published and - # applies ARTIFACT_DRAFT wherever a draft flag exists. + # sets metadata.draft from ARTIFACT_DRAFT. # Each artifact gets its own --output so dry-run layouts don't collide on # the shared tag. - name: Publish catalog and mapping documents to grc.store @@ -81,8 +81,15 @@ jobs: name=$(basename "$f" .yaml) yq '.metadata.version = strenv(RELEASE_VERSION) | (.metadata.mapping-references[] | select(.id == "osps-baseline")).version = strenv(RELEASE_VERSION) | - with(select(.metadata | has("draft")); .metadata.draft = env(ARTIFACT_DRAFT))' \ + .metadata.draft = env(ARTIFACT_DRAFT)' \ "$f" > "$RUNNER_TEMP/$name.yaml" + # A drifted reference id makes the pin above a silent no-op that + # `cue vet` still passes — "draft" is a valid version string — so + # assert the stamp landed rather than publishing an unstamped doc. + # The catalog has no osps-baseline self-reference, so it is exempt. + [ "$name" = baseline.gemara ] || yq -e '.metadata.mapping-references[] | + select(.id == "osps-baseline") | .version == strenv(RELEASE_VERSION)' \ + "$RUNNER_TEMP/$name.yaml" > /dev/null grcli publish "$RUNNER_TEMP/$name.yaml" --url "$HUB_URL" --license Apache-2.0 \ --output "grcli-out/$name" $DRY_RUN_FLAGS done From a27fb39c487d5ac99e40f8a3a19eee8355ee25e1 Mon Sep 17 00:00:00 2001 From: Eddie Knight Date: Mon, 31 Aug 2026 08:05:33 -0500 Subject: [PATCH 3/5] remove fallback if tag or draft version are not specified Signed-off-by: Eddie Knight --- .github/workflows/publish.yaml | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 0f9e74a8..79b8a975 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -19,7 +19,7 @@ on: type: boolean default: true version: - description: "Version stamped on every artifact, and the OCI tag. Defaults to a dev tag from the commit SHA." + description: "Version stamped on every artifact, and the OCI tag. Required for manual runs." type: string permissions: @@ -38,16 +38,24 @@ jobs: # same version must serialize, but two different releases must not queue # against each other (a queued run gets cancelled when a third arrives). concurrency: - group: publish-grc-store-${{ github.event.release.tag_name || inputs.version || github.sha }} + group: publish-grc-store-${{ github.event.release.tag_name || inputs.version }} cancel-in-progress: false env: - RELEASE_VERSION: ${{ github.event.release.tag_name || inputs.version || format('v0.0.0-dev-{0}', github.sha) }} - # Explicitly versioned artifacts (release tag or dispatch version input) - # are published as non-draft; dev-SHA fallback publishes stay draft. - ARTIFACT_DRAFT: ${{ (github.event.release.tag_name || inputs.version) && 'false' || 'true' }} + RELEASE_VERSION: ${{ github.event.release.tag_name || inputs.version }} + # If a version is manually typed instead of targeting an existing tag, the artifact is tagged as "draft" + ARTIFACT_DRAFT: ${{ github.event.release.tag_name && 'false' || 'true' }} HUB_URL: https://hub.grc.store DRY_RUN_FLAGS: ${{ inputs.dry_run && '--dry-run --no-sign' || '' }} steps: + # A dispatch with an empty version input would otherwise stamp + # metadata.version: "" and publish under an empty OCI tag. + - name: Require a release tag or version input + run: | + if [ -z "$RELEASE_VERSION" ]; then + echo "::error::No version to publish: run this from a published release, or pass the version input." + exit 1 + fi + - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: @@ -79,10 +87,12 @@ jobs: run: | for f in baseline.gemara.yaml baseline/mappings/*.yaml; do name=$(basename "$f" .yaml) + yq '.metadata.version = strenv(RELEASE_VERSION) | (.metadata.mapping-references[] | select(.id == "osps-baseline")).version = strenv(RELEASE_VERSION) | .metadata.draft = env(ARTIFACT_DRAFT)' \ "$f" > "$RUNNER_TEMP/$name.yaml" + # A drifted reference id makes the pin above a silent no-op that # `cue vet` still passes — "draft" is a valid version string — so # assert the stamp landed rather than publishing an unstamped doc. @@ -90,6 +100,7 @@ jobs: [ "$name" = baseline.gemara ] || yq -e '.metadata.mapping-references[] | select(.id == "osps-baseline") | .version == strenv(RELEASE_VERSION)' \ "$RUNNER_TEMP/$name.yaml" > /dev/null + grcli publish "$RUNNER_TEMP/$name.yaml" --url "$HUB_URL" --license Apache-2.0 \ --output "grcli-out/$name" $DRY_RUN_FLAGS done From c5cac8ad66a916d06c4e7c3cd755613c3068b2c9 Mon Sep 17 00:00:00 2001 From: Eddie Knight Date: Mon, 31 Aug 2026 08:05:46 -0500 Subject: [PATCH 4/5] ignore new generated files Signed-off-by: Eddie Knight --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 3a60d675..ebeb46f2 100644 --- a/.gitignore +++ b/.gitignore @@ -2,9 +2,11 @@ docs/versions/devel.md .DS_Store docs/_site -# generated output from go run ./... compile +# generated output docs/versions/devel-checklist.md docs/versions/devel-crosswalk.md +cmd/baseline.gemara.* +cmd/.* # go build artifacts cmd/security-baseline From 96ff6ddf607a2e2e0f79bd02a220b84817b8e806 Mon Sep 17 00:00:00 2001 From: Eddie Knight <21176439+eddie-knight@users.noreply.github.com> Date: Mon, 31 Aug 2026 09:40:29 -0500 Subject: [PATCH 5/5] Update .gitignore Signed-off-by: Eddie Knight <21176439+eddie-knight@users.noreply.github.com> --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index ebeb46f2..acc136e7 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,6 @@ docs/_site docs/versions/devel-checklist.md docs/versions/devel-crosswalk.md cmd/baseline.gemara.* -cmd/.* # go build artifacts cmd/security-baseline