Skip to content

Commit 1eeb25b

Browse files
committed
fix(release): tag and pack the resolved release commit, never the dispatch head
Advances #8525. The reconcile self-heal dispatches publish-*.yml bare (against main head), and the bare-dispatch path tagged-and-packed HEAD -- during today's runner backlog that placed ui-kit-v1.1.2 two commits late, sweeping the would-be 1.1.3 fix into the 1.1.2 tag and published artifact and zombifying the open v1.1.3 release PR (release-please saw 'No commits for path' behind the misplaced tag and could neither regenerate nor prune it; closed by hand as #8506). All four publish workflows now resolve the RELEASE COMMIT -- the newest main commit that introduced the current version string into the package's own package.json (the release PR's merge commit) -- then: - verify it is reachable from main, - verify any pre-existing tag points AT it (not at HEAD), - detach onto it so typecheck/pack build the exact tree the version number describes, - create the annotated tag at it explicitly. Unresolvable ⇒ hard abort, never a head fallback. The release-please dispatch path (dispatched against the tag itself) is byte-identical in effect: there HEAD already IS the release commit. Regression pins in test/unit/publish-release-tag-pins.test.ts cover the resolver, the fail-loud arm, the detach, and the death of every head-tagging form across all four workflows.
1 parent a20ddd8 commit 1eeb25b

5 files changed

Lines changed: 186 additions & 44 deletions

File tree

.github/workflows/publish-engine.yml

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ jobs:
3838
outputs:
3939
version: ${{ steps.version.outputs.version }}
4040
tag: ${{ steps.version.outputs.tag }}
41+
release_sha: ${{ steps.version.outputs.release_sha }}
4142
steps:
4243
- name: Checkout
4344
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
@@ -71,19 +72,41 @@ jobs:
7172
exit 1
7273
fi
7374
TAG="engine-v${VERSION}"
75+
# The RELEASE COMMIT is the newest main commit that introduced this exact version string
76+
# into package.json (the release PR's merge commit) -- NOT whatever main head happens to
77+
# be when this run was dispatched. A bare/reconcile dispatch used to tag-and-pack HEAD,
78+
# which silently swept any commits that landed after the version bump into the tag and the
79+
# published artifact (#8525: ui-kit-v1.1.2 got tagged two commits late during a runner
80+
# backlog, absorbing the 1.1.3 fix and zombifying its release PR). Resolving the commit
81+
# from the version string keeps the release-please-dispatched path a no-op (it dispatches
82+
# against the tag, so HEAD already IS this commit) while making late dispatches correct.
83+
RELEASE_SHA="$(git log -n 1 --format=%H -S "\"version\": \"${VERSION}\"" refs/remotes/origin/main -- packages/loopover-engine/package.json)"
84+
if [ -z "$RELEASE_SHA" ]; then
85+
echo "::error::Could not resolve the commit that introduced version $VERSION into packages/loopover-engine/package.json -- refusing to guess (would tag main head). (#8525)"
86+
exit 1
87+
fi
88+
if ! git merge-base --is-ancestor "$RELEASE_SHA" refs/remotes/origin/main; then
89+
echo "::error::Resolved release commit $RELEASE_SHA is not reachable from main."
90+
exit 1
91+
fi
7492
if git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then
75-
HEAD_SHA="$(git rev-parse HEAD)"
7693
TAG_SHA="$(git rev-list -n 1 "$TAG")"
77-
if [ "$TAG_SHA" != "$HEAD_SHA" ]; then
78-
echo "::error::Tag $TAG already exists but points at $TAG_SHA, not the dispatched commit $HEAD_SHA"
94+
if [ "$TAG_SHA" != "$RELEASE_SHA" ]; then
95+
echo "::error::Tag $TAG already exists but points at $TAG_SHA, not the resolved release commit $RELEASE_SHA"
7996
exit 1
8097
fi
81-
echo "Tag $TAG already exists and matches HEAD."
98+
echo "Tag $TAG already exists and matches the resolved release commit."
8299
else
83-
echo "Tag $TAG does not exist yet; the publish job will create it."
100+
echo "Tag $TAG does not exist yet; the publish job will create it at $RELEASE_SHA."
84101
fi
85-
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
86-
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
102+
# Build/typecheck/pack against the release commit's own tree, so the published artifact is
103+
# byte-for-byte the content the version number describes.
104+
git checkout --detach "$RELEASE_SHA"
105+
{
106+
echo "version=$VERSION"
107+
echo "tag=$TAG"
108+
echo "release_sha=$RELEASE_SHA"
109+
} >> "$GITHUB_OUTPUT"
87110
88111
- name: Install dependencies
89112
run: npm ci
@@ -168,16 +191,16 @@ jobs:
168191
GH_TOKEN: ${{ github.token }}
169192
TAG: ${{ needs.validate.outputs.tag }}
170193
VERSION: ${{ needs.validate.outputs.version }}
194+
RELEASE_SHA: ${{ needs.validate.outputs.release_sha }}
171195
run: |
172196
set -euo pipefail
173-
HEAD_SHA="$(git rev-parse HEAD)"
174197
if git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then
175-
echo "Tag $TAG already exists (verified against HEAD by the validate job)."
198+
echo "Tag $TAG already exists (verified against the resolved release commit by the validate job)."
176199
else
177-
echo "Creating tag $TAG at HEAD ($HEAD_SHA)."
200+
echo "Creating tag $TAG at the resolved release commit ($RELEASE_SHA) -- never at HEAD (#8525)."
178201
git config user.name "github-actions[bot]"
179202
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
180-
git tag -a "$TAG" -m "@loopover/engine v${VERSION}"
203+
git tag -a "$TAG" -m "@loopover/engine v${VERSION}" "$RELEASE_SHA"
181204
git remote set-url origin "https://github.com/${GITHUB_REPOSITORY}.git"
182205
gh auth setup-git
183206
git push origin "$TAG"

