Compare commits

..
Author SHA1 Message Date
修雨 9e88116a2d fix(release): fingerprint allocated tag refs 2026-07-21 14:55:11 +08:00
修雨 05a306148a Merge pull request #715 from DingTalk-Real-AI/codex/allow-optional-oss-mirror
fix(release): defer unprovisioned OSS mirror
2026-07-21 14:34:27 +08:00
3 changed files with 77 additions and 8 deletions
+10 -7
View File
@@ -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
+1
View File
@@ -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
+66 -1
View File
@@ -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)