Skip to content

Commit f80963d

Browse files
committed
ci: gate releases on green main ci
1 parent a27c47f commit f80963d

2 files changed

Lines changed: 71 additions & 5 deletions

File tree

.github/workflows/release.yml

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
name: release
22

33
on:
4-
push:
4+
# Release only after the CI workflow succeeds on main so Docker publishes
5+
# are tied to a CI-validated commit instead of any direct branch push.
6+
workflow_run:
7+
workflows:
8+
- ci
9+
types:
10+
- completed
511
branches:
612
- main
713
workflow_dispatch:
@@ -10,11 +16,59 @@ permissions:
1016
contents: read
1117

1218
concurrency:
13-
group: release-${{ github.ref }}
19+
group: release-${{ github.event.workflow_run.head_sha || github.sha }}
1420
cancel-in-progress: true
1521

1622
jobs:
23+
guard:
24+
runs-on: ubuntu-latest
25+
outputs:
26+
target_sha: ${{ steps.resolve.outputs.target_sha }}
27+
target_ref: ${{ steps.resolve.outputs.target_ref }}
28+
steps:
29+
- name: Validate release trigger and resolve target
30+
id: resolve
31+
env:
32+
EVENT_NAME: ${{ github.event_name }}
33+
WORKFLOW_CONCLUSION: ${{ github.event.workflow_run.conclusion }}
34+
WORKFLOW_HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
35+
WORKFLOW_HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }}
36+
GITHUB_REF_VALUE: ${{ github.ref }}
37+
GITHUB_SHA_VALUE: ${{ github.sha }}
38+
run: |
39+
if [ "$EVENT_NAME" = "workflow_run" ]; then
40+
if [ "$WORKFLOW_CONCLUSION" != "success" ]; then
41+
echo "Release requires successful CI on main; got conclusion=$WORKFLOW_CONCLUSION" >&2
42+
exit 1
43+
fi
44+
45+
if [ -z "$WORKFLOW_HEAD_SHA" ] || [ -z "$WORKFLOW_HEAD_BRANCH" ]; then
46+
echo "workflow_run payload missing head SHA or branch" >&2
47+
exit 1
48+
fi
49+
50+
echo "target_sha=$WORKFLOW_HEAD_SHA" >> "$GITHUB_OUTPUT"
51+
echo "target_ref=refs/heads/$WORKFLOW_HEAD_BRANCH" >> "$GITHUB_OUTPUT"
52+
exit 0
53+
fi
54+
55+
if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
56+
if [ "$GITHUB_REF_VALUE" != "refs/heads/main" ]; then
57+
echo "Manual release is restricted to refs/heads/main; got $GITHUB_REF_VALUE" >&2
58+
exit 1
59+
fi
60+
61+
echo "target_sha=$GITHUB_SHA_VALUE" >> "$GITHUB_OUTPUT"
62+
echo "target_ref=$GITHUB_REF_VALUE" >> "$GITHUB_OUTPUT"
63+
exit 0
64+
fi
65+
66+
echo "Unsupported event: $EVENT_NAME" >&2
67+
exit 1
68+
1769
release:
70+
needs:
71+
- guard
1872
runs-on: ubuntu-latest
1973
permissions:
2074
contents: write
@@ -25,6 +79,7 @@ jobs:
2579
steps:
2680
- uses: actions/checkout@v6
2781
with:
82+
ref: ${{ needs.guard.outputs.target_sha }}
2883
fetch-depth: 0
2984

3085
- name: Run release-please
@@ -54,15 +109,17 @@ jobs:
54109
docker-publish:
55110
if: needs.release.outputs.release_created == 'true'
56111
needs:
112+
- guard
57113
- release
58114
runs-on: ubuntu-latest
59115
env:
60116
IMAGE_NAME: html2rss/web
61-
TAG_SHA: ${{ github.sha }}
117+
TAG_SHA: ${{ needs.guard.outputs.target_sha }}
62118
RELEASE_TAG: ${{ needs.release.outputs.tag_name }}
63119
steps:
64120
- uses: actions/checkout@v6
65121
with:
122+
ref: ${{ needs.guard.outputs.target_sha }}
66123
fetch-depth: 0
67124

68125
- name: Setup pnpm
@@ -135,7 +192,7 @@ jobs:
135192
tags: ${{ steps.tags.outputs.tags }}
136193
build-args: |
137194
BUILD_TAG=${{ env.RELEASE_VERSION }}
138-
GIT_SHA=${{ github.sha }}
195+
GIT_SHA=${{ needs.guard.outputs.target_sha }}
139196
platforms: linux/amd64,linux/arm64
140197
cache-from: type=local,src=/tmp/.buildx-cache
141198
cache-to: type=local,dest=/tmp/.buildx-cache-new
@@ -145,7 +202,7 @@ jobs:
145202
org.opencontainers.image.created=${{ env.TIMESTAMP_ISO }}
146203
org.opencontainers.image.description=Generates RSS feeds of any website & serves to the web!
147204
org.opencontainers.image.ref.name=${{ env.RELEASE_TAG }}
148-
org.opencontainers.image.revision=${{ github.sha }}
205+
org.opencontainers.image.revision=${{ needs.guard.outputs.target_sha }}
149206
org.opencontainers.image.source=https://github.com/${{ github.repository }}
150207
org.opencontainers.image.title=html2rss-web
151208
org.opencontainers.image.url=https://github.com/${{ github.repository }}/releases/tag/${{ env.RELEASE_TAG }}

docs/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,15 @@ make ready
9595

9696
---
9797

98+
## Release Automation
99+
100+
- Official releases run only after the `ci` GitHub Actions workflow completes successfully for a commit on `main`.
101+
- Manual `release` workflow dispatch is an emergency/manual replay path and is restricted to `main`.
102+
- Docker publish uses the exact CI-validated commit SHA for release metadata, OCI labels, and `BUILD_TAG` / `GIT_SHA` wiring.
103+
- Branch protection on `main` must continue to require the `ci` workflow even though the release workflow also gates on successful CI.
104+
105+
---
106+
98107
## Backend Structure Rules
99108

100109
- `app/` is the Zeitwerk root for `Html2rss`.

0 commit comments

Comments
 (0)