Skip to content

Commit c220990

Browse files
committed
chore: build, sign and notarize darwin binaries on linux
1 parent cfb914c commit c220990

File tree

6 files changed

+155
-223
lines changed

6 files changed

+155
-223
lines changed

.github/workflows/release.yaml

Lines changed: 33 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,4 @@
11
# GitHub release workflow.
2-
#
3-
# This workflow is a bit complicated because we have to build darwin binaries on
4-
# a mac runner, but the mac runners are extremely slow. So instead of running
5-
# the entire release on a mac (which will take an hour to run), we run only the
6-
# mac build on a mac, and the rest on a linux runner. The final release is then
7-
# published using a final linux runner.
82
name: release
93
on:
104
push:
@@ -25,7 +19,7 @@ env:
2519
CODER_RELEASE: ${{ github.event.inputs.snapshot && 'false' || 'true' }}
2620

2721
jobs:
28-
linux-windows:
22+
release:
2923
runs-on: ubuntu-latest
3024
env:
3125
# Necessary for Docker manifest
@@ -71,15 +65,36 @@ jobs:
7165
- name: Install zstd
7266
run: sudo apt-get install -y zstd
7367

74-
- name: Build Linux and Windows Binaries
68+
- name: Setup Apple Developer certificate and API key
69+
run: |
70+
set -euo pipefail
71+
touch /tmp/{apple_cert.p12,apple_cert_password.txt,apple_apikey.p8}
72+
chmod 600 /tmp/{apple_cert.p12,apple_cert_password.txt,apple_apikey.p8}
73+
echo "$AC_CERTIFICATE_BASE64" | base64 -d > /tmp/apple_cert.p12
74+
echo "$AC_CERTIFICATE_PASSWORD" > /tmp/apple_cert_password.txt
75+
echo "$AC_APIKEY_BASE64" | base64 -d > /tmp/apple_apikey.p8
76+
env:
77+
AC_CERTIFICATE_BASE64: ${{ secrets.AC_CERTIFICATE_P12_BASE64 }}
78+
AC_CERTIFICATE_PASSWORD: ${{ secrets.AC_CERTIFICATE_PASSWORD }}
79+
AC_APIKEY_BASE64: ${{ secrets.AC_APIKEY_PEM_BASE64 }}
80+
81+
- name: Build binaries
7582
run: |
7683
set -euo pipefail
7784
go mod download
7885
7986
version="$(./scripts/version.sh)"
8087
make -j \
8188
build/coder_"$version"_linux_{amd64,armv7,arm64}.{tar.gz,apk,deb,rpm} \
82-
build/coder_"$version"_windows_{amd64,arm64}.zip \
89+
build/coder_"$version"_{darwin,windows}_{amd64,arm64}.zip \
90+
build/coder_helm_"$version".tgz
91+
env:
92+
CODER_SIGN_DARWIN: "1"
93+
AC_CERTIFICATE_FILE: /tmp/apple_cert.p12
94+
AC_CERTIFICATE_PASSWORD_FILE: /tmp/apple_cert_password.txt
95+
AC_APIKEY_ISSUER_ID: ${{ secrets.AC_APIKEY_ISSUER_ID }}
96+
AC_APIKEY_ID: ${{ secrets.AC_APIKEY_ID }}
97+
AC_APIKEY_FILE: /tmp/apple_apikey.p8
8398

