Skip to content

Commit 5c25458

Browse files
authored
ci: bootstrap of release strategy change from rolling to versioned (#932)
This pull request introduces automated release management and versioning for the project, including configuration for release-please, Docker image publishing, and artifact verification. It establishes a formal release process tied to CI validation, adds a canonical version constant, and ensures generated artifacts are kept up-to-date. The most important changes are grouped below: **Release Automation and Versioning:** * Added `.github/workflows/release.yml` to automate releases using release-please, ensuring releases only occur after successful CI on `main`, and Docker images are published with proper metadata and versioning. * Introduced `.github/release-please-config.json` and `.github/.release-please-manifest.json` to configure and track release-please-managed versions, and added `config/version.rb` to define the canonical application version (`Html2rss::Web::VERSION`). [[1]](diffhunk://#diff-b4c6ab25197e992b430b71d65743b6459f06f248f7c6b3364f1942e40d9f0039R1-R10) [[2]](diffhunk://#diff-c25c6d113b8a952cdb94e0acf1a98b51e02c56431b2098a6a2c1b14205aa44b8R1-R3) [[3]](diffhunk://#diff-598844aa3164dd021340e7ec8d71eee09bacc31f5c18d23bd2cdc7ac318e8fa7R1-R8) * Added `CHANGELOG.md` to document notable changes in each release. **CI/CD Workflow Updates:** * Removed the Docker publish job from `.github/workflows/ci.yml` and replaced it with a more robust, release-gated Docker publishing process in the new release workflow. [[1]](diffhunk://#diff-b803fcb7f17ed9235f1e5cb1fcd2f5d3b2838429d4368ae4c57ce4436577f03fL175-L267) [[2]](diffhunk://#diff-87db21a973eed4fef5f32b267aa60fcee5cbdf03c67fafdc2a9b553bb0b15f34R1-R204) * Added `.github/workflows/release_artifacts.yml` to verify and refresh generated OpenAPI artifacts on release branches and pull requests, ensuring generated files are always current. **Build and Metadata Improvements:** * Updated `Dockerfile` to accept and propagate `BUILD_TAG` and `GIT_SHA` as build arguments for improved traceability in image metadata. **Testing and Documentation:** * Added `spec/html2rss/web_spec.rb` to test that the canonical version constant is defined and correctly formatted. * Updated OpenAPI spec support in `spec/support/openapi.rb` to use the version constant and improved code formatting. [[1]](diffhunk://#diff-c83c0549588e83179992e502625e9ba7785ee103f07f78f221243984bb059574L3-R9) [[2]](diffhunk://#diff-c83c0549588e83179992e502625e9ba7785ee103f07f78f221243984bb059574L45-R46) [[3]](diffhunk://#diff-c83c0549588e83179992e502625e9ba7785ee103f07f78f221243984bb059574R208) * Documented the release automation process in `docs/README.md`.
1 parent 8b0b39b commit 5c25458

11 files changed

Lines changed: 540 additions & 269 deletions

File tree

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
".": "1.0.0"
3+
}

.github/release-please-config.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"packages": {
3+
".": {
4+
"release-type": "ruby",
5+
"package-name": "html2rss-web",
6+
"version-file": "config/version.rb",
7+
"changelog-path": "CHANGELOG.md"
8+
}
9+
}
10+
}

.github/workflows/ci.yml

