-
Notifications
You must be signed in to change notification settings - Fork 5
437 lines (394 loc) · 17.1 KB
/
Copy pathrelease-windows.yml
File metadata and controls
437 lines (394 loc) · 17.1 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
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
name: Release Windows (SignPath)
# Authenticode-signs the Windows release binaries through SignPath Foundation
# and builds the Windows installer (MSI).
#
# SignPath's OSS program only accepts artifacts that originate from a trusted
# build system, and CircleCI is not one, so the Windows binaries are rebuilt
# here with the same Makefile targets and version variables the CircleCI
# `release` job uses (.circleci/config.yml), submitted for signing, and the
# signed results published as additional `*-signed*` release assets. CircleCI
# stays authoritative for the unsigned assets, SHA256SUMS.txt and its cosign
# bundle; see "Code signing policy" in README.md and docs/project/releasing.md.
#
# The MSI (packaging/msi, scripts/build-msi.sh) is built here rather than in
# CircleCI because the WiX toolset only runs on Windows. Without SignPath the
# `msi` job publishes an unsigned opensysml-<x.y.z>-windows-amd64.msi; with
# SignPath the `msi-signed` job rebuilds it from the signed binaries and has
# the MSI itself signed, published as ...-windows-amd64-signed.msi. The
# bundled z3.exe is never signed (SignPath policy).
#
# Until SignPath approves the project and a maintainer configures the
# SIGNPATH_API_TOKEN secret plus the SIGNPATH_* variables, only the build and
# msi jobs run.
on:
push:
tags:
- "v*"
workflow_dispatch:
inputs:
version:
description: Version to stamp into the binaries (VERSION)
required: true
default: v0.0.0-dry-run
submit:
description: Submit the build to SignPath (every request needs an Approver; nothing is published)
type: boolean
default: false
permissions:
contents: read
concurrency:
group: release-windows-${{ github.ref }}
cancel-in-progress: false
env:
UNSIGNED_ARTIFACT: windows-amd64-unsigned
SIGNED_EXE_ARTIFACT: windows-amd64-signed-exe
UNSIGNED_MSI_ARTIFACT: windows-amd64-msi-unsigned
PRESIGN_MSI_ARTIFACT: windows-amd64-msi-presign
WIX_VERSION: 5.0.2
jobs:
build:
name: Build Windows binaries
runs-on: ubuntu-latest
timeout-minutes: 30
outputs:
version: ${{ steps.version.outputs.version }}
artifact-id: ${{ steps.upload.outputs.artifact-id }}
sign: ${{ steps.gate.outputs.sign }}
steps:
- name: Check out repository
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Set up Go
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
with:
go-version-file: go.mod
cache: true
- name: Derive the release version
id: version
env:
REF_TYPE: ${{ github.ref_type }}
REF_NAME: ${{ github.ref_name }}
INPUT_VERSION: ${{ inputs.version }}
run: |
# Tag builds mirror CircleCI's VERSION=${CIRCLE_TAG}; dry runs take the input.
if [ "$REF_TYPE" = "tag" ]; then
VERSION="$REF_NAME"
else
VERSION="$INPUT_VERSION"
fi
case "$VERSION" in
v[0-9]*.[0-9]*.[0-9]*) ;;
*) echo "Error: VERSION '$VERSION' must look like v<major>.<minor>.<patch>[-suffix]"; exit 1 ;;
esac
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- name: Build sysml, sysml-lsp and sysml-grpc for windows/amd64
env:
VERSION: ${{ steps.version.outputs.version }}
COMMIT: ${{ github.sha }}
run: |
# Same derivation as the CircleCI release job, so both builds stamp
# identical -ldflags and VERSIONINFO values for a tag.
BUILD_TIME=$(date -u '+%Y-%m-%d_%H:%M:%S')
GO_VERSION=$(go version | awk '{print $3}')
mkdir -p dist/unsigned
GOOS=windows GOARCH=amd64 make build-sysml VERSION="$VERSION" COMMIT="$COMMIT" BUILD_TIME="$BUILD_TIME" GO_VERSION="$GO_VERSION"
mv bin/sysml dist/unsigned/sysml-windows-amd64.exe
GOOS=windows GOARCH=amd64 make build-lsp VERSION="$VERSION" COMMIT="$COMMIT" BUILD_TIME="$BUILD_TIME" GO_VERSION="$GO_VERSION"
mv bin/sysml-lsp dist/unsigned/sysml-lsp-windows-amd64.exe
GOOS=windows GOARCH=amd64 make build-grpc VERSION="$VERSION" COMMIT="$COMMIT" BUILD_TIME="$BUILD_TIME" GO_VERSION="$GO_VERSION"
mv bin/sysml-grpc dist/unsigned/sysml-grpc-windows-amd64.exe
- name: Check the embedded VERSIONINFO matches VERSION
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
for exe in dist/unsigned/*.exe; do
make windows-versioninfo-check EXE="$exe" VERSION="$VERSION"
done
- name: Upload unsigned binaries
id: upload
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: ${{ env.UNSIGNED_ARTIFACT }}
path: dist/unsigned/*.exe
if-no-files-found: error
retention-days: 14
- name: Decide whether SignPath is configured
id: gate
env:
# Secrets cannot be tested in a job-level `if`, so the decision is
# made here and the sign job keys off the output.
HAS_TOKEN: ${{ secrets.SIGNPATH_API_TOKEN != '' }}
ORGANIZATION_ID: ${{ vars.SIGNPATH_ORGANIZATION_ID }}
PROJECT_SLUG: ${{ vars.SIGNPATH_PROJECT_SLUG }}
POLICY_SLUG: ${{ vars.SIGNPATH_SIGNING_POLICY_SLUG }}
EVENT_NAME: ${{ github.event_name }}
SUBMIT_INPUT: ${{ inputs.submit }}
run: |
sign=true
if [ "$HAS_TOKEN" != "true" ]; then
echo "SIGNPATH_API_TOKEN secret is not set: skipping signing."; sign=false
fi
for v in ORGANIZATION_ID PROJECT_SLUG POLICY_SLUG; do
if [ -z "${!v}" ]; then
echo "SIGNPATH_${v/POLICY_SLUG/SIGNING_POLICY_SLUG} variable is not set: skipping signing."; sign=false
fi
done
if [ "$EVENT_NAME" = "workflow_dispatch" ] && [ "$SUBMIT_INPUT" != "true" ]; then
echo "Dry run without 'submit': skipping signing."; sign=false
fi
echo "sign=$sign" >> "$GITHUB_OUTPUT"
msi:
name: Build the MSI (unsigned)
needs: build
runs-on: windows-latest
timeout-minutes: 30
defaults:
run:
shell: bash
steps:
- name: Check out repository
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Download the unsigned binaries
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: ${{ env.UNSIGNED_ARTIFACT }}
path: dist/unsigned
- name: Install WiX
run: |
dotnet tool install --global wix --version "$WIX_VERSION"
wix --version
- name: Build and validate the MSI
env:
VERSION: ${{ needs.build.outputs.version }}
run: |
scripts/build-msi.sh "$VERSION" dist/unsigned/sysml-windows-amd64.exe dist/unsigned/sysml-lsp-windows-amd64.exe dist/unsigned/sysml-grpc-windows-amd64.exe
msi=$(ls dist/opensysml-*-windows-amd64.msi)
wix msi validate "$msi"
mkdir -p dist/release
mv "$msi" dist/release/
(cd dist/release && sha256sum ./*.msi | sed 's|\./||' | tee SHA256SUMS-windows-msi.txt)
- name: Upload the unsigned MSI as a workflow artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: ${{ env.UNSIGNED_MSI_ARTIFACT }}
path: dist/release/
if-no-files-found: error
retention-days: 14
publish-msi:
# Only when SignPath is not configured: with SignPath the MSI is published
# signed by `publish-signed` instead, never both.
name: Publish the unsigned MSI
needs: [build, msi]
if: github.ref_type == 'tag' && needs.build.outputs.sign != 'true'
runs-on: ubuntu-latest
timeout-minutes: 120
permissions:
contents: write
steps:
- name: Check out repository
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Download the unsigned MSI
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: ${{ env.UNSIGNED_MSI_ARTIFACT }}
path: dist/release
- name: Wait for CircleCI to publish the gated release
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
TAG: ${{ needs.build.outputs.version }}
COMMIT: ${{ github.sha }}
run: scripts/wait-for-release-gate.sh "$TAG" "$COMMIT"
- name: Upload the unsigned MSI to the GitHub Release
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836 # v2.3.3
with:
tag_name: ${{ needs.build.outputs.version }}
files: dist/release/*
fail_on_unmatched_files: true
# Only the MSI assets are touched; the release itself, its notes and
# every CircleCI-published asset are left as they are.
overwrite_files: true
sign:
name: Sign the binaries with SignPath
needs: build
if: needs.build.outputs.sign == 'true'
runs-on: ubuntu-latest
timeout-minutes: 330
outputs:
artifact-id: ${{ steps.upload.outputs.artifact-id }}
steps:
- name: Check out repository
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Set up Go
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
with:
go-version-file: go.mod
cache: true
- name: Wait for CircleCI to publish the gated release
if: github.ref_type == 'tag'
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
TAG: ${{ needs.build.outputs.version }}
COMMIT: ${{ github.sha }}
run: scripts/wait-for-release-gate.sh "$TAG" "$COMMIT"
- name: Submit the signing request to SignPath
id: signpath
uses: signpath/github-action-submit-signing-request@c92b958760219087e01f8d67a1669ed57afe2627 # v2.3
with:
api-token: ${{ secrets.SIGNPATH_API_TOKEN }}
organization-id: ${{ vars.SIGNPATH_ORGANIZATION_ID }}
project-slug: ${{ vars.SIGNPATH_PROJECT_SLUG }}
signing-policy-slug: ${{ vars.SIGNPATH_SIGNING_POLICY_SLUG }}
github-artifact-id: ${{ needs.build.outputs.artifact-id }}
wait-for-completion: true
# Release signing policies require a manual approval, so allow time
# for an Approver to act before the job is failed and needs a re-run.
wait-for-completion-timeout-in-seconds: "14000"
output-artifact-directory: dist/signed
- name: Check the signed binaries still carry VERSION
env:
VERSION: ${{ needs.build.outputs.version }}
run: |
ls -la dist/signed
for name in sysml sysml-lsp sysml-grpc; do
test -f "dist/signed/${name}-windows-amd64.exe" || { echo "Error: signed ${name}-windows-amd64.exe missing"; exit 1; }
done
for exe in dist/signed/*.exe; do
make windows-versioninfo-check EXE="$exe" VERSION="$VERSION"
done
- name: Upload signed binaries
id: upload
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: ${{ env.SIGNED_EXE_ARTIFACT }}
path: dist/signed/*.exe
if-no-files-found: error
retention-days: 14
msi-signed:
name: Build the MSI from the signed binaries and sign it
needs: [build, sign]
runs-on: windows-latest
timeout-minutes: 330
defaults:
run:
shell: bash
outputs:
artifact-id: ${{ steps.upload.outputs.artifact-id }}
steps:
- name: Check out repository
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Check the MSI artifact configuration is configured
env:
MSI_ARTIFACT_CONFIGURATION_SLUG: ${{ vars.SIGNPATH_MSI_ARTIFACT_CONFIGURATION_SLUG }}
run: |
if [ -z "$MSI_ARTIFACT_CONFIGURATION_SLUG" ]; then
echo "Error: SIGNPATH_MSI_ARTIFACT_CONFIGURATION_SLUG variable is not set; see docs/project/releasing.md."
exit 1
fi
- name: Download the signed binaries
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: ${{ env.SIGNED_EXE_ARTIFACT }}
path: dist/signed
- name: Install WiX
run: |
dotnet tool install --global wix --version "$WIX_VERSION"
wix --version
- name: Build and validate the MSI
env:
VERSION: ${{ needs.build.outputs.version }}
run: |
scripts/build-msi.sh "$VERSION" dist/signed/sysml-windows-amd64.exe dist/signed/sysml-lsp-windows-amd64.exe dist/signed/sysml-grpc-windows-amd64.exe
msi=$(ls dist/opensysml-*-windows-amd64.msi)
wix msi validate "$msi"
mkdir -p dist/presign
mv "$msi" dist/presign/
- name: Upload the MSI for signing
id: upload
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: ${{ env.PRESIGN_MSI_ARTIFACT }}
path: dist/presign/*.msi
if-no-files-found: error
retention-days: 14
- name: Submit the MSI signing request to SignPath
# The MSI artifact configuration must sign only the MSI itself: the
# .exe files inside are already signed and z3.exe must stay unsigned.
uses: signpath/github-action-submit-signing-request@c92b958760219087e01f8d67a1669ed57afe2627 # v2.3
with:
api-token: ${{ secrets.SIGNPATH_API_TOKEN }}
organization-id: ${{ vars.SIGNPATH_ORGANIZATION_ID }}
project-slug: ${{ vars.SIGNPATH_PROJECT_SLUG }}
signing-policy-slug: ${{ vars.SIGNPATH_SIGNING_POLICY_SLUG }}
artifact-configuration-slug: ${{ vars.SIGNPATH_MSI_ARTIFACT_CONFIGURATION_SLUG }}
github-artifact-id: ${{ steps.upload.outputs.artifact-id }}
wait-for-completion: true
wait-for-completion-timeout-in-seconds: "14000"
output-artifact-directory: dist/signed-msi
- name: Rename the signed MSI
run: |
ls -la dist/signed-msi
mkdir -p dist/release
for msi in dist/signed-msi/*.msi; do
mv "$msi" "dist/release/$(basename "${msi%.msi}")-signed.msi"
done
ls -la dist/release
- name: Upload the signed MSI
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: windows-amd64-msi-signed
path: dist/release/*.msi
if-no-files-found: error
retention-days: 14
publish-signed:
name: Publish the signed assets
needs: [build, sign, msi-signed]
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: write
steps:
- name: Download the signed binaries
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: ${{ env.SIGNED_EXE_ARTIFACT }}
path: dist/signed
- name: Download the signed MSI
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: windows-amd64-msi-signed
path: dist/release
- name: Package the signed assets
run: |
# Same layouts as the CircleCI assets of the same base name, under a
# `-signed` suffix so the CircleCI-published SHA256SUMS.txt and its
# cosign bundle never describe a file they did not hash.
cd dist/signed
zip -q ../release/sysml-windows-amd64-signed.zip sysml-windows-amd64.exe
zip -q ../release/sysml-lsp-windows-amd64-signed.zip sysml-lsp-windows-amd64.exe
cp sysml-grpc-windows-amd64.exe ../release/sysml-grpc-windows-amd64-signed.exe
mkdir -p stage
cp sysml-windows-amd64.exe stage/sysml.exe
cp sysml-lsp-windows-amd64.exe stage/sysml-lsp.exe
(cd stage && zip -q ../../release/opensysml-windows-amd64-signed.zip sysml.exe sysml-lsp.exe)
rm -rf stage
cd ../release
sha256sum sysml-grpc-windows-amd64-signed.exe > sysml-grpc-windows-amd64-signed.exe.sha256
sha256sum ./*.zip ./*.msi sysml-grpc-windows-amd64-signed.exe | sed 's|\./||' > SHA256SUMS-windows-signed.txt
cat SHA256SUMS-windows-signed.txt
- name: Upload signed assets as a workflow artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: windows-amd64-signed
path: dist/release/
if-no-files-found: error
- name: Upload signed assets to the GitHub Release
if: github.ref_type == 'tag'
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836 # v2.3.3
with:
tag_name: ${{ needs.build.outputs.version }}
files: dist/release/*
fail_on_unmatched_files: true
# Only the `-signed` assets are touched; the release itself, its notes
# and every CircleCI-published asset are left as they are.
overwrite_files: true