Skip to content

Commit 749c9bf

Browse files
authored
Merge pull request #128 from beNative/codex/create-automated-git-tag-release-workflow
Automate tagged releases with multi-platform builds
2 parents 8fe97ce + a5e53c3 commit 749c9bf

2 files changed

Lines changed: 480 additions & 0 deletions

File tree

.github/workflows/release.yml

Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*'
7+
8+
permissions:
9+
contents: write
10+
packages: read
11+
12+
concurrency:
13+
group: release-${{ github.ref }}
14+
cancel-in-progress: false
15+
16+
jobs:
17+
validate:
18+
name: Validate tag & version
19+
runs-on: ubuntu-latest
20+
outputs:
21+
version: ${{ steps.package.outputs.version }}
22+
tag: ${{ steps.metadata.outputs.tag }}
23+
steps:
24+
- name: Checkout repository
25+
uses: actions/checkout@v4
26+
with:
27+
fetch-depth: 0
28+
29+
- name: Determine package version
30+
id: package
31+
run: |
32+
set -euo pipefail
33+
VERSION=$(node -p "require('./package.json').version")
34+
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
35+
echo "Package version: ${VERSION}"
36+
37+
- name: Capture tag metadata
38+
id: metadata
39+
run: |
40+
set -euo pipefail
41+
if [[ "${GITHUB_REF_TYPE}" != "tag" ]]; then
42+
echo "::error::The release workflow must be triggered by a tag." >&2
43+
exit 1
44+
fi
45+
echo "tag=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT"
46+
echo "Tag name: ${GITHUB_REF_NAME}"
47+
48+
- name: Validate tag matches package version
49+
run: |
50+
set -euo pipefail
51+
TAG="${GITHUB_REF_NAME}"
52+
VERSION="${{ steps.package.outputs.version }}"
53+
if [[ "${TAG}" != "v${VERSION}" ]]; then
54+
echo "::error::Tag ${TAG} does not match package version v${VERSION}." >&2
55+
exit 1
56+
fi
57+
echo "Tag ${TAG} matches package version v${VERSION}."
58+
59+
- name: Validate changelog entries
60+
run: |
61+
set -euo pipefail
62+
VERSION="${{ steps.package.outputs.version }}"
63+
for file in VERSION_LOG.md docs/VERSION_LOG.md; do
64+
if [[ ! -f "$file" ]]; then
65+
echo "::error file=$file::Changelog file is missing." >&2
66+
exit 1
67+
fi
68+
if ! grep -q "## v${VERSION}" "$file"; then
69+
echo "::error file=$file::Missing changelog entry for v${VERSION}." >&2
70+
exit 1
71+
fi
72+
echo "Found changelog entry for v${VERSION} in $file."
73+
done
74+
75+
build:
76+
name: Build ${{ matrix.display-name }}
77+
needs: validate
78+
runs-on: ${{ matrix.os }}
79+
defaults:
80+
run:
81+
shell: bash
82+
strategy:
83+
fail-fast: false
84+
matrix:
85+
include:
86+
- display-name: macOS x64
87+
os: macos-latest
88+
script: package:mac:x64
89+
arch: x64
90+
npm-platform: darwin
91+
cache-paths: |
92+
~/Library/Caches/electron
93+
~/Library/Caches/electron-builder
94+
artifact-name: docforge-macos-x64
95+
- display-name: Windows x64
96+
os: windows-latest
97+
script: package:win:x64
98+
arch: x64
99+
npm-platform: win32
100+
cache-paths: |
101+
~/AppData/Local/electron/Cache
102+
~/AppData/Local/electron-builder/Cache
103+
artifact-name: docforge-windows-x64
104+
- display-name: Windows ia32
105+
os: windows-latest
106+
script: package:win:ia32
107+
arch: ia32
108+
npm-platform: win32
109+
cache-paths: |
110+
~/AppData/Local/electron/Cache
111+
~/AppData/Local/electron-builder/Cache
112+
artifact-name: docforge-windows-ia32
113+
- display-name: Linux x64
114+
os: ubuntu-latest
115+
script: package:linux:x64
116+
arch: x64
117+
npm-platform: linux
118+
cache-paths: |
119+
~/.cache/electron
120+
~/.cache/electron-builder
121+
artifact-name: docforge-linux-x64
122+
- display-name: Linux armv7l
123+
os: ubuntu-latest
124+
script: package:linux:armv7l
125+
arch: armv7l
126+
npm-platform: linux
127+
cache-paths: |
128+
~/.cache/electron
129+
~/.cache/electron-builder
130+
artifact-name: docforge-linux-armv7l
131+
- display-name: Linux arm64
132+
os: ubuntu-latest
133+
script: package:linux:arm64
134+
arch: arm64
135+
npm-platform: linux
136+
cache-paths: |
137+
~/.cache/electron
138+
~/.cache/electron-builder
139+
artifact-name: docforge-linux-arm64
140+
env:
141+
NODE_OPTIONS: --max_old_space_size=4096
142+
steps:
143+
- name: Checkout repository
144+
uses: actions/checkout@v4
145+
with:
146+
fetch-depth: 0
147+
148+
- name: Setup Node.js
149+
uses: actions/setup-node@v4
150+
with:
151+
node-version: 20
152+
cache: npm
153+
cache-dependency-path: package-lock.json
154+
155+
- name: Cache Electron downloads
156+
uses: actions/cache@v4
157+
with:
158+
path: ${{ matrix.cache-paths }}
159+
key: ${{ runner.os }}-electron-${{ matrix.arch }}-${{ hashFiles('package-lock.json') }}
160+
restore-keys: |
161+
${{ runner.os }}-electron-${{ matrix.arch }}-
162+
${{ runner.os }}-electron-
163+
164+
- name: Install system dependencies (Linux)
165+
if: runner.os == 'Linux'
166+
run: |
167+
set -euo pipefail
168+
sudo apt-get update
169+
sudo apt-get install --no-install-recommends -y rpm libarchive-tools
170+
if ! sudo apt-get install --no-install-recommends -y libfuse2; then
171+
sudo apt-get install --no-install-recommends -y libfuse2t64
172+
fi
173+
174+
- name: Install dependencies
175+
run: npm ci
176+
env:
177+
npm_config_arch: ${{ matrix.arch }}
178+
npm_config_target_arch: ${{ matrix.arch }}
179+
npm_config_platform: ${{ matrix.npm-platform }}
180+
181+
- name: Prepare native modules
182+
run: npm run postinstall --if-present
183+
env:
184+
npm_config_arch: ${{ matrix.arch }}
185+
npm_config_target_arch: ${{ matrix.arch }}
186+
npm_config_platform: ${{ matrix.npm-platform }}
187+
188+
- name: Package application
189+
run: npm run ${{ matrix.script }}
190+
env:
191+
npm_config_arch: ${{ matrix.arch }}
192+
npm_config_target_arch: ${{ matrix.arch }}
193+
npm_config_platform: ${{ matrix.npm-platform }}
194+
195+
- name: Upload build artifacts
196+
if: always()
197+
uses: actions/upload-artifact@v4
198+
with:
199+
name: ${{ matrix.artifact-name }}
200+
path: |
201+
release/**
202+
!**/*.blockmap
203+
if-no-files-found: error
204+
retention-days: 14
205+
206+
- name: Upload build logs on failure
207+
if: failure()
208+
uses: actions/upload-artifact@v4
209+
with:
210+
name: ${{ matrix.artifact-name }}-logs
211+
path: |
212+
npm-debug.log
213+
*.log
214+
release/**/*.log
215+
if-no-files-found: ignore
216+
retention-days: 7
217+
218+
publish:
219+
name: Publish GitHub release
220+
needs:
221+
- validate
222+
- build
223+
runs-on: ubuntu-latest
224+
steps:
225+
- name: Checkout repository
226+
uses: actions/checkout@v4
227+
228+
- name: Download build artifacts
229+
uses: actions/download-artifact@v4
230+
with:
231+
path: release-artifacts
232+
merge-multiple: false
233+
234+
- name: Setup Node.js
235+
uses: actions/setup-node@v4
236+
with:
237+
node-version: 20
238+
239+
- name: Generate release notes
240+
id: notes
241+
run: |
242+
set -euo pipefail
243+
node scripts/generate-release-notes.mjs \
244+
--tag "${{ needs.validate.outputs.tag }}" \
245+
--version "${{ needs.validate.outputs.version }}" \
246+
--artifact-root "release-artifacts" \
247+
--changelog "docs/VERSION_LOG.md" \
248+
--output "release-notes.md" \
249+
--files-output "release-files.txt"
250+
echo "files<<EOF" >> "$GITHUB_OUTPUT"
251+
cat release-files.txt >> "$GITHUB_OUTPUT"
252+
echo "EOF" >> "$GITHUB_OUTPUT"
253+
env:
254+
GITHUB_REPOSITORY: ${{ github.repository }}
255+
256+
- name: Display release notes
257+
run: |
258+
echo "Generated release notes:" >&2
259+
cat release-notes.md
260+
261+
- name: Create GitHub release
262+
uses: softprops/action-gh-release@v2
263+
with:
264+
tag_name: ${{ needs.validate.outputs.tag }}
265+
name: DocForge v${{ needs.validate.outputs.version }}
266+
body_path: release-notes.md
267+
files: ${{ steps.notes.outputs.files }}
268+
draft: false
269+
prerelease: false
270+
fail_on_unmatched_files: true
271+
env:
272+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

0 commit comments

Comments
 (0)