Skip to content

Commit 1ec4c1c

Browse files
anujhydrabadiclaude
andcommitted
Fix cleanup ownership check to read stage-appropriate provenance
Live e2e testing showed cleanup always skipped previews for brand-new modules. The Control Plane only populates previewGitRef on a PUBLISHED module that also has a preview sibling; a never-published module IS the preview (stage PREVIEW) and carries its owning commit in plain gitRef, with previewGitRef null — the common PR case. Cleanup now derives the owning commit per module row by stage: - stage PREVIEW -> .gitRef - stage PUBLISHED w/ preview sibling (.previewModuleId set) -> .previewGitRef - otherwise -> no preview slot, skip Prefers a PREVIEW row when both exist. Downstream SHA comparison against the PR's commits is unchanged, and absent/older provenance fields still degrade to a safe skip. README cleanup/single-preview-slot section updated to match. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 06bf5a1 commit 1ec4c1c

2 files changed

Lines changed: 46 additions & 18 deletions

File tree

module-ci-action/README.md

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,10 @@ modules live at `infra/modules/...`).
4444
- **raptor version:** the **preview** mode uses `raptor create iac-module --feature-branch`,
4545
a flag that marks a preview as **unpublishable**. It is newer than the base
4646
`iac-module` commands, so preview mode requires a `raptor` release that ships
47-
`--feature-branch` (and the `previewGitRef` field that cleanup reads). Pin
48-
`raptor_version` accordingly if `latest` ever lags. Publish and cleanup-by-metadata
49-
work on any recent `raptor`.
47+
`--feature-branch` and surfaces module git provenance (`gitRef` / `previewGitRef`) in
48+
`raptor get iac-module -o json`, which cleanup's ownership check reads. Pin
49+
`raptor_version` accordingly if `latest` ever lags. Publish works on any recent
50+
`raptor`; cleanup safely no-ops when those provenance fields are absent.
5051

5152
## Inputs
5253

@@ -116,15 +117,27 @@ stay `auto`; the action derives `preview` / `publish` / `cleanup` from each even
116117

117118
Each module (`intent/flavor/version`) has exactly **one preview slot** on the Control
118119
Plane. When a PR previews a module, the action registers the preview against the PR's
119-
**head commit** (`--git-ref`), so the slot records which commit owns it (`previewGitRef`).
120+
**head commit** (`--git-ref`), so the slot records which commit owns it.
120121

