-
Notifications
You must be signed in to change notification settings - Fork 0
411 lines (401 loc) · 17 KB
/
Copy pathmain.yaml
File metadata and controls
411 lines (401 loc) · 17 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
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
---
name: main
on: # yamllint disable-line rule:truthy
push:
branches: [main]
pull_request:
types: [closed]
branches: [main]
workflow_dispatch:
permissions:
contents: write
packages: write
pull-requests: write
jobs:
validate:
name: Validations
uses: ./.github/workflows/ci.yaml
with:
skip_commit_validation: true
manage-release-pr:
needs: [validate]
# Every push to main (not a release-PR merge itself) - keeps the
# pending "chore(main): release X.Y.Z" PR current as commits land.
if: github.event_name == 'push'
name: Open/update the release PR
runs-on: ubuntu-latest
steps:
-
uses: actions/checkout@v7
with:
fetch-tags: true
-
# skip-github-release: this only ever manages the version-bump PR
# (manifest/CHANGELOG.md/mix.exs) - it never creates a tag or
# GitHub Release. The burrito-package job below does that itself,
# atomically, once this PR merges and binaries are already built -
# see its own comments for why.
uses: googleapis/release-please-action@v5
with:
config-file: .release-please-config.json
manifest-file: .release-please-manifest.json
token: ${{ secrets.RELEASE_PLEASE_TOKEN }}
skip-github-release: true
burrito-build:
needs: [validate]
# Only when release-please's own release PR (always from this exact
# branch) actually merges - "approving the release PR" is the trigger
# for building, not every push to main.
if: github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request' && github.event.pull_request.merged == true && github.event.pull_request.head.ref == 'release-please--branches--main')
name: Build Burrito target (${{ matrix.target }})
runs-on: ${{ matrix.runner }}
strategy:
# Kept in sync with mix.exs's releases.lc.burrito.targets - Burrito
# has no introspection command to read this list back out, so it's
# duplicated here by hand (see #66). Default fail-fast: true - a
# failed required leg cancels the others and fails this job outright,
# which correctly skips burrito-package below rather than packaging
# a release with a target missing.
#
# Each target runs on its matching OS runner so rustler_precompiled
# (used by mdex_native) downloads the correct precompiled NIF for the
# target platform. Cross-compiling from Ubuntu caused the Linux NIF to
# be bundled into macOS/Windows releases, making MDEx unavailable at
# runtime (see CRY-40).
matrix:
include:
- target: macos_aarch64
runner: macos-latest
- target: linux_x86_64
runner: ubuntu-latest
- target: windows_x86_64
runner: windows-latest
defaults:
run:
working-directory: app
shell: bash
steps:
-
uses: actions/checkout@v7
-
uses: erlef/setup-beam@v1
with:
otp-version: "29.0.3"
elixir-version: "1.20.3"
# Without this, setup-beam's problem matchers promote every
# compiler warning from deps (e.g. postgrex/rewrite's deprecated
# `xref: [exclude: ...]`, yamerl's deprecated `catch ...` syntax -
# both already at their latest published Hex versions, so not
# fixable from here) into noisy GH Actions annotations. See #9.
disable_problem_matchers: true
-
# Version pinned to what Burrito 1.6.0 actually requires - its own
# README says 0.15.2, but the version check enforces 0.16.0. See
# documents/phase-8-plan.adoc.
uses: mlugg/setup-zig@v2.2.1
with:
version: "0.16.0"
-
run: mix deps.get
-
name: Build the ${{ matrix.target }} target
run: MIX_ENV=prod BURRITO_TARGET=${{ matrix.target }} mix release lc
-
# `mix release lc`'s exit code alone doesn't guarantee this target
# actually produced a binary - Burrito builds each target
# independently, so a target failing partway through wouldn't
# necessarily fail the whole `mix release` invocation. Not everyone
# installing this uses Homebrew (no tap exists yet anyway - see
# #14/CRY-37), so linux_x86_64/macos_aarch64 are the primary
# install path for a lot of users; silently shipping a release
# missing one of them is worse than failing loudly here.
# windows_x86_64 stays best-effort, as it always has.
name: Verify this target actually built
run: |
artifact="burrito_out/lc_${{ matrix.target }}"
if [ "${{ matrix.target }}" = "windows_x86_64" ]; then
artifact="${artifact}.exe"
fi
if [ ! -f "$artifact" ]; then
if [ "${{ matrix.target }}" = "windows_x86_64" ]; then
printf '::warning::missing optional release artifact: %s\n' "$artifact"
else
printf '::error::missing required release artifact: %s\n' "$artifact"
exit 1
fi
fi
-
# Handed off to burrito-package below, which needs every target's
# binary gathered back into one place before it can build the
# per-platform tarballs/SBOM/release.
name: Upload the built binary
uses: actions/upload-artifact@v7
with:
name: burrito-${{ matrix.target }}
path: app/burrito_out/lc_${{ matrix.target }}*
burrito-package:
needs: [burrito-build]
name: Package Burrito binaries and create the release
runs-on: ubuntu-latest
outputs:
tag_name: ${{ steps.version.outputs.tag_name }}
defaults:
run:
working-directory: app
steps:
-
uses: actions/checkout@v7
-
# Needed for erlef/mix_sbom below (an accurate dependency graph
# needs `mix deps.get` to have run against a real OTP/Elixir
# install) - this job never builds a release itself, so no
# setup-zig/p7zip here.
uses: erlef/setup-beam@v1
with:
otp-version: "29.0.3"
elixir-version: "1.20.3"
disable_problem_matchers: true
-
run: mix deps.get
-
# Every burrito-build matrix leg uploaded exactly one binary under
# its own `burrito-<target>` artifact name - merge them all back
# into one directory here, same layout the pre-matrix single job
# produced.
name: Download every built target's binary
uses: actions/download-artifact@v7
with:
path: app/burrito_out
pattern: burrito-*
merge-multiple: true
-
# A bare `lc_macos_aarch64` binary download doesn't get you
# lcreate/lcls/lclose/lcomment/lproj - only install.sh and the
# container image ever pulled those, straight from the repo
# checkout, never from a release asset. Package each target
# together with the wrapper scripts so a release download is
# actually installable on its own, matching what install.sh gives
# you.
name: Package each target into a per-platform tarball with the wrapper scripts
run: |
for artifact in lc_*
do
case "$artifact" in
lc_windows_x86_64.exe)
target=lc_windows_x86_64
binary_name=lc.exe
;;
*)
target=$artifact
binary_name=lc
;;
esac
stage=$(mktemp -d)
cp "$artifact" "$stage/$binary_name"
cp ../../bin/lcreate ../../bin/lcls ../../bin/lclose ../../bin/lcomment ../../bin/lproj "$stage/"
chmod +x "$stage"/*
tar -czf "${target}.tar.gz" -C "$stage" .
rm -rf "$stage" "$artifact"
done
working-directory: app/burrito_out
-
name: Generate checksums for every built artifact
run: sha256sum -- * > SHA256SUMS
working-directory: app/burrito_out
-
# manage-release-pr already bumped .release-please-manifest.json
# on main as part of the PR this job's trigger just merged - read
# the version straight from it rather than asking release-please
# again.
name: Read the version release-please just bumped to
id: version
run: |
version=$(ruby -rjson -e "print JSON.parse(File.read('../.release-please-manifest.json'))['.']")
printf 'tag_name=v%s\n' "$version" >> "$GITHUB_OUTPUT"
-
# Covers the app + Hex deps + the actual Erlang/OTP and Elixir
# versions this release was built with (mix_sbom includes those by
# default - verified they show up as individual OTP application
# components like kernel/stdlib/crypto/ssl, not one umbrella
# "erlang" entry). Not the container image's own OS packages -
# that's a separate SBOM, generated in the container job below,
# since it's meaningless to anyone not using the container.
name: Generate the app SBOM
uses: erlef/mix_sbom@v0
id: sbom
with:
project-path: ${{ github.workspace }}/app
reuse-beam: true
schema: "1.6"
format: "json"
-
# mix_sbom has no output-filename input - it always writes to
# $RUNNER_TEMP/$RANDOM.cdx.json internally (verified directly in
# the action's own script) and exposes that random path via
# sbom-path. gh release create uses a file's own basename as the
# asset's actual download filename (the `#label` syntax only sets
# a cosmetic display label, not the filename) - without this
# rename, the Readme's documented .../download/sbom.cdx.json URL
# would 404.
name: Rename the SBOM to a stable filename
run: mv "${{ steps.sbom.outputs.sbom-path }}" sbom.cdx.json
-
# One atomic command creates the tag, the release, and uploads
# every asset together - no separate release object sits around
# empty/unlocked waiting for a later upload, so GitHub's Immutable
# Releases (GA since Oct 2025) never gets a chance to lock us out
# (that's exactly what broke v0.2.0 permanently - see #18).
name: Create the release with every asset attached
env:
GH_TOKEN: ${{ github.token }}
run: gh release create "${{ steps.version.outputs.tag_name }}" burrito_out/*.tar.gz burrito_out/SHA256SUMS sbom.cdx.json --generate-notes
working-directory: app
-
# release-please labels its own release PR "autorelease: pending"
# and only relabels it "autorelease: tagged" once it creates the
# tag itself. Since we tag it ourselves above instead, that
# transition never happens on its own. Without this,
# release-please's own merged-PR guard (in manage-release-pr)
# keeps finding this PR stuck "pending" forever and refuses to
# open any future release PR ("There are untagged, merged release
# PRs outstanding - aborting").
name: Mark the release PR as tagged
env:
GH_TOKEN: ${{ github.token }}
run: |
gh pr edit "${{ github.event.pull_request.number }}" \
--remove-label "autorelease: pending" \
--add-label "autorelease: tagged"
working-directory: .
container:
needs: [burrito-package]
name: Build and publish container image
runs-on: ubuntu-latest
steps:
-
uses: actions/checkout@v7
-
uses: erlef/setup-beam@v1
with:
otp-version: "29.0.3"
elixir-version: "1.20.3"
disable_problem_matchers: true
-
# Same Burrito build as the `burrito-build` job above, so it needs
# the same pinned Zig (see that job's comment) - missing here would
# fail this job's build at `mix release` time even though
# `burrito-build` succeeds.
uses: mlugg/setup-zig@v2.2.1
with:
version: "0.16.0"
-
run: mix deps.get
working-directory: app
-
name: Build the linux_x86_64 target (container's payload)
run: MIX_ENV=prod BURRITO_TARGET=linux_x86_64 mix release lc
working-directory: app
-
name: Build the image
env:
APP_VERSION: ${{ needs.burrito-package.outputs.tag_name }}
run: ./ci/build_image.sh "${{ needs.burrito-package.outputs.tag_name }}"
-
# ci/build_image.sh prefers Podman over Docker (both are present on
# GitHub-hosted runners), so the image above lives only in Podman's
# own local storage. Trivy's `image-ref` scanning mode expects a
# Docker-compatible daemon/socket and has no visibility into that
# storage, so it'd either find nothing or silently try to pull from
# a registry instead (see #67 - a runtime-preference reorder was
# tried and rejected, since it would've meant Podman-built images
# go untested here). Saving to a tarball and scanning that instead
# sidesteps the runtime question entirely: Trivy reads the tarball
# directly, no daemon of either kind involved.
name: Save the image to a tarball for Trivy to scan directly
id: save
run: |
tarball="$RUNNER_TEMP/linear-cli.tar"
./ci/save_image.sh "${{ needs.burrito-package.outputs.tag_name }}" "$tarball"
printf 'tarball_path=%s\n' "$tarball" >> "$GITHUB_OUTPUT"
-
# Covers the container's own OS packages (Alpine/apk - ca-certificates,
# bash) - meaningless to anyone not using the container, which is
# exactly why it's a separate file from the app SBOM in the
# burrito-package job above, not merged into it. Scans the tarball
# the previous step just saved, not a live image-ref - see that
# step's own comment for why - before publishing, though nothing
# here gates the publish step on it.
name: Generate the container SBOM
uses: aquasecurity/trivy-action@v0.36.0
with:
input: ${{ steps.save.outputs.tarball_path }}
scan-type: image
format: cyclonedx
output: container-sbom.cdx.json
-
name: Publish the image
env:
GITHUB_TOKEN: ${{ github.token }}
GITHUB_ACTOR: ${{ github.actor }}
run: ./ci/publish.sh "${{ needs.burrito-package.outputs.tag_name }}"
-
# Can't attach this to the GitHub release the burrito-package job
# already created - it's published (and thus immutable, see #18) by
# the time this job runs. A workflow artifact is the honest option
# here, not a release asset pretending to be one.
name: Upload the container SBOM as a workflow artifact
uses: actions/upload-artifact@v7
with:
name: container-sbom
path: container-sbom.cdx.json
retention-days: 90
homebrew-tap-bump:
needs: [burrito-package]
name: Bump the Homebrew tap formula
runs-on: ubuntu-latest
steps:
-
name: Checkout linear-cli (for ci/bump_homebrew_formula.rb)
uses: actions/checkout@v7
with:
path: linear-cli
-
# RELEASE_PLEASE_TOKEN (org secret) - the default GITHUB_TOKEN is
# scoped only to this repo, and can't push/open a PR against a
# different one.
name: Checkout rubyists/homebrew-tap
uses: actions/checkout@v7
with:
repository: rubyists/homebrew-tap
token: ${{ secrets.RELEASE_PLEASE_TOKEN }}
path: homebrew-tap
-
name: Download this release's SHA256SUMS
env:
GH_TOKEN: ${{ github.token }}
run: |
gh release download "${{ needs.burrito-package.outputs.tag_name }}" \
--repo rubyists/linear-cli --pattern SHA256SUMS --output linear-cli/SHA256SUMS
-
name: Bump the formula's url/sha256 for each platform
run: |
ruby linear-cli/ci/bump_homebrew_formula.rb \
homebrew-tap/Formula/linear-cli/linear-cli.rb \
"${{ needs.burrito-package.outputs.tag_name }}" \
linear-cli/SHA256SUMS
-
# No-op (no PR opened, nothing pushed) when there's nothing to
# commit - same action/behavior already relied on in
# usage-rules-sync.yaml.
name: Open a PR bumping the formula, if anything changed
uses: peter-evans/create-pull-request@v8
with:
token: ${{ secrets.RELEASE_PLEASE_TOKEN }}
path: homebrew-tap
commit-message: "fix: bump linear-cli formula to ${{ needs.burrito-package.outputs.tag_name }}"
title: "fix: bump linear-cli formula to ${{ needs.burrito-package.outputs.tag_name }}"
body: >-
Auto-generated after rubyists/linear-cli's
${{ needs.burrito-package.outputs.tag_name }} release.
branch: "bump-formula-${{ needs.burrito-package.outputs.tag_name }}"
delete-branch: true