Repository navigation
173 lines (168 loc) · 7.53 KB
/
Copy pathrelease.yml
File metadata and controls
173 lines (168 loc) · 7.53 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
name: Release
# Releases a version: its tag on the commit, the installers for every platform, and a GitHub
# release they're attached to (a draft pre-release unless asked otherwise). Started by
# - merging a "Version X.Y.Z" pull request from Prepare release (its release- branch): the merge
# commit is tagged vX.Y.Z, with that run's pre-release / publish choices;
# - pushing a vX.Y.Z tag by hand: a draft pre-release;
# - running it by hand: the installers as workflow artifacts only, no tag or release.
on:
push:
tags: ['v*']
pull_request:
types: [closed]
branches: [main]
workflow_dispatch:
permissions:
contents: read
jobs:
setup:
name: What to release
if: github.event_name != 'pull_request' || (github.event.pull_request.merged && startsWith(github.event.pull_request.head.ref, 'release-'))
runs-on: ubuntu-latest
permissions:
contents: write
outputs:
release: ${{ steps.plan.outputs.release }}
tag: ${{ steps.plan.outputs.tag }}
sha: ${{ steps.plan.outputs.sha }}
prerelease: ${{ steps.plan.outputs.prerelease }}
publish: ${{ steps.plan.outputs.publish }}
steps:
- uses: actions/checkout@v7
with:
ref: ${{ github.event.pull_request.merge_commit_sha || github.sha }}
- id: plan
env:
EVENT: ${{ github.event_name }}
SHA: ${{ github.event.pull_request.merge_commit_sha || github.sha }}
BODY: ${{ github.event.pull_request.body }}
GH_TOKEN: ${{ github.token }}
run: |
tag="v$(node -p 'require("./package.json").version')"
release=true prerelease=true publish=false
case "$EVENT" in
push)
[ "$GITHUB_REF_NAME" = "$tag" ] || { echo "::error::Tag $GITHUB_REF_NAME, but package.json says $tag"; exit 1; } ;;
pull_request)
# The choices Prepare release left in the pull request.
grep -q 'prerelease=false' <<<"$BODY" && prerelease=false
grep -q 'publish=true' <<<"$BODY" && publish=true
if existing=$(gh api "repos/$GITHUB_REPOSITORY/git/ref/tags/$tag" --jq .object.sha 2>/dev/null); then
[ "$existing" = "$SHA" ] || { echo "::error::$tag already tags $existing"; exit 1; }
else
gh api "repos/$GITHUB_REPOSITORY/git/refs" -f ref="refs/tags/$tag" -f sha="$SHA" >/dev/null
fi ;;
*) release=false ;;
esac
echo "release=$release tag=$tag sha=$SHA prerelease=$prerelease publish=$publish"
{ echo "release=$release"; echo "tag=$tag"; echo "sha=$SHA"; echo "prerelease=$prerelease"; echo "publish=$publish"; } >> "$GITHUB_OUTPUT"
build:
name: ${{ matrix.name }}
needs: setup
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
# The Python sidecar is built for the runner's own architecture: one runner per arch.
- { name: macOS arm64, runner: macos-15, target: --mac --arm64, arch: arm64, unpacked: mac-arm64/CFEngine Policy Builder.app/Contents/Resources }
- { name: macOS x64, runner: macos-15-intel, target: --mac --x64, arch: x86_64, unpacked: mac/CFEngine Policy Builder.app/Contents/Resources }
- { name: Linux x64, runner: ubuntu-latest, target: --linux --x64, unpacked: linux-unpacked/resources }
- { name: Windows x64, runner: windows-latest, target: --win --x64, unpacked: win-unpacked/resources }
steps:
- uses: actions/checkout@v7
with:
ref: ${{ needs.setup.outputs.sha }}
# No dependency caches: installers are built from a clean install, and a cache can't carry
# anything into them (CodeQL's cache poisoning check).
- uses: actions/setup-node@v7
with:
node-version-file: .nvmrc
package-manager-cache: false
# Its cache is on by default on GitHub's runners.
- uses: astral-sh/setup-uv@v7
with:
enable-cache: false
- name: rpm tooling
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y rpm
- run: npm ci
- run: uv sync --frozen --directory python --group build
# No signing certificates yet: macOS builds are signed ad hoc (Apple Silicon runs nothing
# unsigned), which hardened runtime would reject; Windows builds are unsigned.
- name: Package
shell: bash
env:
CSC_IDENTITY_AUTO_DISCOVERY: 'false'
run: |
mac=()
if [ "$RUNNER_OS" = macOS ]; then mac=(-c.mac.identity=- -c.mac.hardenedRuntime=false); fi
npm run package:prepare
npx electron-builder ${{ matrix.target }} --publish never "${mac[@]}"
- name: The packaged sidecar runs
shell: bash
run: |
backend="release/${{ matrix.unpacked }}/backend/cfpb-backend"
[ "$RUNNER_OS" = Windows ] && backend="$backend.exe"
# Built for the app's architecture (Rosetta would run a wrong one anyway).
[ "$RUNNER_OS" != macOS ] || file "$backend" | grep -q "${{ matrix.arch }}"
echo '{"files": []}' | "$backend" compile
- uses: actions/upload-artifact@v7
with:
name: ${{ matrix.name }}
path: |
release/*.dmg
release/*.zip
release/*.exe
release/*.AppImage
release/*.deb
release/*.rpm
if-no-files-found: error
retention-days: 14
release:
name: Release
needs: [setup, build]
if: needs.setup.outputs.release == 'true'
runs-on: ubuntu-latest
permissions:
contents: write
env:
TAG: ${{ needs.setup.outputs.tag }}
PRERELEASE: ${{ needs.setup.outputs.prerelease }}
PUBLISH: ${{ needs.setup.outputs.publish }}
GH_TOKEN: ${{ github.token }}
steps:
- uses: actions/checkout@v7
with:
ref: ${{ needs.setup.outputs.sha }}
- uses: actions/download-artifact@v8
with:
path: installers
merge-multiple: true
# A draft prepared by hand for this tag gets the installers, keeping its title and notes.
- name: Attach the installers
run: |
draft() { gh api "repos/$GITHUB_REPOSITORY/releases" --jq ".[] | select(.draft and .tag_name == \"$TAG\") | .id" | head -1; }
id=$(draft)
if [ -z "$id" ]; then
title="$TAG" flags=()
notes=.github/release-notes.md
if [ "$PRERELEASE" = true ]; then
title="$TAG (preview, not production-ready)" flags=(--prerelease)
else
# The "not ready for production" box is for previews only.
grep -v '^>' .github/release-notes.md | sed '/./,$!d' > /tmp/notes.md && notes=/tmp/notes.md
fi
gh release create "$TAG" installers/* --draft "${flags[@]}" --title "$title" --generate-notes --notes-file "$notes"
id=$(draft)
else
# The tag goes along: a draft's update without one clears it.
gh api --method PATCH "repos/$GITHUB_REPOSITORY/releases/$id" -f tag_name="$TAG" -F prerelease="$PRERELEASE" >/dev/null
for file in installers/*; do
gh api --method POST -H "Content-Type: application/octet-stream" --input "$file" \
"https://uploads.github.com/repos/$GITHUB_REPOSITORY/releases/$id/assets?name=$(basename "$file")" >/dev/null
done
fi
if [ "$PUBLISH" = true ]; then
gh api --method PATCH "repos/$GITHUB_REPOSITORY/releases/$id" -f tag_name="$TAG" -F draft=false >/dev/null
fi