Skip to content

build(refactor-release): shadow GoReleaser releases on tags - #12760

Draft
DariuszPorowski wants to merge 1 commit into
dp/edge-tags-deprecationfrom
dp/goreleaser-shadow-release
Draft

build(refactor-release): shadow GoReleaser releases on tags#12760
DariuszPorowski wants to merge 1 commit into
dp/edge-tags-deprecationfrom
dp/goreleaser-shadow-release

Conversation

@DariuszPorowski

@DariuszPorowski DariuszPorowski commented Aug 20, 2026

Copy link
Copy Markdown
Member

Summary

Run the canonical GoReleaser configuration beside the existing v* tag release path without changing production authority:

  • publish the five candidate production images only as ghcr.io/radius-project/dev/${IMAGE}:${REL_VERSION}
  • upload candidate CLI binaries, split checksums, and GoReleaser metadata as workflow artifacts
  • disable GoReleaser SCM release creation in shadow mode
  • capture the current production image manifests immediately after publication and lock them by digest
  • compare same-run production and shadow CLI binaries byte-for-byte, including embedded build/runtime metadata
  • compare image names, platforms, complete OCI runtime configuration, auxiliary manifests, per-platform filesystem contents, UCP manifests, and embedded server binaries
  • retain a machine-readable parity report with the native GoReleaser split-checksum content format recorded as the sole explained difference

The shadow, digest-capture, and parity jobs are advisory (continue-on-error) and are not included in the production build gate or summary.

Reason for change

PR 7 of the GoReleaser release-lifecycle migration needs real tag builds to exercise GoReleaser against the production publisher for a complete RC-plus-final cycle before cutover. This exposes unexplained parity differences while the current release path remains authoritative and rollback is simply removal of the advisory jobs.

How to test

  • goreleaser check
  • make test-capture-release-image-digests test-goreleaser-shadow
  • make goreleaser-snapshot GORELEASER_ARGS=--skip=docker
  • make build-rad-linux-amd64 followed by byte comparison with dist/goreleaser/rad_linux_amd64_v1/rad
  • actionlint .github/workflows/build-release.yaml
  • ghalint run scoped to build-release.yaml
  • zizmor --pedantic .github/workflows/build-release.yaml
  • shellcheck and shfmt -d over the changed Bash scripts
  • Markdown lint/table checks for the release runbook

Local results: the complete GoReleaser snapshot built 21 targets and passed its parity verifier in 7m36s; the production and GoReleaser Linux amd64 CLI binaries had the identical SHA-256 digest eee31e09e5889c42d320d613978c464aa412f3cb5f5a9b4cdbf881303c4e6ef6.

The remaining exit criterion is observational: after merge, one full RC-plus-final cycle must complete with no unexplained parity differences.

File change summary

File Summary of change
.goreleaser.yaml Parameterize the image registry and SCM-release switch with production-safe defaults.
.github/workflows/build-release.yaml Add isolated advisory shadow publication, production-digest capture, and parity jobs.
.github/scripts/verify-goreleaser-shadow*.sh Verify same-run CLI and all-platform image parity, with hermetic success/failure tests.
.github/scripts/capture-release-image-digests*.sh Lock production channel images to immutable digests, with a fixture test.
.github/scripts/verify-goreleaser-snapshot.sh Add a pre-publish config guard and recognize the parameterized canonical config.
build/artifacts.mk, build/test.mk Expose local shadow, verification, digest-capture, and test targets.
docs/contributing/contributing-releases/README.md Document the advisory migration jobs and retained production authority.

@DariuszPorowski
DariuszPorowski requested review from a team as code owners August 20, 2026 23:52
@github-actions

github-actions Bot commented Aug 20, 2026

Copy link
Copy Markdown

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

OpenSSF Scorecard