.github/workflows/publish-mcp.yml

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ jobs:
3333
outputs:
3434
version: ${{ steps.version.outputs.version }}
3535
tag: ${{ steps.version.outputs.tag }}
36+
release_sha: ${{ steps.version.outputs.release_sha }}
3637
steps:
3738
- name: Checkout
3839
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
@@ -71,19 +72,41 @@ jobs:
7172
exit 1
7273
fi
7374
TAG="mcp-v${VERSION}"
75+
# The RELEASE COMMIT is the newest main commit that introduced this exact version string
76+
# into package.json (the release PR's merge commit) -- NOT whatever main head happens to
77+
# be when this run was dispatched. A bare/reconcile dispatch used to tag-and-pack HEAD,
78+
# which silently swept any commits that landed after the version bump into the tag and the
79+
# published artifact (#8525: ui-kit-v1.1.2 got tagged two commits late during a runner
80+
# backlog, absorbing the 1.1.3 fix and zombifying its release PR). Resolving the commit
81+
# from the version string keeps the release-please-dispatched path a no-op (it dispatches
82+
# against the tag, so HEAD already IS this commit) while making late dispatches correct.
83+
RELEASE_SHA="$(git log -n 1 --format=%H -S "\"version\": \"${VERSION}\"" refs/remotes/origin/main -- packages/loopover-mcp/package.json)"
84+
if [ -z "$RELEASE_SHA" ]; then
85+
echo "::error::Could not resolve the commit that introduced version $VERSION into packages/loopover-mcp/package.json -- refusing to guess (would tag main head). (#8525)"
86+
exit 1
87+
fi
88+
if ! git merge-base --is-ancestor "$RELEASE_SHA" refs/remotes/origin/main; then
89+
echo "::error::Resolved release commit $RELEASE_SHA is not reachable from main."
90+
exit 1
91+
fi
7492
if git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then
75-
HEAD_SHA="$(git rev-parse HEAD)"
7693
TAG_SHA="$(git rev-list -n 1 "$TAG")"
77-
if [ "$TAG_SHA" != "$HEAD_SHA" ]; then
78-
echo "::error::Tag $TAG already exists but points at $TAG_SHA, not the dispatched commit $HEAD_SHA"
94+
if [ "$TAG_SHA" != "$RELEASE_SHA" ]; then
95+
echo "::error::Tag $TAG already exists but points at $TAG_SHA, not the resolved release commit $RELEASE_SHA"
7996
exit 1
8097
fi
81-
echo "Tag $TAG already exists and matches HEAD."
98+
echo "Tag $TAG already exists and matches the resolved release commit."
8299
else
83-
echo "Tag $TAG does not exist yet; the publish job will create it."
100+
echo "Tag $TAG does not exist yet; the publish job will create it at $RELEASE_SHA."
84101
fi
85-
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
86-
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
102+
# Build/typecheck/pack against the release commit's own tree, so the published artifact is
103+
# byte-for-byte the content the version number describes.
104+
git checkout --detach "$RELEASE_SHA"
105+
{
106+
echo "version=$VERSION"
107+
echo "tag=$TAG"
108+
echo "release_sha=$RELEASE_SHA"
109+
} >> "$GITHUB_OUTPUT"
87110
88111
- name: Install dependencies
89112
run: npm ci
@@ -185,16 +208,16 @@ jobs:
185208
GH_TOKEN: ${{ github.token }}
186209
TAG: ${{ needs.validate.outputs.tag }}
187210
VERSION: ${{ needs.validate.outputs.version }}
211+
RELEASE_SHA: ${{ needs.validate.outputs.release_sha }}
188212
run: |
189213
set -euo pipefail
190-
HEAD_SHA="$(git rev-parse HEAD)"
191214
if git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then
192-
echo "Tag $TAG already exists (verified against HEAD by the validate job)."
215+
echo "Tag $TAG already exists (verified against the resolved release commit by the validate job)."
193216
else
194-
echo "Creating tag $TAG at HEAD ($HEAD_SHA)."
217+
echo "Creating tag $TAG at the resolved release commit ($RELEASE_SHA) -- never at HEAD (#8525)."
195218
git config user.name "github-actions[bot]"
196219
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
197-
git tag -a "$TAG" -m "@loopover/mcp v${VERSION}"
220+
git tag -a "$TAG" -m "@loopover/mcp v${VERSION}" "$RELEASE_SHA"
198221
git remote set-url origin "https://github.com/${GITHUB_REPOSITORY}.git"
199222
gh auth setup-git
200223
git push origin "$TAG"

