Repository navigation
201 lines (192 loc) · 7.35 KB
/
Copy pathrelease.yml
File metadata and controls
201 lines (192 loc) · 7.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
name: Release
# Manually triggered (Actions tab -> Release -> Run workflow) by someone
# with write access to this repo -- deliberately NOT a tag-push trigger,
# so a release/PyPI publish can never fire from a stray tag or from a
# fork/PR (which never has write access or secrets on a public repo
# regardless). Builds standalone binaries for every supported platform
# (PyInstaller can't cross-compile, so each one runs on a real runner of
# that OS/arch), attaches them to a GitHub Release, then publishes to
# PyPI, npm (as platform-specific optionalDependencies packages), and
# GHCR.
#
# Required repo secrets (Settings -> Secrets and variables -> Actions):
# PYPI_API_TOKEN - PyPI account -> API tokens
# NPM_TOKEN - npmjs.com account -> Access Tokens -> Automation
# GITHUB_TOKEN (GHCR push) is provided automatically, no setup needed.
on:
workflow_dispatch:
inputs:
tag:
description: "Tag to release (e.g. v0.3.0) -- must already exist and match src/secfoo/__about__.py"
required: true
# Read-only by default; the two jobs that need to write (the GitHub
# Release, the GHCR push) grant themselves exactly that permission.
permissions:
contents: read
jobs:
check-version:
name: Verify tag matches package version
runs-on: ubuntu-latest
outputs:
version: ${{ steps.check.outputs.version }}
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
with:
ref: ${{ inputs.tag }}
- id: check
run: |
TAG_VERSION="${{ inputs.tag }}"
TAG_VERSION="${TAG_VERSION#v}"
PKG_VERSION=$(grep -oP '(?<=__version__ = ")[^"]+' src/secfoo/__about__.py)
echo "Tag version: $TAG_VERSION"
echo "Package version: $PKG_VERSION"
if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then
echo "::error::Tag ${{ inputs.tag }} does not match src/secfoo/__about__.py's __version__ ($PKG_VERSION). Bump __about__.py and retag."
exit 1
fi
echo "version=$PKG_VERSION" >> "$GITHUB_OUTPUT"
build-binaries:
name: Build binary (${{ matrix.name }})
needs: check-version
strategy:
fail-fast: false
matrix:
include:
- os: macos-14
name: darwin-arm64
- os: macos-15-intel
name: darwin-x64
- os: ubuntu-latest
name: linux-x64
- os: ubuntu-24.04-arm
name: linux-arm64
- os: windows-latest
name: win32-x64
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
with:
ref: ${{ inputs.tag }}
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: "3.12"
- name: Install package + build deps
run: pip install -e ".[build]"
- name: Build binary
run: pyinstaller packaging/secfoo.spec --distpath dist/binary --workpath build/pyinstaller --noconfirm
- name: Stage artifact under its final release name
shell: bash
run: |
mkdir -p out
if [ "${{ runner.os }}" = "Windows" ]; then
cp dist/binary/secfoo.exe out/secfoo-${{ matrix.name }}.exe
else
cp dist/binary/secfoo out/secfoo-${{ matrix.name }}
fi
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: secfoo-${{ matrix.name }}
path: out/*
if-no-files-found: error
release:
name: Create GitHub Release
needs: build-binaries
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
path: artifacts
- name: Flatten artifacts and compute checksums
run: |
mkdir -p release
find artifacts -type f -exec cp {} release/ \;
(cd release && sha256sum * > SHA256SUMS)
- uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2.6.2
with:
tag_name: ${{ inputs.tag }}
files: release/*
generate_release_notes: true
publish-pypi:
name: Publish to PyPI
needs: [check-version]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
with:
ref: ${{ inputs.tag }}
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: "3.12"
- run: pip install build twine
- run: python -m build
- env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
run: twine upload dist/*.tar.gz dist/*.whl
publish-npm:
name: Publish npm packages
needs: [check-version, release]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
path: artifacts
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: "20"
registry-url: "https://registry.npmjs.org"
- name: Populate platform binaries and publish all 6 packages
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
VERSION: ${{ needs.check-version.outputs.version }}
run: |
set -euo pipefail
declare -A DIRS=(
[darwin-arm64]=npm/secfoo-darwin-arm64
[darwin-x64]=npm/secfoo-darwin-x64
[linux-x64]=npm/secfoo-linux-x64
[linux-arm64]=npm/secfoo-linux-arm64
[win32-x64]=npm/secfoo-win32-x64
)
for plat in "${!DIRS[@]}"; do
pkgdir="${DIRS[$plat]}"
mkdir -p "$pkgdir/bin"
if [ "$plat" = "win32-x64" ]; then
cp "artifacts/secfoo-$plat/secfoo-$plat.exe" "$pkgdir/bin/secfoo.exe"
else
cp "artifacts/secfoo-$plat/secfoo-$plat" "$pkgdir/bin/secfoo"
chmod +x "$pkgdir/bin/secfoo"
fi
npm version "$VERSION" --no-git-tag-version --prefix "$pkgdir" --allow-same-version
npm publish --access public "$pkgdir"
done
npm version "$VERSION" --no-git-tag-version --prefix npm/secfoo --allow-same-version
npm publish --access public npm/secfoo
publish-docker:
name: Publish Docker image (GHCR)
needs: [check-version, publish-pypi]
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
with:
ref: ${{ inputs.tag }}
- uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Wait for the new version to be installable from PyPI
run: sleep 60
- uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
with:
context: .
push: true
tags: |
ghcr.io/rakfortltd/secfoo:${{ needs.check-version.outputs.version }}
ghcr.io/rakfortltd/secfoo:latest