PackageVersionScoreDetails
actions/actions/download-artifact 3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c 🟢 5.1
Details
CheckScoreReason
Maintained⚠️ 00 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Packaging⚠️ -1packaging workflow not detected
Code-Review🟢 10all changesets reviewed
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Binary-Artifacts🟢 10no binaries found in the repo
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
Fuzzing⚠️ 0project is not fuzzed
License🟢 10license file detected
Signed-Releases⚠️ -1no releases found
Security-Policy🟢 9security policy file detected
SAST🟢 10SAST tool is run on all commits
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
actions/actions/setup-go b7ad1dad31e06c5925ef5d2fc7ad053ef454303e 🟢 6.1
Details
CheckScoreReason
Maintained🟢 57 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 5
Code-Review🟢 10all changesets reviewed
Binary-Artifacts🟢 10no binaries found in the repo
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Packaging⚠️ -1packaging workflow not detected
Pinned-Dependencies🟢 7dependency not pinned by hash detected -- score normalized to 7
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
License🟢 10license file detected
Fuzzing⚠️ 0project is not fuzzed
Signed-Releases⚠️ -1no releases found
Security-Policy🟢 9security policy file detected
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
SAST🟢 10SAST tool is run on all commits
actions/actions/setup-python 5fda3b95a4ea91299a34e894583c3862153e4b97 🟢 6.6
Details
CheckScoreReason
Code-Review🟢 10all changesets reviewed
Maintained🟢 1021 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
Binary-Artifacts🟢 10no binaries found in the repo
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Packaging⚠️ -1packaging workflow not detected
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
Pinned-Dependencies🟢 7dependency not pinned by hash detected -- score normalized to 7
Fuzzing⚠️ 0project is not fuzzed
License🟢 10license file detected
Signed-Releases⚠️ -1no releases found
Security-Policy🟢 9security policy file detected
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
SAST🟢 9SAST tool is not run on all commits -- score normalized to 9
actions/actions/upload-artifact 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a 🟢 5.2
Details
CheckScoreReason
Code-Review🟢 10all changesets reviewed
Maintained⚠️ 00 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Packaging⚠️ -1packaging workflow not detected
Binary-Artifacts🟢 10no binaries found in the repo
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
Pinned-Dependencies⚠️ 1dependency not pinned by hash detected -- score normalized to 1
Fuzzing⚠️ 0project is not fuzzed
License🟢 10license file detected
Signed-Releases⚠️ -1no releases found
Security-Policy🟢 9security policy file detected
SAST🟢 10SAST tool is run on all commits
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
actions/docker/login-action dbcb813823bdd20940b903addbd779551569679f 🟢 8.5
Details
CheckScoreReason
Maintained🟢 1030 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
Security-Policy🟢 9security policy file detected
Binary-Artifacts🟢 10no binaries found in the repo
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Code-Review🟢 10all changesets reviewed
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Token-Permissions🟢 10GitHub workflow tokens follow principle of least privilege
Fuzzing⚠️ 0project is not fuzzed
License🟢 10license file detected
Signed-Releases⚠️ -1no releases found
Branch-Protection⚠️ -1internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration
Packaging🟢 10packaging workflow detected
Pinned-Dependencies🟢 6dependency not pinned by hash detected -- score normalized to 6
SAST🟢 9SAST tool detected but not run on all commits
actions/docker/setup-buildx-action bb05f3f5519dd87d3ba754cc423b652a5edd6d2c 🟢 8.6
Details
CheckScoreReason
Security-Policy🟢 9security policy file detected
Code-Review🟢 10all changesets reviewed
Binary-Artifacts🟢 10no binaries found in the repo
Maintained🟢 1030 commit(s) and 2 issue activity found in the last 90 days -- score normalized to 10
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Token-Permissions🟢 10GitHub workflow tokens follow principle of least privilege
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Fuzzing⚠️ 0project is not fuzzed
License🟢 10license file detected
Branch-Protection⚠️ -1internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration
Signed-Releases⚠️ -1no releases found
Packaging🟢 10packaging workflow detected
Pinned-Dependencies🟢 7dependency not pinned by hash detected -- score normalized to 7
SAST🟢 10SAST tool is run on all commits
actions/docker/setup-qemu-action 96fe6ef7f33517b61c61be40b68a1882f3264fb8 🟢 8.4
Details
CheckScoreReason
Maintained🟢 1030 commit(s) and 5 issue activity found in the last 90 days -- score normalized to 10
Code-Review🟢 10all changesets reviewed
Packaging⚠️ -1packaging workflow not detected
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Binary-Artifacts🟢 10no binaries found in the repo
Security-Policy🟢 9security policy file detected
Token-Permissions🟢 10GitHub workflow tokens follow principle of least privilege
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Fuzzing⚠️ 0project is not fuzzed
License🟢 10license file detected
Signed-Releases⚠️ -1no releases found
Branch-Protection⚠️ -1internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration
Pinned-Dependencies🟢 5dependency not pinned by hash detected -- score normalized to 5
SAST🟢 10SAST tool is run on all commits

Scanned Files

  • .github/workflows/build-release.yaml

Copilot AI lite review requested due to automatic review settings August 21, 2026 00:18
@DariuszPorowski
DariuszPorowski force-pushed the dp/goreleaser-shadow-release branch from 26a32d8 to 368b661 Compare August 21, 2026 00:18

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds advisory GoReleaser shadow releases, digest capture, and parity verification while retaining the existing production release path.

