Skip to content

Commit 1f96e3e

Browse files
Merge pull request #2 from ourPLCC/fix/release-check-pr-creation
ci: make the release-check workflow parseable and non-wedging
2 parents 4b5d4c8 + d96d0eb commit 1f96e3e

2 files changed

Lines changed: 98 additions & 26 deletions

File tree

.github/workflows/check-plcc-release.yml

Lines changed: 69 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -39,40 +39,83 @@ jobs:
3939
4040
BRANCH="chore/update-plcc-${LATEST}"
4141
42-
# Check whether a PR branch already exists (idempotency guard)
43-
if git ls-remote --exit-code --heads origin "$BRANCH" > /dev/null 2>&1; then
44-
echo "Branch '$BRANCH' already exists. PR already open."
42+
# The image's major tag is the stability contract for courses, so a
43+
# new PLCC major has to release the image as a major too. Otherwise a
44+
# patch release would move :N onto a new major of the tool. Tags are
45+
# v-prefixed, so strip it before comparing.
46+
# Deliberately `feat:` and not `feat!:` — the Angular preset that
47+
# semantic-release uses does not parse the `!` shorthand, so a
48+
# `feat!:` subject whose BREAKING CHANGE footer went missing yields
49+
# no release at all. `feat:` degrades to a minor instead.
50+
if [ "$(echo "${LATEST#v}" | cut -d. -f1)" != "$(echo "${CURRENT#v}" | cut -d. -f1)" ]; then
51+
COMMIT_SUBJECT="feat: update PLCC to ${LATEST}"
52+
COMMIT_BODY="BREAKING CHANGE: PLCC ${LATEST} is a new major version (was ${CURRENT}). Images already published under the previous major tag keep PLCC ${CURRENT}; this release publishes under a new major tag."
53+
else
54+
COMMIT_SUBJECT="fix: update PLCC to ${LATEST}"
55+
COMMIT_BODY=""
56+
fi
57+
echo "Release type: $COMMIT_SUBJECT"
58+
59+
# Idempotency guard: keyed on the PR, not the branch. A branch with no
60+
# PR means an earlier run died between push and PR creation; that must
61+
# be recoverable, not mistaken for work already done.
62+
if [ -n "$(gh pr list --head "$BRANCH" --state all --json number --jq '.[].number')" ]; then
63+
echo "A PR for '$BRANCH' already exists. No action needed."
4564
exit 0
4665
fi
4766
48-
# Create update branch
49-
git config user.name "github-actions[bot]"
50-
git config user.email "github-actions[bot]@users.noreply.github.com"
51-
git checkout -b "$BRANCH"
67+
if git ls-remote --exit-code --heads origin "$BRANCH" > /dev/null 2>&1; then
68+
echo "Branch '$BRANCH' exists with no PR. Recovering by opening the PR."
69+
# actions/checkout configures a single-branch refspec, so no
70+
# origin/$BRANCH tracking ref exists here — use FETCH_HEAD.
71+
git fetch --depth=1 origin "$BRANCH"
72+
git checkout -B "$BRANCH" FETCH_HEAD
73+
else
74+
git config user.name "github-actions[bot]"
75+
git config user.email "github-actions[bot]@users.noreply.github.com"
76+
git checkout -b "$BRANCH"
5277
53-
# Update the pinned PLCC version in devcontainer.json
54-
jq --arg v "$LATEST" \
55-
'.features["./features/plcc"].version = $v' \
56-
.devcontainer/devcontainer.json > tmp.json
57-
mv tmp.json .devcontainer/devcontainer.json
78+
# Update the pinned PLCC version in devcontainer.json
79+
jq --arg v "$LATEST" \
80+
'.features["./features/plcc"].version = $v' \
81+
.devcontainer/devcontainer.json > tmp.json
82+
mv tmp.json .devcontainer/devcontainer.json
5883
59-
git add .devcontainer/devcontainer.json
60-
git commit -m "fix: update PLCC to ${LATEST}"
84+
git add .devcontainer/devcontainer.json
85+
if [ -n "$COMMIT_BODY" ]; then
86+
git commit -m "$COMMIT_SUBJECT" -m "$COMMIT_BODY"
87+
else
88+
git commit -m "$COMMIT_SUBJECT"
89+
fi
6190
62-
# Push branch; treat push failure as no-op (race condition guard)
63-
git push origin "$BRANCH" || {
64-
echo "Push failed — branch may already exist. Exiting cleanly."
65-
exit 0
66-
}
91+
# Push branch; treat push failure as no-op (race condition guard)
92+
git push origin "$BRANCH" || {
93+
echo "Push failed — concurrent run. Exiting cleanly."
94+
exit 0
95+
}
96+
fi
6797
68-
# Open PR
69-
gh pr create \
70-
--title "fix: update PLCC to ${LATEST}" \
71-
--label "automated" \
98+
# The PR title becomes the squash-merge commit subject, so it carries
99+
# the release type. The body carries the BREAKING CHANGE footer for
100+
# the same reason — on a major, both are what semantic-release reads.
101+
# Every line stays indented to the run block. A line at column 0 ends
102+
# the YAML block scalar, which is what made this file unparseable and
103+
# kept the workflow from ever running. YAML strips this indentation,
104+
# so the body text itself is unaffected.
105+
PR_URL=$(gh pr create \
106+
--title "$COMMIT_SUBJECT" \
72107
--body "Automated update: PLCC has a new release (${LATEST}).
73108
74-
CI will build and test the image on this PR. Merge if green — merging triggers the release workflow, which publishes a new versioned image automatically.
109+
CI will build and test the image on this PR. Merge if green — merging triggers the release workflow, which publishes a new versioned image automatically.
110+
111+
PLCC release: https://github.com/ourPLCC/plcc/releases/tag/${LATEST}
75112
76-
PLCC release: https://github.com/ourPLCC/plcc/releases/tag/${LATEST}" \
113+
${COMMIT_BODY}" \
77114
--base main \
78-
--head "$BRANCH"
115+
--head "$BRANCH")
116+
117+
echo "Opened $PR_URL"
118+
119+
# Cosmetic only — never fail the run over a missing label or scope.
120+
gh pr edit "$PR_URL" --add-label automated \
121+
|| echo "::warning::Could not apply the 'automated' label to $PR_URL"

.github/workflows/ci.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,32 @@ jobs:
6666
repo: context.repo.repo,
6767
body: `🐳 **PR image built:** \`ghcr.io/ourplcc/plcc-devcontainer:pr-${{ github.event.pull_request.number }}\`\n\nTo test manually, update \`.devcontainer/devcontainer.json\` to use this image tag.`
6868
})
69+
70+
# The single check the ruleset should require.
71+
#
72+
# The ruleset previously required "CI" — this workflow's top-level name.
73+
# Actions posts one check per job, never one named after the workflow, so
74+
# that check was never reported and every PR hung on "Expected — waiting
75+
# for status to be reported" while all real checks were green. Requiring
76+
# `build` would fix it today but break again the moment that job gains a
77+
# matrix and its check becomes `build (x)`. This name never changes.
78+
ci-gate:
79+
needs: [build]
80+
# Must run even when build fails — without this the gate is itself
81+
# skipped on failure, reports nothing, and reproduces the same hang.
82+
if: always()
83+
runs-on: ubuntu-latest
84+
permissions: {}
85+
steps:
86+
- name: Require all CI jobs to have succeeded
87+
env:
88+
BUILD: ${{ needs.build.result }}
89+
run: |
90+
echo "build=$BUILD"
91+
# Anything other than success — failure, cancelled, or skipped —
92+
# must block the merge.
93+
if [ "$BUILD" != "success" ]; then
94+
echo "::error::CI did not fully succeed — build was '$BUILD'."
95+
exit 1
96+
fi
97+
echo "All required CI jobs succeeded."

0 commit comments

Comments
 (0)