.github/workflows/publish-miner.yml

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ jobs:
3939
outputs:
4040
version: ${{ steps.version.outputs.version }}
4141
tag: ${{ steps.version.outputs.tag }}
42+
release_sha: ${{ steps.version.outputs.release_sha }}
4243
steps:
4344
- name: Checkout
4445
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
@@ -72,19 +73,41 @@ jobs:
7273
exit 1
7374
fi
7475
TAG="miner-v${VERSION}"
76+
# The RELEASE COMMIT is the newest main commit that introduced this exact version string
77+
# into package.json (the release PR's merge commit) -- NOT whatever main head happens to
78+
# be when this run was dispatched. A bare/reconcile dispatch used to tag-and-pack HEAD,
79+
# which silently swept any commits that landed after the version bump into the tag and the
80+
# published artifact (#8525: ui-kit-v1.1.2 got tagged two commits late during a runner
81+
# backlog, absorbing the 1.1.3 fix and zombifying its release PR). Resolving the commit
82+
# from the version string keeps the release-please-dispatched path a no-op (it dispatches
83+
# against the tag, so HEAD already IS this commit) while making late dispatches correct.
84+
RELEASE_SHA="$(git log -n 1 --format=%H -S "\"version\": \"${VERSION}\"" refs/remotes/origin/main -- packages/loopover-miner/package.json)"
85+
if [ -z "$RELEASE_SHA" ]; then
86+
echo "::error::Could not resolve the commit that introduced version $VERSION into packages/loopover-miner/package.json -- refusing to guess (would tag main head). (#8525)"
87+
exit 1
88+
fi
89+
if ! git merge-base --is-ancestor "$RELEASE_SHA" refs/remotes/origin/main; then
90+
echo "::error::Resolved release commit $RELEASE_SHA is not reachable from main."
91+
exit 1
92+
fi
7593
if git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then
76-
HEAD_SHA="$(git rev-parse HEAD)"
7794
TAG_SHA="$(git rev-list -n 1 "$TAG")"
78-
if [ "$TAG_SHA" != "$HEAD_SHA" ]; then
79-
echo "::error::Tag $TAG already exists but points at $TAG_SHA, not the dispatched commit $HEAD_SHA"
95+
if [ "$TAG_SHA" != "$RELEASE_SHA" ]; then
96+
echo "::error::Tag $TAG already exists but points at $TAG_SHA, not the resolved release commit $RELEASE_SHA"
8097
exit 1
8198
fi
82-
echo "Tag $TAG already exists and matches HEAD."
99+
echo "Tag $TAG already exists and matches the resolved release commit."
83100
else
84-
echo "Tag $TAG does not exist yet; the publish job will create it."
101+
echo "Tag $TAG does not exist yet; the publish job will create it at $RELEASE_SHA."
85102
fi
86-
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
87-
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
103+
# Build/typecheck/pack against the release commit's own tree, so the published artifact is
104+
# byte-for-byte the content the version number describes.
105+
git checkout --detach "$RELEASE_SHA"
106+
{
107+
echo "version=$VERSION"
108+
echo "tag=$TAG"
109+
echo "release_sha=$RELEASE_SHA"
110+
} >> "$GITHUB_OUTPUT"
88111
89112
- name: Install dependencies
90113
run: npm ci
@@ -177,16 +200,16 @@ jobs:
177200
GH_TOKEN: ${{ github.token }}
178201
TAG: ${{ needs.validate.outputs.tag }}
179202
VERSION: ${{ needs.validate.outputs.version }}
203+
RELEASE_SHA: ${{ needs.validate.outputs.release_sha }}
180204
run: |
181205
set -euo pipefail
182-
HEAD_SHA="$(git rev-parse HEAD)"
183206
if git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then
184-
echo "Tag $TAG already exists (verified against HEAD by the validate job)."
207+
echo "Tag $TAG already exists (verified against the resolved release commit by the validate job)."
185208
else
186-
echo "Creating tag $TAG at HEAD ($HEAD_SHA)."
209+
echo "Creating tag $TAG at the resolved release commit ($RELEASE_SHA) -- never at HEAD (#8525)."
187210
git config user.name "github-actions[bot]"
188211
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
189-
git tag -a "$TAG" -m "@loopover/miner v${VERSION}"
212+
git tag -a "$TAG" -m "@loopover/miner v${VERSION}" "$RELEASE_SHA"
190213
git remote set-url origin "https://github.com/${GITHUB_REPOSITORY}.git"
191214
gh auth setup-git
192215
git push origin "$TAG"