Changes:

  • Parameterizes GoReleaser publishing and release creation.
  • Adds shadow workflows, verification scripts, tests, and Make targets.
  • Documents the migration workflow.
  • Failed parity runs currently do not produce the promised machine-readable report.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Show a summary per file
File Summary
docs/contributing/contributing-releases/README.md Documents advisory migration jobs.
build/test.mk Includes new verification tests.
build/artifacts.mk Adds shadow and digest targets.
.goreleaser.yaml Parameterizes registries and SCM release behavior.
.github/workflows/build-release.yaml Adds advisory shadow, digest, and parity jobs.
.github/scripts/verify-goreleaser-snapshot.sh Validates the parameterized GoReleaser configuration.
.github/scripts/verify-goreleaser-shadow.sh Verifies CLI and image parity; failed runs lack reports.
.github/scripts/verify-goreleaser-shadow_test.sh Tests parity verification behavior.
.github/scripts/image-payload-manifest.py Produces image payload manifests.
.github/scripts/capture-release-image-digests.sh Captures immutable production image digests.
.github/scripts/capture-release-image-digests_test.sh Tests digest capture.
Suppressed comments (4)

.github/scripts/verify-goreleaser-shadow.sh:565

  • These prefixes restrict the comparison to the Radius binary (and UCPD manifests), so imageRuntimeFilesystem: "match" does not mean the per-platform filesystem matches: an extra runtime file or a different base-layer/package file is invisible. The Dockerfile text check only compares instructions and cannot prove the resolved base/image contents. Compare the exported filesystem, or make the excluded base-layer difference explicit in the parity contract and report.
    printf '%s\n' "${name}"
    if [[ "${name}" == "ucpd" ]]; then
        printf '%s\n' "manifest"
    fi

