Skip to content

Commit 6a54ffb

Browse files
authored
Add automated release workflow (#1748)
* Add automated release workflow Triggered on semver tag push. Handles npm publish, GitHub Release with binaries, .deb build+sign, and PPA upload to Cloudflare R2. Includes workflow_dispatch with dry-run mode for safe testing. * Ignore all GitHub workflows in yamllint * Pass GPG secrets via env vars instead of inline expansion * Use npm trusted publishing instead of NPM_TOKEN * Set version in package.json and index.ts from tag * Use 0.0.0 placeholder for version in package.json and index.ts * Use rclone copy instead of sync to never delete R2 files
1 parent ee99b20 commit 6a54ffb

4 files changed

Lines changed: 106 additions & 3 deletions

File tree

.github/workflows/release.yml

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
---
2+
name: release
3+
on:
4+
push:
5+
tags:
6+
- '[0-9]+.[0-9]+.[0-9]+'
7+
workflow_dispatch:
8+
inputs:
9+
dry-run:
10+
description: 'Dry run (npm --dry-run, draft release, skip R2 upload)'
11+
type: boolean
12+
default: true
13+
14+
env:
15+
PKG_CACHE_PATH: .pkg
16+
DRY_RUN: ${{ inputs.dry-run || false }}
17+
18+
jobs:
19+
release:
20+
runs-on: ubuntu-24.04
21+
permissions:
22+
contents: write
23+
id-token: write
24+
steps:
25+
- uses: actions/checkout@v6
26+
27+
- name: Set version from tag
28+
run: |
29+
sed -i "s/0.0.0/${GITHUB_REF_NAME}/g" package.json src/index.ts
30+
31+
- uses: actions/setup-node@v6
32+
with:
33+
node-version: '24'
34+
cache: 'npm'
35+
registry-url: 'https://registry.npmjs.org'
36+
37+
- uses: actions/cache@v5.0.3
38+
with:
39+
path: ${{ env.PKG_CACHE_PATH }}
40+
key: pkg-cache
41+
42+
- run: npm ci
43+
- run: npm run build
44+
- run: npm run esbuild
45+
- run: npm run pkg-all
46+
47+
- name: Install rclone
48+
run: sudo apt-get update && sudo apt-get install -y rclone
49+
50+
- name: Download existing ppa from R2
51+
run: |
52+
rclone copy r2:${R2_BUCKET}/ ppa/ \
53+
--s3-provider=Cloudflare \
54+
--s3-access-key-id="${R2_ACCESS_KEY_ID}" \
55+
--s3-secret-access-key="${R2_SECRET_ACCESS_KEY}" \
56+
--s3-endpoint="${R2_ENDPOINT}" \
57+
--s3-no-check-bucket
58+
env:
59+
R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
60+
R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
61+
R2_ENDPOINT: ${{ secrets.R2_ENDPOINT }}
62+
R2_BUCKET: ${{ secrets.R2_BUCKET }}
63+
64+
- name: Import GPG key and preset passphrase
65+
run: |
66+
echo "${GPG_PRIVATE_KEY}" | gpg --batch --import
67+
echo "allow-preset-passphrase" >> ~/.gnupg/gpg-agent.conf
68+
gpgconf --kill gpg-agent
69+
gpgconf --launch gpg-agent
70+
for keygrip in $(gpg --list-secret-keys --with-keygrip --with-colons madsjon@gmail.com | awk -F: '/grp/{print $10}'); do
71+
/usr/lib/gnupg/gpg-preset-passphrase --preset --passphrase "${GPG_PASSPHRASE}" "$keygrip"
72+
done
73+
env:
74+
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
75+
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
76+
77+
- name: Build .deb and sign
78+
run: ./publish-deb
79+
80+
- name: Publish to npm
81+
run: npm publish --provenance ${{ env.DRY_RUN == 'true' && '--dry-run' || '' }}
82+
83+
- name: Create GitHub Release
84+
run: >
85+
gh release create "${GITHUB_REF_NAME}" bin/*.gz --generate-notes
86+
${{ env.DRY_RUN == 'true' && '--draft' || '' }}
87+
env:
88+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
89+
90+
- name: Upload ppa to Cloudflare R2
91+
if: env.DRY_RUN != 'true'
92+
run: |
93+
rclone copy ppa/ r2:${R2_BUCKET}/ \
94+
--s3-provider=Cloudflare \
95+
--s3-access-key-id="${R2_ACCESS_KEY_ID}" \
96+
--s3-secret-access-key="${R2_SECRET_ACCESS_KEY}" \
97+
--s3-endpoint="${R2_ENDPOINT}" \
98+
--s3-no-check-bucket
99+
env:
100+
R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
101+
R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
102+
R2_ENDPOINT: ${{ secrets.R2_ENDPOINT }}
103+
R2_BUCKET: ${{ secrets.R2_BUCKET }}

.yamllint.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ ignore: |
99
examples/docker-swarm-php/vendor/
1010
MergeRequest-Pipelines.gitlab-ci.yml
1111
PHP.gitlab-ci.yml
12-
/.github/workflows/build.yml
12+
/.github/workflows/
1313
1414
rules:
1515
line-length: { max: 160 }

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"description": "Tired of pushing to test your .gitlab-ci.yml?",
55
"main": "src/index.js",
66
"bin": "src/index.js",
7-
"version": "4.64.1",
7+
"version": "0.0.0",
88
"scripts": {
99
"esbuild": "esbuild src/index.ts --outfile=bin/index.cjs --bundle --platform=node --format=cjs --minify --external:yargs --sourcemap=inline",
1010
"pkg-linux": "pkg bin/index.cjs --public --options=enable-source-maps --no-bytecode -t linux-x64 -o bin/linux/gitlab-ci-local && chmod +x bin/linux/gitlab-ci-local && gzip -c bin/linux/gitlab-ci-local > bin/linux.gz",

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ process.on("SIGUSR2", async () => await cleanupJobResources(jobs));
2525
const yparser = yargs(process.argv.slice(2));
2626
yparser.parserConfiguration({"greedy-arrays": false})
2727
.showHelpOnFail(false)
28-
.version("4.64.1")
28+
.version("0.0.0")
2929
.wrap(yparser.terminalWidth?.())
3030
.command({
3131
handler: async (argv) => {

0 commit comments

Comments
 (0)