Lines changed: 0 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -172,96 +172,3 @@ jobs:
172172
DOCKER_SMOKE_SKIP_BUILD: "true"
173173
SMOKE_AUTO_SOURCE_ENABLED: ${{ matrix.smoke_auto_source_enabled }}
174174
run: bundle exec rake
175-
176-
docker-publish:
177-
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
178-
needs:
179-
- docker-test
180-
runs-on: ubuntu-latest
181-
permissions:
182-
contents: read
183-
packages: write
184-
env:
185-
IMAGE_NAME: html2rss/web
186-
TAG_SHA: ${{ github.sha }}
187-
steps:
188-
- name: Checkout code
189-
uses: actions/checkout@v6
190-
191-
- name: Setup pnpm
192-
uses: pnpm/action-setup@v6
193-
with:
194-
cache: true
195-
cache_dependency_path: frontend/pnpm-lock.yaml
196-
package_json_file: frontend/package.json
197-
198-
- name: Setup Node.js for Docker build
199-
uses: actions/setup-node@v6
200-
with:
201-
node-version-file: ".tool-versions"
202-
203-
- name: Install frontend dependencies
204-
run: pnpm install --frozen-lockfile
205-
working-directory: frontend
206-
207-
- name: Build frontend static assets
208-
run: pnpm run build
209-
working-directory: frontend
210-
211-
- name: Set up QEMU
212-
uses: docker/setup-qemu-action@v4
213-
214-
- name: Set up Docker Buildx
215-
uses: docker/setup-buildx-action@v4
216-
217-
- name: Get Git commit timestamps
218-
run: echo "TIMESTAMP=$(git log -1 --pretty=%ct)" >> $GITHUB_ENV
219-
220-
- name: Extract metadata
221-
id: meta
222-
uses: docker/metadata-action@v6
223-
with:
224-
images: ${{ env.IMAGE_NAME }}
225-
226-
- name: Log in to DockerHub
227-
uses: docker/login-action@v4
228-
with:
229-
username: ${{ secrets.DOCKER_USERNAME }}
230-
password: ${{ secrets.DOCKER_PASSWORD }}
231-
232-
- name: Cache Docker layers
233-
uses: actions/cache@v5
234-
with:
235-
path: /tmp/.buildx-cache
236-
key: ${{ runner.os }}-buildx-${{ github.sha }}
237-
restore-keys: |
238-
${{ runner.os }}-buildx-
239-
240-
- name: Build and push Docker image
241-
uses: docker/build-push-action@v7
242-
env:
243-
SOURCE_DATE_EPOCH: ${{ env.TIMESTAMP }}
244-
with:
245-
context: .
246-
push: true
247-
tags: |
248-
html2rss/web:latest
249-
html2rss/web:${{ github.sha }}
250-
${{ steps.meta.outputs.tags }}
251-
platforms: linux/amd64,linux/arm64
252-
cache-from: type=local,src=/tmp/.buildx-cache
253-
cache-to: type=local,dest=/tmp/.buildx-cache-new
254-
provenance: true
255-
sbom: true
256-
labels: |
257-
org.opencontainers.image.source=https://github.com/${{ github.repository }}
258-
org.opencontainers.image.created=${{ github.event.head_commit.timestamp }}
259-
org.opencontainers.image.revision=${{ github.sha }}
260-
org.opencontainers.image.title=html2rss-web
261-
org.opencontainers.image.description=Generates RSS feeds of any website & serves to the web!
262-
org.opencontainers.image.sbom=https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts
263-
264-
- name: Move updated cache into place
265-
run: |
266-
rm -rf /tmp/.buildx-cache
267-
mv /tmp/.buildx-cache-new /tmp/.buildx-cache