121122
- **Concurrent PRs on the same module:** last write wins. Whichever PR most recently
122123
ran preview owns the slot; an earlier PR's preview is overwritten.
123-
- **Cleanup is ownership-checked.** On PR close, the action deletes a module's preview
124-
**only if** `previewGitRef` matches one of this PR's commit SHAs. If the slot was
125-
taken over by another branch, this PR leaves it untouched — it never deletes a
126-
preview owned by someone else. (Cleanup needs `github_token` to read the PR's
127-
commits; without it, cleanup is skipped.)
124+
- **Cleanup is ownership-checked.** On PR close, the action reads each module's owning
125+
commit from `raptor get iac-module -o json` and deletes the preview **only if** that
126+
commit is one of this PR's commit SHAs. If the slot was taken over by another branch,
127+
this PR leaves it untouched — it never deletes a preview owned by someone else.
128+
(Cleanup needs `github_token` to read the PR's commits; without it, cleanup is skipped.)
129+
130+
The owning-commit field depends on the module's stage, because the Control Plane
131+
stores preview provenance in two different places:
132+
133+
- a **brand-new, never-published** module *is* its own preview (stage `PREVIEW`) —
134+
its owning commit is the row's plain `gitRef`. This is the common PR case.
135+
- a **published** module that also has a live preview sibling (its `previewModuleId`
136+
is set) exposes the preview's commit as `previewGitRef` on the `PUBLISHED` row.
137+
- anything else has no preview slot, so cleanup skips it.
138+
139+
Reading only `previewGitRef` would miss every brand-new-module preview (where it is
140+
null), so the ownership check consults `gitRef` or `previewGitRef` by stage.
128141

129142
## Behavior details
130143

module-ci-action/action.yml

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ runs:
322322
fi
323323
324324
# This PR's commit SHAs form the ownership set: a preview is only deleted
325-
# if its previewGitRef is one of these commits.
325+
# if the commit that created it is one of these commits.
326326
PR_SHAS="$(gh api "repos/${REPO}/pulls/${PR_NUMBER}/commits" --paginate --jq '.[].sha' 2>/dev/null || true)"
327327
if [ -z "$PR_SHAS" ]; then
328328
echo "::warning::Could not fetch commits for PR #${PR_NUMBER}; skipping cleanup."
@@ -346,26 +346,41 @@ runs:
346346
fi
347347
REF="${TYPE}/${FLAVOR}/${VERSION}"
348348
349-
PREVIEW_REF="$(printf '%s' "$MODULES_JSON" | jq -r \
350-
--arg t "$TYPE" --arg f "$FLAVOR" --arg v "$VERSION" \
351-
'map(select(.intent==$t and .flavor==$f and .version==$v)) | (.[0].previewGitRef // "")' 2>/dev/null || true)"
352-
if [ -z "$PREVIEW_REF" ] || [ "$PREVIEW_REF" = "null" ]; then
353-
echo "No preview slot / previewGitRef recorded for ${REF}; nothing to clean up."
349+
# Find the commit that owns this module's preview slot. The provenance
350+
# field depends on the module's stage in the list:
351+
# - a brand-new, never-published module IS the preview (stage PREVIEW);
352+
# its owning commit is the row's own .gitRef.
353+
# - a PUBLISHED module with a preview sibling (.previewModuleId set)
354+
# exposes the preview's commit as .previewGitRef.
355+
# - anything else has no preview slot -> skip.
356+
# Prefer a PREVIEW row when both exist (its .gitRef is the preview's own
357+
# provenance). Absent/older fields yield "" and are treated as "no slot".
358+
OWNER_SHA="$(printf '%s' "$MODULES_JSON" | jq -r \
359+
--arg t "$TYPE" --arg f "$FLAVOR" --arg v "$VERSION" '
360+
map(select(.intent==$t and .flavor==$f and .version==$v)) as $rows
361+
| ($rows | map(select(.stage=="PREVIEW"))[0]) as $preview
362+
| ($rows | map(select(.stage=="PUBLISHED" and ((.previewModuleId // "") != "")))[0]) as $published
363+
| if $preview then ($preview.gitRef // "")
364+
elif $published then ($published.previewGitRef // "")
365+
else "" end
366+
' 2>/dev/null || true)"
367+
if [ -z "$OWNER_SHA" ] || [ "$OWNER_SHA" = "null" ]; then
368+
echo "No preview slot recorded for ${REF}; nothing to clean up."
354369
echo "::endgroup::"; continue
355370
fi
356371
357372
OWNED="false"
358373
while IFS= read -r sha; do
359374
[ -z "$sha" ] && continue
360-
if [ "$sha" = "$PREVIEW_REF" ]; then OWNED="true"; break; fi
375+
if [ "$sha" = "$OWNER_SHA" ]; then OWNED="true"; break; fi
361376
done <<< "$PR_SHAS"
362377
363378
if [ "$OWNED" != "true" ]; then
364-
echo "Preview for ${REF} is owned by another branch (previewGitRef=${PREVIEW_REF}); leaving it untouched."
379+
echo "Preview for ${REF} is owned by another branch (owning commit=${OWNER_SHA}); leaving it untouched."
365380
echo "::endgroup::"; continue
366381
fi
367382
368-
echo "Deleting preview for ${REF} (owned by this PR, previewGitRef=${PREVIEW_REF})..."
383+
echo "Deleting preview for ${REF} (owned by this PR, owning commit=${OWNER_SHA})..."
369384
if ! raptor delete iac-module "$REF" --yes --force; then
370385
echo "::error::Failed to delete preview for ${REF}"
371386
echo "${REF} (cleanup)" >> "$FAILURES"

0 commit comments

Comments
 (0)