Repository navigation
147 lines (134 loc) · 5.51 KB
/
Copy pathrelease.yml
File metadata and controls
147 lines (134 loc) · 5.51 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
# Builds rb.exe from a version tag, signs it, and attaches the binaries
# and their SHA-256 checksums to a GitHub release. Signing is described
# in docs/code-signing.md; when the signing credentials are not
# configured, the release is created as an unsigned draft instead.
#
# A workflow_dispatch run is a signing rehearsal: it builds, signs and
# verifies exactly like a release, then uploads the signed binaries as
# a workflow artifact instead of creating a GitHub release. Use it to
# exercise the SignPath pipeline (for example against a test
# certificate) without cutting a release.
name: Release
on:
push:
tags: ['v*']
workflow_dispatch:
permissions:
contents: read
jobs:
build:
runs-on: windows-latest
timeout-minutes: 30
outputs:
artifact-id: ${{ steps.upload.outputs.artifact-id }}
version: ${{ steps.version.outputs.version }}
steps:
- uses: actions/checkout@v5
- uses: actions/setup-dotnet@v4
with:
global-json-file: global.json
- name: Derive the version from the tag
id: version
shell: pwsh
run: |
if ($env:GITHUB_REF_TYPE -eq 'tag') {
# Anchored, so a tag that is not a plain release version
# never reaches the interpolations below. Git refnames may
# contain quotes, so an unvalidated name could break out of
# a quoted pwsh string.
if ($env:GITHUB_REF_NAME -notmatch '^v[0-9]+\.[0-9]+\.[0-9]+(-[a-z0-9.]+)?$') {
Write-Output "::error::tag is not a release version tag"
exit 1
}
$version = $env:GITHUB_REF_NAME.Substring(1)
} else {
# Rehearsal build from a branch; the version only labels the
# workflow artifact.
$version = '0.0.0-rehearsal'
}
"version=$version" >> $env:GITHUB_OUTPUT
- run: dotnet test rbmanager.sln
# win-arm64 cross-compiles on the x64 runner; the hosted image
# carries the ARM64 MSVC tools.
- name: Publish rb.exe for win-x64 and win-arm64
shell: pwsh
run: |
foreach ($rid in 'win-x64', 'win-arm64') {
dotnet publish src/rbmanager -r $rid -c Release `
-p:Version=${{ steps.version.outputs.version }} `
-o artifacts/publish/$rid
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
}
New-Item -ItemType Directory unsigned | Out-Null
Copy-Item artifacts/publish/win-x64/rb.exe unsigned/rb-x64.exe
Copy-Item artifacts/publish/win-arm64/rb.exe unsigned/rb-arm64.exe
- name: Upload the unsigned binaries
id: upload
uses: actions/upload-artifact@v7
with:
name: rb-unsigned
path: unsigned/
if-no-files-found: error
release:
needs: build
runs-on: windows-latest
timeout-minutes: 90
# The environment holds the signing credentials and can require a
# manual approval before the job starts.
environment: release-signing
permissions:
contents: write # create the release
actions: read # let the signing provider download the artifact
steps:
# The checkout only provides ./.github/actions/sign; the binaries
# come from the build job's artifact.
- uses: actions/checkout@v5
- name: Sign rb.exe
id: sign
uses: ./.github/actions/sign
with:
github-artifact-id: ${{ needs.build.outputs.artifact-id }}
github-artifact-name: rb-unsigned
output-directory: signed
signpath-api-token: ${{ secrets.SIGNPATH_API_TOKEN }}
signpath-organization-id: ${{ vars.SIGNPATH_ORGANIZATION_ID }}
signpath-project-slug: ${{ vars.SIGNPATH_PROJECT_SLUG }}
signpath-signing-policy-slug: ${{ vars.SIGNPATH_SIGNING_POLICY_SLUG }}
signpath-artifact-configuration-slug: ${{ vars.SIGNPATH_ARTIFACT_CONFIGURATION_SLUG }}
certificate-thumbprint: ${{ vars.SIGNPATH_CERTIFICATE_THUMBPRINT }}
# Set SIGNPATH_TEST_CERTIFICATE to true while SignPath issues
# a test certificate, which no machine trusts.
require-trusted-chain: ${{ vars.SIGNPATH_TEST_CERTIFICATE != 'true' }}
- name: Generate the SHA-256 checksums
shell: pwsh
run: |
foreach ($exe in Get-ChildItem signed -Filter *.exe) {
$hash = (Get-FileHash $exe.FullName -Algorithm SHA256).Hash.ToLowerInvariant()
"$hash *$($exe.Name)" | Out-File -Encoding ascii "$($exe.FullName).sha256"
}
- name: Create the GitHub release
if: github.ref_type == 'tag'
shell: pwsh
env:
GH_TOKEN: ${{ github.token }}
VERSION: ${{ needs.build.outputs.version }}
SIGNED: ${{ steps.sign.outputs.signed }}
run: |
$ghArgs = @('release', 'create', $env:GITHUB_REF_NAME,
'--title', "rbmanager $env:VERSION",
'--generate-notes', '--verify-tag')
if ($env:SIGNED -ne 'true') {
# Never publish unsigned binaries silently; leave the
# decision to a human.
$ghArgs += '--draft'
Write-Output "::warning::Created a DRAFT release with unsigned binaries."
}
$ghArgs += Get-ChildItem signed -File | ForEach-Object FullName
gh @ghArgs
- name: Upload the rehearsal binaries
if: github.ref_type != 'tag'
uses: actions/upload-artifact@v7
with:
name: rb-signed-rehearsal
path: signed/
if-no-files-found: error