.github/workflows/release.yml

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
name: release
2+
3+
on:
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
11+
branches:
12+
- main
13+
workflow_dispatch:
14+
15+
permissions:
16+
contents: read
17+
18+
concurrency:
19+
group: release-${{ github.event.workflow_run.head_sha || github.sha }}
20+
cancel-in-progress: true
21+
22+
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+
69+
release:
70+
needs:
71+
- guard
72+
runs-on: ubuntu-latest
73+
permissions:
74+
contents: write
75+
pull-requests: write
76+
outputs:
77+
release_created: ${{ steps.release.outputs.release_created }}
78+
tag_name: ${{ steps.release.outputs.tag_name }}
79+
steps:
80+
- uses: actions/checkout@v6
81+
with:
82+
ref: ${{ needs.guard.outputs.target_sha }}
83+
fetch-depth: 0
84+
85+
- name: Run release-please
86+
id: release
87+
uses: googleapis/release-please-action@v4
88+
with:
89+
token: ${{ secrets.RELEASE_PLEASE_TOKEN || github.token }}
90+
config-file: .github/release-please-config.json
91+
manifest-file: .github/.release-please-manifest.json
92+
93+
- name: Summarize release outcome
94+
env:
95+
RELEASE_CREATED: ${{ steps.release.outputs.release_created }}
96+
RELEASE_TAG: ${{ steps.release.outputs.tag_name }}
97+
run: |
98+
{
99+
echo "## Release outcome"
100+
echo
101+
echo "- Release created: ${RELEASE_CREATED:-false}"
102+
if [ -n "${RELEASE_TAG}" ]; then
103+
echo "- Release tag: ${RELEASE_TAG}"
104+
else
105+
echo "- Release tag: none"
106+
fi
107+
} >> "$GITHUB_STEP_SUMMARY"
108+
109+
docker-publish:
110+
if: needs.release.outputs.release_created == 'true'
111+
needs:
112+
- guard
113+
- release
114+
runs-on: ubuntu-latest
115+
env:
116+
IMAGE_NAME: html2rss/web
117+
TAG_SHA: ${{ needs.guard.outputs.target_sha }}
118+
RELEASE_TAG: ${{ needs.release.outputs.tag_name }}
119+
steps:
120+
- uses: actions/checkout@v6
121+
with:
122+
ref: ${{ needs.guard.outputs.target_sha }}
123+
fetch-depth: 0
124+
125+
- name: Set up QEMU
126+
uses: docker/setup-qemu-action@v4
127+
128+
- name: Set up Docker Buildx
129+
uses: docker/setup-buildx-action@v4
130+
131+
- name: Get Git commit timestamp
132+
run: |
133+
echo "TIMESTAMP_EPOCH=$(git log -1 --format=%ct)" >> "$GITHUB_ENV"
134+
echo "TIMESTAMP_ISO=$(git log -1 --format=%cI)" >> "$GITHUB_ENV"
135+
136+
- name: Compute Docker tags
137+
id: tags
138+
run: |
139+
release_version="${RELEASE_TAG#v}"
140+
echo "RELEASE_VERSION=${release_version}" >> "$GITHUB_ENV"
141+
major="${release_version%%.*}"
142+
{
143+
echo "tags<<EOF"
144+
echo "${IMAGE_NAME}:${release_version}"
145+
echo "${IMAGE_NAME}:${major}"
146+
echo "${IMAGE_NAME}:latest"
147+
echo "${IMAGE_NAME}:${TAG_SHA}"
148+
echo "EOF"
149+
} >> "$GITHUB_OUTPUT"
150+
151+
- name: Log in to DockerHub
152+
uses: docker/login-action@v4
153+
with:
154+
username: ${{ secrets.DOCKER_USERNAME }}
155+
password: ${{ secrets.DOCKER_PASSWORD }}
156+
157+
- name: Cache Docker layers
158+
uses: actions/cache@v4
159+
with:
160+
path: /tmp/.buildx-cache
161+
key: ${{ runner.os }}-buildx-${{ github.sha }}
162+
restore-keys: |
163+
${{ runner.os }}-buildx-
164+
165+
- name: Build and push Docker image
166+
uses: docker/build-push-action@v7
167+
env:
168+
SOURCE_DATE_EPOCH: ${{ env.TIMESTAMP_EPOCH }}
169+
with:
170+
context: .
171+
push: true
172+
tags: ${{ steps.tags.outputs.tags }}
173+
build-args: |
174+
BUILD_TAG=${{ env.RELEASE_VERSION }}
175+
GIT_SHA=${{ needs.guard.outputs.target_sha }}
176+
platforms: linux/amd64,linux/arm64
177+
cache-from: type=local,src=/tmp/.buildx-cache
178+
cache-to: type=local,dest=/tmp/.buildx-cache-new
179+
provenance: true
180+
sbom: true
181+
labels: |
182+
org.opencontainers.image.created=${{ env.TIMESTAMP_ISO }}
183+
org.opencontainers.image.description=Generates RSS feeds of any website & serves to the web!
184+
org.opencontainers.image.ref.name=${{ env.RELEASE_TAG }}
185+
org.opencontainers.image.revision=${{ needs.guard.outputs.target_sha }}
186+
org.opencontainers.image.source=https://github.com/${{ github.repository }}
187+
org.opencontainers.image.title=html2rss-web
188+
org.opencontainers.image.url=https://github.com/${{ github.repository }}/releases/tag/${{ env.RELEASE_TAG }}
189+
org.opencontainers.image.version=${{ env.RELEASE_VERSION }}
190+
191+
- name: Move updated cache into place
192+
run: |
193+
rm -rf /tmp/.buildx-cache
194+
mv /tmp/.buildx-cache-new /tmp/.buildx-cache
195+
196+
- name: Summarize published image tags
197+
run: |
198+
{
199+
echo "## Docker publish"
200+
echo
201+
echo "- Release tag: ${RELEASE_TAG}"
202+
echo "- Docker tags pushed:"
203+
echo "${{ steps.tags.outputs.tags }}" | sed 's/^/ - /'
204+
} >> "$GITHUB_STEP_SUMMARY"

0 commit comments

Comments
 (0)