@@ -330,13 +330,67 @@ jobs:
330330 RELEASE_CHANNEL : ${{ inputs.channel }}
331331 run : |
332332 set -euo pipefail
333- latest_flags=()
334- [ "$RELEASE_CHANNEL" = "stable" ] && latest_flags=(--latest)
333+ release_flags=()
334+ case "$RELEASE_CHANNEL" in
335+ stable) release_flags=(--latest) ;;
336+ canary) release_flags=(--prerelease) ;;
337+ esac
335338 gh release create "$RELEASE_TAG" \
336339 --repo "$GITHUB_REPOSITORY" \
337340 --title "GDS $RELEASE_TAG" \
338341 --verify-tag \
339- "${latest_flags [@]}" \
342+ "${release_flags [@]}" \
340343 --notes "Immutable GDS ${RELEASE_CHANNEL} bundle for ${RELEASE_TAG}. Multi-platform binaries + SPDX SBOM + SHA256SUMS, with keyless SLSA build provenance and SBOM attestations. Online: gh attestation verify <asset> -R ${GITHUB_REPOSITORY}. Offline: verify <asset> against provenance.sigstore.json and sbom.sigstore.json using trusted-root.jsonl, all attached here." \
341344 "$RELEASE_DIRECTORY"/* \
342345 "$EVIDENCE_DIRECTORY"/*
346+
347+ record-failure :
348+ name : Record failed release evidence
349+ needs : [build, attest, publish]
350+ if : ${{ always() && startsWith(github.ref, 'refs/tags/') && (needs.build.result == 'failure' || needs.attest.result == 'failure' || needs.publish.result == 'failure') }}
351+ runs-on : ubuntu-latest
352+ permissions :
353+ contents : write
354+ env :
355+ GH_TOKEN : ${{ github.token }}
356+ RELEASE_TAG : ${{ github.ref_name }}
357+ RELEASE_VERSION : ${{ inputs.version }}
358+ RELEASE_SEQUENCE : ${{ inputs.release_sequence }}
359+ RELEASE_CHANNEL : ${{ inputs.channel }}
360+ BUILD_RESULT : ${{ needs.build.result }}
361+ ATTEST_RESULT : ${{ needs.attest.result }}
362+ PUBLISH_RESULT : ${{ needs.publish.result }}
363+ steps :
364+ - name : Publish machine-readable failure envelope
365+ shell : bash
366+ run : |
367+ set -euo pipefail
368+ envelope="$RUNNER_TEMP/release-failure-envelope.json"
369+ failed_jobs="$RUNNER_TEMP/failed-jobs.json"
370+ jq -cn \
371+ --arg build "$BUILD_RESULT" \
372+ --arg attest "$ATTEST_RESULT" \
373+ --arg publish "$PUBLISH_RESULT" \
374+ '[{name:"build",result:$build},{name:"attest",result:$attest},{name:"publish",result:$publish}] | map(select(.result == "failure") | .name)' \
375+ > "$failed_jobs"
376+ jq -n \
377+ --arg version "$RELEASE_VERSION" \
378+ --argjson sequence "$RELEASE_SEQUENCE" \
379+ --arg channel "$RELEASE_CHANNEL" \
380+ --arg commit "$GITHUB_SHA" \
381+ --arg ref "$GITHUB_REF" \
382+ --argjson run_id "$GITHUB_RUN_ID" \
383+ --slurpfile failed "$failed_jobs" \
384+ '{schema_version:1,status:"failed",bundle_version:$version,release_sequence:$sequence,channel:$channel,source_commit:$commit,source_ref:$ref,workflow_run_id:$run_id,failed_jobs:$failed[0],superseded_by:null}' \
385+ > "$envelope"
386+ if gh release view "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
387+ gh release upload "$RELEASE_TAG" "$envelope" --repo "$GITHUB_REPOSITORY"
388+ else
389+ gh release create "$RELEASE_TAG" \
390+ --repo "$GITHUB_REPOSITORY" \
391+ --title "GDS $RELEASE_TAG (failed)" \
392+ --verify-tag \
393+ --prerelease \
394+ --notes "Release production failed. See release-failure-envelope.json for machine-readable evidence; retain this tag and release until a later release records it as superseded." \
395+ "$envelope"
396+ fi
0 commit comments