.github/scripts/verify-goreleaser-shadow.sh:505

  • This projection intentionally discards digest, size, and annotations from unknown-platform descriptors, so a changed auxiliary manifest payload can pass as equal when its mediaType/platform/type stay constant. The baseline generator preserves those fields (.github/scripts/release-parity-manifest.sh:420-427); retain and compare them in both baseline and current projections before reporting auxiliary parity.
                    | select(.platform.os == "unknown")
                    | {
                        mediaType,
                        platform: platform_name(.platform),
                        type: (.annotations[

.github/scripts/verify-goreleaser-shadow.sh:704

  • normalize_image preserves each root manifest's mediaType, but this verification never compares it; only platforms, per-platform config, and auxiliary metadata are checked. An OCI index and a Docker manifest list could therefore be reported as parity despite different top-level image contracts. Compare the two normalized media types before continuing.
        assert_json_equal "${shadow_config}" "${production_config}" \
            "runtime image configuration for ${name}"

.github/workflows/build-release.yaml:347

  • When verify-goreleaser-shadow.sh finds a mismatch, fail exits before write_report, so this always() upload has no file—the only run where a machine-readable report is needed—and if-no-files-found: warn leaves only log text. Emit a failure report (for example, from an exit trap) or otherwise generate it before returning so advisory mismatches remain downloadable as documented.
        if: always()
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: goreleaser-shadow-parity-${{ github.sha }}
          path: dist/goreleaser-shadow-parity.json

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

verify_contract "${baseline}"
verify_cli_artifacts "${cli_entries}" "${baseline}"
verify_images "${image_entries}" "${baseline}"
write_report "${baseline}" "${cli_entries}" "${image_entries}"
@github-actions

github-actions Bot commented Aug 21, 2026

Copy link
Copy Markdown

Unit Tests

    2 files    461 suites   8m 27s ⏱️
6 531 tests 6 529 ✅ 2 💤 0 ❌
7 807 runs  7 805 ✅ 2 💤 0 ❌

Results for commit 4c6612d.

♻️ This comment has been updated with latest results.

@codecov

codecov Bot commented Aug 21, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
⚠️ Please upload report for BASE (dp/edge-tags-deprecation@ead6402). Learn more about missing BASE report.

Additional details and impacted files
@@                     Coverage Diff                     @@
##             dp/edge-tags-deprecation   #12760   +/-   ##
===========================================================
  Coverage                            ?   59.82%           
===========================================================
  Files                               ?      779           
  Lines                               ?    46058           
  Branches                            ?        0           
===========================================================
  Hits                                ?    27556           
  Misses                              ?    18502           
  Partials                            ?        0           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

github-actions Bot commented Aug 21, 2026

Copy link
Copy Markdown

Functional Tests - upgrade-noncloud

1 tests   0 ✅  5m 7s ⏱️
1 suites  0 💤
1 files    1 ❌

For more details on these failures, see this check.

Results for commit 4c6612d.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 21, 2026

Copy link
Copy Markdown

Functional Tests - corerp-cloud

32 tests  ±0   31 ✅ ±0   20m 9s ⏱️ + 1m 6s
 2 suites ±0    1 💤 ±0 
 1 files   ±0    0 ❌ ±0 

Results for commit b4db810. ± Comparison against base commit 8c43de5.

♻️ This comment has been updated with latest results.

@DariuszPorowski
DariuszPorowski force-pushed the dp/goreleaser-shadow-release branch from 368b661 to 1f53cf6 Compare August 21, 2026 02:24
@DariuszPorowski
DariuszPorowski force-pushed the dp/goreleaser-shadow-release branch from 1f53cf6 to 8b90c54 Compare August 21, 2026 02:50
@DariuszPorowski
DariuszPorowski force-pushed the dp/goreleaser-shadow-release branch from 8b90c54 to b4db810 Compare August 21, 2026 03:31
@DariuszPorowski
DariuszPorowski force-pushed the dp/goreleaser-shadow-release branch from b4db810 to 3d96ff3 Compare August 21, 2026 16:39
@DariuszPorowski DariuszPorowski self-assigned this Aug 21, 2026
@DariuszPorowski
DariuszPorowski force-pushed the dp/goreleaser-shadow-release branch from 3d96ff3 to dc08516 Compare August 24, 2026 15:25
@DariuszPorowski DariuszPorowski linked an issue Aug 24, 2026 that may be closed by this pull request
@DariuszPorowski DariuszPorowski linked an issue Aug 25, 2026 that may be closed by this pull request
3 tasks
@DariuszPorowski
DariuszPorowski marked this pull request as draft August 28, 2026 16:00
@DariuszPorowski
DariuszPorowski force-pushed the dp/goreleaser-shadow-release branch from dc08516 to 22929e1 Compare August 28, 2026 16:36
@DariuszPorowski
DariuszPorowski force-pushed the dp/goreleaser-shadow-release branch from 22929e1 to 6fbc377 Compare August 28, 2026 16:54
@DariuszPorowski DariuszPorowski changed the title build: shadow GoReleaser releases on tags build(refactor-release): shadow GoReleaser releases on tags Aug 28, 2026
Run the canonical GoReleaser configuration beside the current tag release, publish candidate images only to the dev namespace, and suppress SCM release creation. Compare same-run CLI binaries and digest-locked image outputs across every platform, retaining an explicit parity report without affecting production release status.

Signed-off-by: Dariusz Porowski <3431813+DariuszPorowski@users.noreply.github.com>
@DariuszPorowski
DariuszPorowski force-pushed the dp/goreleaser-shadow-release branch from 6fbc377 to 4c6612d Compare September 3, 2026 16:22
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown

Functional Tests - corerp-noncloud

190 tests   186 ✅  1h 16m 12s ⏱️
  3 suites    2 💤
  1 files      2 ❌

For more details on these failures, see this check.

Results for commit 4c6612d.

@radius-functional-tests

radius-functional-tests Bot commented Sep 3, 2026

Copy link
Copy Markdown

Radius functional test overview

🔍 Go to test action run

Click here to see the test run details
Name Value
Repository radius-project/radius
Commit ref 4c6612d
Unique ID func2a00239a93
Image tag pr-func2a00239a93
  • Dapr: 1.14.4
  • Azure KeyVault CSI driver: 1.4.2
  • Azure Workload identity webhook: 1.3.0
  • Bicep recipe location ghcr.io/radius-project/dev/test/testrecipes/test-bicep-recipes/<name>:pr-func2a00239a93
  • Terraform recipe location http://tf-module-server.radius-test-tf-module-server.svc.cluster.local/<name>.zip (in cluster)
  • applications-rp test image location: ghcr.io/radius-project/dev/applications-rp:pr-func2a00239a93
  • dynamic-rp test image location: ghcr.io/radius-project/dev/dynamic-rp:pr-func2a00239a93
  • controller test image location: ghcr.io/radius-project/dev/controller:pr-func2a00239a93
  • ucp test image location: ghcr.io/radius-project/dev/ucpd:pr-func2a00239a93
  • deployment-engine test image location: ghcr.io/radius-project/deployment-engine:latest

Test Status

⌛ Building Radius and pushing container images for functional tests...
✅ Container images build succeeded
⌛ Publishing Bicep Recipes for functional tests...
✅ Recipe publishing succeeded
⌛ Starting corerp-cloud functional tests...
⌛ Starting ucp-cloud functional tests...
✅ ucp-cloud functional tests succeeded
✅ corerp-cloud functional tests succeeded

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Shadow GoReleaser releases on tags Migrate release automation to GoReleaser

2 participants