8499
- name: Build Linux Docker images
85100
run: |
@@ -115,154 +130,18 @@ jobs:
115130
"${images[@]}"
116131
fi
117132
118-
- name: Upload binary artifacts
119-
uses: actions/upload-artifact@v3
120-
with:
121-
name: linux
122-
path: |
123-
./build/*.zip
124-
./build/*.tar.gz
125-
./build/*.apk
126-
./build/*.deb
127-
./build/*.rpm
128-
129-
# The mac binaries get built on mac runners because they need to be signed,
130-
# and the signing tool only runs on mac. This darwin job only builds the Mac
131-
# binaries and uploads them as job artifacts used by the publish step.
132-
darwin:
133-
runs-on: macos-latest
134-
steps:
135-
- uses: actions/checkout@v3
136-
with:
137-
fetch-depth: 0
138-
139-
# If the event that triggered the build was an annotated tag (which our
140-
# tags are supposed to be), actions/checkout has a bug where the tag in
141-
# question is only a lightweight tag and not a full annotated tag. This
142-
# command seems to fix it.
143-
# https://github.com/actions/checkout/issues/290
144-
- name: Fetch git tags
145-
run: git fetch --tags --force
146-
147-
- uses: actions/setup-go@v3
148-
with:
149-
go-version: "~1.19"
150-
151-
- name: Import Signing Certificates
152-
uses: Apple-Actions/import-codesign-certs@v1
153-
with:
154-
p12-file-base64: ${{ secrets.AC_CERTIFICATE_P12_BASE64 }}
155-
p12-password: ${{ secrets.AC_CERTIFICATE_PASSWORD }}
156-
157-
- name: Cache Node
158-
id: cache-node
159-
uses: actions/cache@v3
160-
with:
161-
path: |
162-
**/node_modules
163-
.eslintcache
164-
key: js-${{ runner.os }}-test-${{ hashFiles('**/yarn.lock') }}
165-
restore-keys: |
166-
js-${{ runner.os }}-
133+
- name: ls build
134+
run: ls build
167135

