diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 14b97058..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,13 +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) }} + 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: @@ -64,15 +75,32 @@ 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 + # 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 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) | + .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 diff --git a/.gitignore b/.gitignore index 3a60d675..acc136e7 100644 --- a/.gitignore +++ b/.gitignore @@ -2,9 +2,10 @@ 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.* # go build artifacts cmd/security-baseline