-
Notifications
You must be signed in to change notification settings - Fork 0
247 lines (219 loc) · 10.2 KB
/
release.yml
File metadata and controls
247 lines (219 loc) · 10.2 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
name: release
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
tag:
description: 'Tag to release (must already exist). Leave empty for a snapshot dry-run that builds artifacts without creating a GitHub release.'
required: false
permissions:
contents: write
jobs:
build-darwin:
runs-on: macos-latest
steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
ref: ${{ inputs.tag || github.ref }}
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0
with:
go-version-file: go.mod
cache: true
# Load secrets needed for codesigning + notarization from 1Password.
# Expects three items under the "CI" vault:
# - "Developer ID Application" with fields:
# certificate (base64-encoded .p12 contents)
# password (passphrase for the .p12)
# identity (e.g. "Developer ID Application: Foo (TEAMID)")
# - "Developer ID Installer" with fields:
# certificate (base64-encoded .p12 contents)
# password (passphrase for the .p12)
# identity (e.g. "Developer ID Installer: Foo (TEAMID)")
# - "App Store Connect API Key" with fields:
# key_id (10-character key identifier)
# issuer_id (UUID)
# key (contents of the .p8 private key file)
- name: Load Apple signing secrets
uses: 1password/load-secrets-action@92467eb28f72e8255933372f1e0707c567ce2259 # v4
with:
export-env: true
env:
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
APPLE_DEVID_CERT_P12: op://CI/Developer ID Application/certificate
APPLE_DEVID_CERT_PASSWORD: op://CI/Developer ID Application/password
APPLE_DEVID_IDENTITY: op://CI/Developer ID Application/identity
APPLE_INSTALLER_CERT_P12: op://CI/Developer ID Installer/certificate
APPLE_INSTALLER_CERT_PASSWORD: op://CI/Developer ID Installer/password
APPLE_INSTALLER_IDENTITY: op://CI/Developer ID Installer/identity
APPLE_API_KEY_ID: op://CI/App Store Connect API Key/key_id
APPLE_API_ISSUER_ID: op://CI/App Store Connect API Key/issuer_id
APPLE_API_KEY: op://CI/App Store Connect API Key/key
- name: Import Developer ID certificates into temporary keychain
run: |
set -eu
KEYCHAIN_PATH="$RUNNER_TEMP/build.keychain"
KEYCHAIN_PASSWORD=$(openssl rand -hex 32)
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
# Grant every code-signing tool we use (and a few that codesign
# may shell out to internally) access to the imported keys. Using
# one -T per tool instead of -A keeps the ACL narrow but still
# avoids the GUI "Allow access?" prompt that would otherwise
# deadlock pkgbuild on a headless runner.
import_cert() {
local b64="$1" pw="$2" path="$RUNNER_TEMP/cert.p12"
printf '%s' "$b64" | base64 --decode > "$path"
security import "$path" -k "$KEYCHAIN_PATH" -P "$pw" \
-T /usr/bin/codesign \
-T /usr/bin/pkgbuild \
-T /usr/bin/productbuild \
-T /usr/bin/productsign \
-T /usr/bin/security
rm "$path"
}
import_cert "$APPLE_DEVID_CERT_P12" "$APPLE_DEVID_CERT_PASSWORD"
import_cert "$APPLE_INSTALLER_CERT_P12" "$APPLE_INSTALLER_CERT_PASSWORD"
# shellcheck disable=SC2046 # intentional word-splitting: each existing keychain path becomes a separate arg
security list-keychains -d user -s "$KEYCHAIN_PATH" $(security list-keychains -d user | tr -d '"')
security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" >/dev/null
- name: Run GoReleaser (darwin)
uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0
with:
distribution: goreleaser
version: '~> v2'
args: release --config .goreleaser-darwin.yml --clean ${{ github.event_name == 'workflow_dispatch' && inputs.tag == '' && '--snapshot' || '' }}
# Build the .pkg from the GoReleaser-produced (signed) universal binary,
# sign with the Developer ID Installer identity, submit to Apple's notary
# service, and staple the ticket. The .pkg notarization also covers the
# binary it contains, so a separate bare-binary notarization is not
# needed — Gatekeeper's online check finds the binary's signature hash
# via Apple's notary records.
- name: Build, sign, and notarize macOS installer (.pkg)
run: |
set -eu
ARCHIVE=$(ls dist/osquery-extension_*_darwin_all.tar.gz)
VERSION=$(basename "$ARCHIVE" | sed -E 's/^osquery-extension_(.+)_darwin_all\.tar\.gz$/\1/')
PKG_NAME="osquery-extension_${VERSION}_darwin.pkg"
PKG_PATH="dist/${PKG_NAME}"
# Stage the install payload at the layout it should land in on disk.
PKG_ROOT="$RUNNER_TEMP/pkg-root"
rm -rf "$PKG_ROOT"
mkdir -p "$PKG_ROOT/usr/local/zentral/osquery/extensions"
tar -xzf "$ARCHIVE" -C "$PKG_ROOT/usr/local/zentral/osquery/extensions" osquery-extension.ext
install -m 0644 pkg/extensions.load "$PKG_ROOT/usr/local/zentral/osquery/extensions.load"
# Sanity-check the binary's signature before packing it.
codesign --verify --strict --verbose=2 \
"$PKG_ROOT/usr/local/zentral/osquery/extensions/osquery-extension.ext"
# Two-stage build to produce a distribution package (the format
# Installer.app and Gatekeeper expect for redistributable .pkg
# files) rather than a bare component package:
# 1. pkgbuild emits an unsigned component .pkg with our payload.
# 2. productbuild --synthesize derives a Distribution.xml from
# that component, capturing identifier + version.
# 3. productbuild --distribution wraps the component, signs the
# result with the Installer identity, and writes the final
# .pkg we ship.
COMPONENT_PKG="$RUNNER_TEMP/component.pkg"
DISTRIBUTION_XML="$RUNNER_TEMP/distribution.xml"
pkgbuild \
--root "$PKG_ROOT" \
--identifier com.zentral.osquery-extension \
--version "$VERSION" \
--install-location / \
--scripts pkg/scripts \
"$COMPONENT_PKG"
rm -rf "$PKG_ROOT"
productbuild --synthesize \
--package "$COMPONENT_PKG" \
"$DISTRIBUTION_XML"
productbuild \
--distribution "$DISTRIBUTION_XML" \
--package-path "$RUNNER_TEMP" \
--sign "$APPLE_INSTALLER_IDENTITY" \
"$PKG_PATH"
rm -f "$COMPONENT_PKG" "$DISTRIBUTION_XML"
# Submit to the notary service.
KEY_PATH="$RUNNER_TEMP/AuthKey.p8"
printf '%s' "$APPLE_API_KEY" > "$KEY_PATH"
xcrun notarytool submit "$PKG_PATH" \
--key "$KEY_PATH" \
--key-id "$APPLE_API_KEY_ID" \
--issuer "$APPLE_API_ISSUER_ID" \
--wait
rm -f "$KEY_PATH"
# Staple the ticket onto the .pkg so it installs offline.
xcrun stapler staple "$PKG_PATH"
xcrun stapler validate "$PKG_PATH"
- name: Upload darwin artifacts
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: dist-darwin
path: |
dist/*.tar.gz
dist/*.pkg
if-no-files-found: error
build-others:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
ref: ${{ inputs.tag || github.ref }}
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0
with:
go-version-file: go.mod
cache: true
- name: Run GoReleaser (linux+windows)
uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0
with:
distribution: goreleaser
version: '~> v2'
args: release --config .goreleaser-others.yml --clean ${{ github.event_name == 'workflow_dispatch' && inputs.tag == '' && '--snapshot' || '' }}
- name: Upload linux+windows artifacts
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: dist-others
path: |
dist/*.tar.gz
dist/*.zip
if-no-files-found: error
release:
needs: [build-darwin, build-others]
if: github.event_name == 'push' || inputs.tag != ''
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
ref: ${{ inputs.tag || github.ref }}
fetch-depth: 0
- name: Download darwin artifacts
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: dist-darwin
path: release-artifacts
- name: Download linux+windows artifacts
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: dist-others
path: release-artifacts
- name: Generate combined checksums
working-directory: release-artifacts
run: sha256sum -- *.tar.gz *.zip *.pkg > checksums.txt
- name: Create GitHub release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ inputs.tag || github.ref_name }}
run: |
gh release create "$TAG" \
--repo "${{ github.repository }}" \
--generate-notes \
release-artifacts/*