168-
- name: Install dependencies
169-
run: |
170-
set -euo pipefail
171-
# The version of bash that macOS ships with is too old
172-
brew install bash
173-
174-
# The version of make that macOS ships with is too old
175-
brew install make
176-
echo "$(brew --prefix)/opt/make/libexec/gnubin" >> $GITHUB_PATH
177-
178-
# BSD getopt is incompatible with the build scripts
179-
brew install gnu-getopt
180-
echo "$(brew --prefix)/opt/gnu-getopt/bin" >> $GITHUB_PATH
181-
182-
# Used for notarizing the binaries
183-
brew tap mitchellh/gon
184-
brew install mitchellh/gon/gon
185-
186-
# Used for compressing embedded slim binaries
187-
brew install zstd
188-
189-
- name: Build Site
190-
run: make site/out/index.html
191-
192-
- name: Build darwin Binaries (with signatures)
193-
run: |
194-
set -euo pipefail
195-
go mod download
196-
197-
version="$(./scripts/version.sh)"
198-
make -j \
199-
build/coder_"$version"_darwin_{amd64,arm64}.zip
200-
env:
201-
CODER_SIGN_DARWIN: "1"
202-
AC_USERNAME: ${{ secrets.AC_USERNAME }}
203-
AC_PASSWORD: ${{ secrets.AC_PASSWORD }}
204-
AC_APPLICATION_IDENTITY: BDB050EB749EDD6A80C6F119BF1382ECA119CCCC
205-
206-
- name: Upload Binary Artifacts
207-
uses: actions/upload-artifact@v3
208-
with:
209-
name: darwin
210-
path: ./build/*.zip
211-
212-
publish:
213-
runs-on: ubuntu-latest
214-
needs:
215-
- linux-windows
216-
- darwin
217-
steps:
218-
- uses: actions/checkout@v3
219-
with:
220-
fetch-depth: 0
221-
222-
# If the event that triggered the build was an annotated tag (which our
223-
# tags are supposed to be), actions/checkout has a bug where the tag in
224-
# question is only a lightweight tag and not a full annotated tag. This
225-
# command seems to fix it.
226-
# https://github.com/actions/checkout/issues/290
227-
- name: Fetch git tags
228-
run: git fetch --tags --force
229-
230-
- name: mkdir artifacts
231-
run: mkdir artifacts
232-
233-
- name: Download darwin Artifacts
234-
uses: actions/download-artifact@v3
235-
with:
236-
name: darwin
237-
path: artifacts
238-
239-
- name: Download Linux and Windows Artifacts
240-
uses: actions/download-artifact@v3
241-
with:
242-
name: linux
243-
path: artifacts
244-
245-
- name: ls artifacts
246-
run: ls artifacts
247-
248-
- name: Publish Helm
249-
run: |
250-
set -euxo pipefail
251-
252-
version="$(./scripts/version.sh)"
253-
make -j \
254-
build/coder_helm_"$version".tgz
255-
mv ./build/*.tgz ./artifacts/
256-
257-
- name: Publish Release
136+
- name: Publish release
258137
run: |
259138
./scripts/publish_release.sh \
260139
${{ (github.event.inputs.dry_run || github.event.inputs.snapshot) && '--dry-run' }} \
261-
./artifacts/*.zip \
262-
./artifacts/*.tar.gz \
263-
./artifacts/*.tgz \
264-
./artifacts/*.apk \
265-
./artifacts/*.deb \
266-
./artifacts/*.rpm
140+
./build/*.zip \
141+
./build/*.tar.gz \
142+
./build/*.tgz \
143+
./build/*.apk \
144+
./build/*.deb \
145+
./build/*.rpm
267146
env:
268147
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

scripts/archive.sh

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,9 @@
1010
# If the --output parameter is not set, the default output path is the binary
1111
# path (minus any .exe suffix) plus the format extension ".zip" or ".tar.gz".
1212
#
13-
# If --sign-darwin is specified, the zip file is signed with the `codesign`
14-
# utility and then notarized using the `gon` utility, which may take a while.
15-
# $AC_APPLICATION_IDENTITY must be set and the signing certificate must be
16-
# imported for this to work. Also, the input binary must already be signed with
17-
# the `codesign` tool.
13+
# If --sign-darwin is specified, the zip file will be notarized using
14+
# ./notarize_darwin.sh, which may take a while. Read that file for more details
15+
# on the requirements.
1816
#
1917
# If the --agpl parameter is specified, only the AGPL license is included in the
2018
# outputted archive.
@@ -139,7 +137,7 @@ rm -rf "$temp_dir"
139137

140138
if [[ "$sign_darwin" == 1 ]]; then
141139
log "Notarizing archive..."
142-
execrelative ./sign_darwin.sh "$output_path"
140+
execrelative ./notarize_darwin.sh "$output_path"
143141
fi
144142

145143
echo "$output_path"

scripts/build_go.sh

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@
1616
# builds) and the absolute path to the binary will be printed to stdout on
1717
# completion.
1818
#
19-
# If the --sign-darwin parameter is specified and the OS is darwin, binaries
20-
# will be signed using the `codesign` utility. $AC_APPLICATION_IDENTITY must be
21-
# set and the signing certificate must be imported for this to work.
19+
# If the --sign-darwin parameter is specified and the OS is darwin, the output
20+
# binary will be signed using ./sign_darwin.sh. Read that file for more details
21+
# on the requirements.
2222
#
2323
# If the --agpl parameter is specified, builds only the AGPL-licensed code (no
2424
# Coder enterprise features).
@@ -65,9 +65,6 @@ while true; do
6565
shift
6666
;;
6767
--sign-darwin)
68-
if [[ "${AC_APPLICATION_IDENTITY:-}" == "" ]]; then
69-
error "AC_APPLICATION_IDENTITY must be set when --sign-darwin is supplied"
70-
fi
7168
sign_darwin=1
7269
shift
7370
;;
@@ -133,13 +130,7 @@ CGO_ENABLED=0 GOOS="$os" GOARCH="$arch" GOARM="$arm_version" go build \
133130
"$cmd_path" 1>&2
134131

135132
if [[ "$sign_darwin" == 1 ]] && [[ "$os" == "darwin" ]]; then
136-
codesign \
137-
-f -v \
138-
-s "$AC_APPLICATION_IDENTITY" \
139-
--timestamp \
140-
--options runtime \
141-
"$output_path" \
142-
1>&2
133+
execrelative ./scripts/sign_darwin.sh "$output_path" 1>&2
143134
fi
144135

145136
echo "$output_path"

scripts/lib.sh

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,21 @@ dependencies() {
8181
fi
8282
}
8383

84+
requiredenvs() {
85+
local fail=0
86+
for env in "$@"; do
87+
if [[ "${!env:-}" == "" ]]; then
88+
log "ERROR: The '$env' environment variable is required, but is not set."
89+
fail=1
90+
fi
91+
done
92+
93+
if [[ "$fail" == 1 ]]; then
94+
log
95+
error "One or more required environment variables are not set, check above log output for more details."
96+
fi
97+
}
98+
8499
# maybedryrun prints the given program and flags, and then, if the first
85100
# argument is 0, executes it. The reason the first argument should be 0 is that
86101
# it is expected that you have a dry_run variable in your script that is set to

scripts/notarize_darwin.sh

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#!/usr/bin/env bash
2+
3+
# This script notarizes the provided zip file using an Apple Developer account.
4+
#
5+
# Usage: ./notarize_darwin.sh path/to/zipfile.zip
6+
#
7+
# The provided zip file must contain a coder binary that has already been signed
8+
# using ./sign_darwin.sh.
9+
#
10+
# On success, all of the contained binaries inside the input zip file will
11+
# notarized. This does not make any changes to the zip or contained files
12+
# itself, but GateKeeper checks will pass for the binaries inside the zip file
13+
# as long as the device is connected to the internet to download the
14+
# notarization ticket from Apple.
15+
#
16+
# You can check if a binary is notarized by running the following command on a
17+
# Mac:
18+
# spctl --assess -vvv -t install path/to/binary
19+
#
20+
# Depends on the rcodesign utility. Requires the following environment variables
21+
# to be set:
22+
# - $AC_APIKEY_ISSUER_ID: The issuer UUID of the Apple App Store Connect API
23+
# key.
24+
# - $AC_APIKEY_ID: The key ID of the Apple App Store Connect API key.
25+
# - $AC_APIKEY_FILE: The path to the private key P8 file of the Apple App Store
26+
# Connect API key.
27+
28+
set -euo pipefail
29+
# shellcheck source=scripts/lib.sh
30+
source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
31+
32+
# Check dependencies
33+
dependencies rcodesign
34+
requiredenvs AC_APIKEY_ISSUER_ID AC_KEY_ID AC_APIKEY_FILE
35+
36+
# Encode the notarization key components into a JSON file for easily calling
37+
# `rcodesign notary-submit`.
38+
key_file="$(mktemp)"
39+
chmod 600 "$key_file"
40+
trap 'rm -f "$key_file"' EXIT
41+
rcodesign encode-app-store-connect-api-key \
42+
"$AC_APIKEY_ISSUER_ID" \
43+
"$AC_KEY_ID" \
44+
"$AC_APIKEY_FILE" \
45+
> "$key_file"
46+
47+
# The notarization process is very fragile and heavily dependent on Apple's
48+
# notarization server not returning server errors, so we retry this step twice
49+
# with a delay of 30 seconds between attempts.
50+
rc=0
51+
for i in $(seq 1 2); do
52+
rcodesign notary-submit \
53+
-vvv \
54+
--key "$key_file" \
55+
--wait "$@" \
56+
1>&2 \
57+
&& rc=0 && break || rc=$?
58+
59+
log "rcodesign exit code: $rc"
60+
if [[ $i -lt 5 ]]; then
61+
log
62+
log "Retrying notarization in 30 seconds"
63+
log
64+
sleep 30
65+
else
66+
log
67+
log "Giving up :("
68+
fi
69+
done
70+
71+
exit $rc

0 commit comments

Comments
 (0)