.github/workflows/publish-ui-kit.yml

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ jobs:
3838
outputs:
3939
version: ${{ steps.version.outputs.version }}
4040
tag: ${{ steps.version.outputs.tag }}
41+
release_sha: ${{ steps.version.outputs.release_sha }}
4142
steps:
4243
- name: Checkout
4344
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
@@ -71,19 +72,41 @@ jobs:
7172
exit 1
7273
fi
7374
TAG="ui-kit-v${VERSION}"
75+
# The RELEASE COMMIT is the newest main commit that introduced this exact version string
76+
# into package.json (the release PR's merge commit) -- NOT whatever main head happens to
77+
# be when this run was dispatched. A bare/reconcile dispatch used to tag-and-pack HEAD,
78+
# which silently swept any commits that landed after the version bump into the tag and the
79+
# published artifact (#8525: ui-kit-v1.1.2 got tagged two commits late during a runner
80+
# backlog, absorbing the 1.1.3 fix and zombifying its release PR). Resolving the commit
81+
# from the version string keeps the release-please-dispatched path a no-op (it dispatches
82+
# against the tag, so HEAD already IS this commit) while making late dispatches correct.
83+
RELEASE_SHA="$(git log -n 1 --format=%H -S "\"version\": \"${VERSION}\"" refs/remotes/origin/main -- packages/loopover-ui-kit/package.json)"
84+
if [ -z "$RELEASE_SHA" ]; then
85+
echo "::error::Could not resolve the commit that introduced version $VERSION into packages/loopover-ui-kit/package.json -- refusing to guess (would tag main head). (#8525)"
86+
exit 1
87+
fi
88+
if ! git merge-base --is-ancestor "$RELEASE_SHA" refs/remotes/origin/main; then
89+
echo "::error::Resolved release commit $RELEASE_SHA is not reachable from main."
90+
exit 1
91+
fi
7492
if git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then
75-
HEAD_SHA="$(git rev-parse HEAD)"
7693
TAG_SHA="$(git rev-list -n 1 "$TAG")"
77-
if [ "$TAG_SHA" != "$HEAD_SHA" ]; then
78-
echo "::error::Tag $TAG already exists but points at $TAG_SHA, not the dispatched commit $HEAD_SHA"
94+
if [ "$TAG_SHA" != "$RELEASE_SHA" ]; then
95+
echo "::error::Tag $TAG already exists but points at $TAG_SHA, not the resolved release commit $RELEASE_SHA"
7996
exit 1
8097
fi
81-
echo "Tag $TAG already exists and matches HEAD."
98+
echo "Tag $TAG already exists and matches the resolved release commit."
8299
else
83-
echo "Tag $TAG does not exist yet; the publish job will create it."
100+
echo "Tag $TAG does not exist yet; the publish job will create it at $RELEASE_SHA."
84101
fi
85-
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
86-
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
102+
# Build/typecheck/pack against the release commit's own tree, so the published artifact is
103+
# byte-for-byte the content the version number describes.
104+
git checkout --detach "$RELEASE_SHA"
105+
{
106+
echo "version=$VERSION"
107+
echo "tag=$TAG"
108+
echo "release_sha=$RELEASE_SHA"
109+
} >> "$GITHUB_OUTPUT"
87110
88111
- name: Install dependencies
89112
run: npm ci
@@ -167,16 +190,16 @@ jobs:
167190
GH_TOKEN: ${{ github.token }}
168191
TAG: ${{ needs.validate.outputs.tag }}
169192
VERSION: ${{ needs.validate.outputs.version }}
193+
RELEASE_SHA: ${{ needs.validate.outputs.release_sha }}
170194
run: |
171195
set -euo pipefail
172-
HEAD_SHA="$(git rev-parse HEAD)"
173196
if git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then
174-
echo "Tag $TAG already exists (verified against HEAD by the validate job)."
197+
echo "Tag $TAG already exists (verified against the resolved release commit by the validate job)."
175198
else
176-
echo "Creating tag $TAG at HEAD ($HEAD_SHA)."
199+
echo "Creating tag $TAG at the resolved release commit ($RELEASE_SHA) -- never at HEAD (#8525)."
177200
git config user.name "github-actions[bot]"
178201
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
179-
git tag -a "$TAG" -m "@loopover/ui-kit v${VERSION}"
202+
git tag -a "$TAG" -m "@loopover/ui-kit v${VERSION}" "$RELEASE_SHA"
180203
git remote set-url origin "https://github.com/${GITHUB_REPOSITORY}.git"
181204
gh auth setup-git
182205
git push origin "$TAG"

0 commit comments

Comments
 (0)