Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e88116a2d | ||
|
|
05a306148a |
@@ -2481,13 +2481,16 @@ jobs:
|
||||
value="$(sed -n "s/^$key=//p" "$allocation")"
|
||||
printf '%s=%s\n' "$key" "$value" >> "$GITHUB_OUTPUT"
|
||||
done
|
||||
refs_fingerprint="$(
|
||||
git for-each-ref --format='%(refname)=%(objectname)' \
|
||||
refs/tags/v refs/tags/withdrawn/v \
|
||||
| LC_ALL=C sort \
|
||||
| sha256sum \
|
||||
| awk '{print $1}'
|
||||
)"
|
||||
refs_unsorted="$RUNNER_TEMP/release-refs.unsorted"
|
||||
refs_manifest="$RUNNER_TEMP/release-refs"
|
||||
git for-each-ref --format='%(refname)=%(objectname)' \
|
||||
'refs/tags/v*' 'refs/tags/withdrawn/v*' > "$refs_unsorted"
|
||||
LC_ALL=C sort "$refs_unsorted" > "$refs_manifest"
|
||||
test -s "$refs_manifest" || {
|
||||
echo "release ref manifest is empty after fetching allocated tags" >&2
|
||||
exit 1
|
||||
}
|
||||
refs_fingerprint="$(sha256sum "$refs_manifest" | awk '{print $1}')"
|
||||
echo "release_commit=$GITHUB_SHA" >> "$GITHUB_OUTPUT"
|
||||
echo "refs_fingerprint=$refs_fingerprint" >> "$GITHUB_OUTPUT"
|
||||
case "$OSS_MIRROR" in enabled|deferred) ;; *) exit 1 ;; esac
|
||||
|
||||
@@ -17,6 +17,7 @@ This beta validates guarded local release compatibility and tag-bound OSS deferr
|
||||
### Fixed
|
||||
|
||||
- **Guarded local release compatibility** — The tag-push Release workflow now accepts the `Channel`-only annotated tags created by the guarded local release entry while continuing to reject any partial cloud-only seal metadata.
|
||||
- **Cloud release tag allocation fingerprint** — Release planning now fingerprints the actual `v*` and `withdrawn/v*` refs fetched from GitHub, matching the seal job's API view instead of hashing an empty non-wildcard ref prefix and rejecting every publish before tag creation.
|
||||
|
||||
## [1.0.53-beta.5] - 2026-07-21
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
@@ -836,7 +837,8 @@ func TestReleaseWorkflowPlansAndSealsCurrentMainInTheCloud(t *testing.T) {
|
||||
"persist-credentials: false",
|
||||
`refs/remotes/origin/main)" = "$GITHUB_SHA`,
|
||||
"next-release-version.sh",
|
||||
"refs/tags/withdrawn/v",
|
||||
`'refs/tags/v*' 'refs/tags/withdrawn/v*'`,
|
||||
"release ref manifest is empty after fetching allocated tags",
|
||||
"refs_fingerprint",
|
||||
"Validate the candidate release contract before sealing",
|
||||
"release-contract.sh",
|
||||
@@ -850,6 +852,9 @@ func TestReleaseWorkflowPlansAndSealsCurrentMainInTheCloud(t *testing.T) {
|
||||
if strings.Contains(plan, "contents: write") {
|
||||
t.Error("cloud release planning must remain read-only")
|
||||
}
|
||||
if strings.Contains(plan, "refs/tags/v refs/tags/withdrawn/v") {
|
||||
t.Error("cloud release planning must use wildcard ref patterns that match the seal API prefixes")
|
||||
}
|
||||
|
||||
for _, required := range []string{
|
||||
"name: Seal cloud release tag",
|
||||
@@ -884,6 +889,66 @@ func TestReleaseWorkflowPlansAndSealsCurrentMainInTheCloud(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestReleaseFingerprintRefPatternsMatchAllAllocatedTags(t *testing.T) {
|
||||
t.Parallel()
|
||||
repo := t.TempDir()
|
||||
mustRun(t, repo, "git", "init", "-b", "main")
|
||||
mustRun(t, repo, "git", "config", "user.name", "Release Fingerprint Test")
|
||||
mustRun(t, repo, "git", "config", "user.email", "release-fingerprint@example.com")
|
||||
mustWriteFile(t, filepath.Join(repo, "tracked"), []byte("fixture\n"), 0o644)
|
||||
mustRun(t, repo, "git", "add", "tracked")
|
||||
mustRun(t, repo, "git", "commit", "-m", "fixture")
|
||||
|
||||
allocatedTags := []string{
|
||||
"v1.0.52",
|
||||
"v1.0.53-beta.5",
|
||||
"withdrawn/v1.0.51",
|
||||
}
|
||||
for _, tag := range allocatedTags {
|
||||
mustRun(t, repo, "git", "tag", "-a", tag, "-m", "Release "+tag)
|
||||
}
|
||||
mustRun(t, repo, "git", "tag", "-a", "release/v1.0.52", "-m", "unrelated namespace")
|
||||
legacy := exec.Command(
|
||||
"git", "for-each-ref", "--format=%(refname)=%(objectname)",
|
||||
"refs/tags/v", "refs/tags/withdrawn/v",
|
||||
)
|
||||
legacy.Dir = repo
|
||||
legacyOutput, err := legacy.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("legacy git for-each-ref error = %v\noutput:\n%s", err, legacyOutput)
|
||||
}
|
||||
if strings.TrimSpace(string(legacyOutput)) != "" {
|
||||
t.Fatalf("legacy component patterns unexpectedly matched flat release refs:\n%s", legacyOutput)
|
||||
}
|
||||
|
||||
cmd := exec.Command(
|
||||
"git", "for-each-ref", "--format=%(refname)=%(objectname)",
|
||||
"refs/tags/v*", "refs/tags/withdrawn/v*",
|
||||
)
|
||||
cmd.Dir = repo
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("git for-each-ref error = %v\noutput:\n%s", err, output)
|
||||
}
|
||||
got := strings.Split(strings.TrimSpace(string(output)), "\n")
|
||||
sort.Strings(got)
|
||||
|
||||
want := make([]string, 0, len(allocatedTags))
|
||||
for _, tag := range allocatedTags {
|
||||
object := strings.TrimSpace(mustOutput(t, repo, "git", "rev-parse", "refs/tags/"+tag))
|
||||
want = append(want, "refs/tags/"+tag+"="+object)
|
||||
}
|
||||
sort.Strings(want)
|
||||
if strings.Join(got, "\n") != strings.Join(want, "\n") {
|
||||
t.Fatalf("release ref set differs from the seal API set\ngot:\n%s\nwant:\n%s", strings.Join(got, "\n"), strings.Join(want, "\n"))
|
||||
}
|
||||
workflowDigest := sha256.Sum256([]byte(strings.Join(got, "\n") + "\n"))
|
||||
sealDigest := sha256.Sum256([]byte(strings.Join(want, "\n") + "\n"))
|
||||
if workflowDigest != sealDigest {
|
||||
t.Fatalf("release ref fingerprint differs from seal fingerprint: workflow=%x seal=%x", workflowDigest, sealDigest)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReleaseWorkflowAcceptsGuardedLocalTagMetadata(t *testing.T) {
|
||||
t.Parallel()
|
||||
workflow := readReleaseWorkflow(t)
|
||||
|
||||
Reference in New Issue
Block a user