Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 19 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -234,8 +234,8 @@ jobs:

notify-approval-needed:
name: Notify Slack - Approval Needed
needs: [check-changesets, prepare-release-candidate, verify-release-candidate]
if: needs.check-changesets.outputs.has-changesets == 'true' && needs.prepare-release-candidate.result == 'success' && needs.verify-release-candidate.result == 'success'
needs: [check-changesets, prepare-release-candidate, verify-release-candidate, resume-check]
if: always() && !cancelled() && ((needs.check-changesets.outputs.has-changesets == 'true' && needs.prepare-release-candidate.result == 'success' && needs.verify-release-candidate.result == 'success') || (needs.resume-check.result == 'success' && needs.resume-check.outputs.slack-ts == ''))
uses: posthog/.github/.github/workflows/notify-approval-needed.yml@5ba5d24966f2c416ca792654638e4ce6f19f545b # main
with:
slack_channel_id: ${{ vars.SLACK_APPROVALS_CLIENT_LIBRARIES_CHANNEL_ID }}
Expand Down Expand Up @@ -602,6 +602,16 @@ jobs:
RESUME_DISTRIBUTION_COMMIT=$(gh api "repos/$DISTRIBUTION_REPOSITORY/git/ref/heads/main" --jq '.object.sha')
export RESUME_DISTRIBUTION_COMMIT
bash scripts/check-release-refs.sh
- name: Notify Slack - Resume approval needed
if: steps.thread.outputs.slack_ts != ''
continue-on-error: true
uses: posthog/.github/.github/actions/slack-thread-reply@cb0979b67dcd585828b61ac45927eef4da8f6287 # main
with:
slack_bot_token: ${{ secrets.SLACK_CLIENT_LIBRARIES_BOT_TOKEN }}
slack_channel_id: ${{ vars.SLACK_APPROVALS_CLIENT_LIBRARIES_CHANNEL_ID }}
thread_ts: ${{ steps.thread.outputs.slack_ts }}
emoji_reaction: hourglass_flowing_sand
message: "<!subteam^${{ vars.GROUP_CLIENT_LIBRARIES_SLACK_GROUP_ID }}|@support-hero-client-libraries> Resume approval needed: <https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}|run ${{ github.run_id }}> completes ${{ steps.candidate.outputs.core_changed == 'true' && format('PHP core {0} and ', steps.candidate.outputs.core_version) || '' }}Laravel ${{ steps.candidate.outputs.version }} from run ${{ inputs.resume_run_id }}. Please review and approve the resume."
- name: Send failure event to PostHog
if: ${{ failure() }}
continue-on-error: true
Expand All @@ -626,21 +636,25 @@ jobs:

resume-publication:
name: Resume publication
needs: resume-check
needs: [resume-check, notify-approval-needed]
if: always() && !cancelled() && needs.resume-check.result == 'success' && (needs.notify-approval-needed.result == 'success' || needs.notify-approval-needed.result == 'skipped')
runs-on: ubuntu-latest
environment: Release
env:
SLACK_TS: ${{ needs.resume-check.outputs.slack-ts || needs.notify-approval-needed.outputs.slack_ts }}
permissions:
actions: read
contents: read
steps:
- name: Notify Slack - Approved
if: needs.resume-check.outputs.slack-ts != ''
if: env.SLACK_TS != ''
continue-on-error: true
uses: posthog/.github/.github/actions/slack-thread-reply@cb0979b67dcd585828b61ac45927eef4da8f6287 # main
with:
slack_bot_token: ${{ secrets.SLACK_CLIENT_LIBRARIES_BOT_TOKEN }}
slack_channel_id: ${{ vars.SLACK_APPROVALS_CLIENT_LIBRARIES_CHANNEL_ID }}
thread_ts: ${{ needs.resume-check.outputs.slack-ts }}
thread_ts: ${{ env.SLACK_TS }}
remove_emoji_reaction: hourglass_flowing_sand
message: "✅ Resume approved! Completing the missing publication..."
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
Expand Down
6 changes: 4 additions & 2 deletions RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,16 +138,18 @@ Rerunning the failed publishing job after the source version commit fails its or
gh workflow run release.yml --ref main -f resume_run_id=<failed-release-run-id>
```

The resume skips change-intent checks, preparation, verification, and the approval notification. Before approval, a read-only job checks:
The resume skips change-intent checks, preparation, and verification. Before approval, a read-only job checks:

- The run is a completed **Release** run of this repository on `main`, and its `release-candidate-<source>` artifact is still available.
- `release.patch` and `package.tar.gz` match the digests in `candidate.json`, and `main` still carries the candidate versions. A later release that consumed them makes the run not resumable.
- The shared version commit is the only first-parent child of the approved source on `main`. Its tree is exactly the source plus `release.patch`, and GitHub verifies its signature from a GitHub App.
- Every planned tag is either unused or a lightweight tag on the expected commit, and every existing release for it is published. The legacy `vX.Y.Z` distribution tag must stay unused.

When these checks pass, the same job replies in the original approval Slack thread with a link to the resume run, as long as the thread artifact is still available. Otherwise the workflow posts the normal approval message to the release approvals channel, and later replies land under it.

A run that failed before the shared version commit is not resumable: its change intents are still pending, so dispatch **Release** without `resume_run_id` instead.

After approval, the publishing job rechecks these, fails if `main` moved since the dispatch, and requires the releaser App to be the version commit author. It then completes only what is missing, in the normal order and with the same token handling: the core release, the Packagist wait, the distribution commit (reused when it already matches the approved archive), the distribution release, and the source release with its attachments. Existing releases and tags are only verified, never recreated, moved, or deleted. The released message replies in the original approval thread when the resumed run preserved it, and posts a new channel message otherwise. A failed resume is reported by the release failure notification workflow.
After approval, the publishing job rechecks these, fails if `main` moved since the dispatch, and requires the releaser App to be the version commit author. It then completes only what is missing, in the normal order and with the same token handling: the core release, the Packagist wait, the distribution commit (reused when it already matches the approved archive), the distribution release, and the source release with its attachments. Existing releases and tags are only verified, never recreated, moved, or deleted. The approved, released, and failure messages reply in that same thread when there is one. Without a thread, the released and failure messages post to the channel instead. A failed resume is reported by the release failure notification workflow.

The candidate artifact is the only approved input, and it is retained for five days. After it expires, a resume is refused. In that case, reproduce the candidate from the approved source commit with `release.py prepare` (see [Preview and test](#preview-and-test)). Confirm that its `release.patch` gives exactly the shared version commit's tree, and that its archive matches any existing signed distribution commit byte-for-byte. Inspect the signatures and existing tags/releases, then complete only the missing publication at the **same recorded version** manually. Wait for Packagist indexing rather than creating another release intent. An all-workflow rerun after versions were consumed does not create a new candidate automatically.

Expand Down
Loading