-
Notifications
You must be signed in to change notification settings - Fork 0
282 lines (254 loc) · 11.7 KB
/
Copy pathrelease.yml
File metadata and controls
282 lines (254 loc) · 11.7 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
# Publishing waits for same-commit gates and native/WSL psmux proof before
# nuget.org grants a token for its immutable feed.
#
# The name of this file is part of the trusted publishing policy on nuget.org.
# Renaming it means editing that policy, and until then no publish is possible.
name: release
on:
push:
tags: ['v*']
workflow_dispatch:
permissions:
contents: read
concurrency:
group: release
cancel-in-progress: false
env:
DOTNET_NOLOGO: 'true'
DOTNET_CLI_TELEMETRY_OPTOUT: 'true'
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 'true'
jobs:
validate:
name: validate release ref
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: global.json
- name: Check the tag matches the version
env:
REF_TYPE: ${{ github.ref_type }}
TAG: ${{ github.ref_name }}
run: |
set -euo pipefail
if [ "${REF_TYPE}" != tag ] || [[ "${TAG}" != v* ]]; then
echo "release must run at a v* tag, not ${REF_TYPE} ${TAG}" >&2
exit 1
fi
version="$(dotnet msbuild src/LibTmux/LibTmux.csproj \
-getProperty:Version -verbosity:quiet | tr -d '[:space:]')"
if [ "${TAG}" != "v${version}" ]; then
echo "tag ${TAG} does not match version ${version}" >&2
exit 1
fi
echo "validated ${TAG} at ${GITHUB_SHA}"
dotnet:
name: full dotnet gate
needs: validate
uses: ./.github/workflows/dotnet.yml
compatibility:
name: supported tmux matrix
needs: validate
uses: ./.github/workflows/dotnet-tmux.yml
psmux-metadata:
name: published psmux artifact
needs: validate
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
# psmux ships the client as a published release asset, so this fetches
# what anyone else can fetch rather than a build only a maintainer holds.
# The archive and the client inside it are both pinned: the archive hash
# proves the download, and the client hash is the one the library will
# accept at launch.
- name: Verify the published psmux release
env:
ARTIFACT_URL: https://github.com/psmux/psmux/releases/download/v3.3.8/psmux-v3.3.8-windows-x64.zip
ARCHIVE_SHA256: 1ad127ba937194a890b933a73d9b023e297bd73dc742abd841bf159984c2effe
CLIENT_SHA256: 54e5c54db259218348f966b5d0d0b5153fdef6350074855ea9ce627d20537b0d
SOURCE_URL: https://github.com/psmux/psmux/commit/66cf61354c473b35d4f0c06c57384fc46d61ffdb
run: |
set -euo pipefail
curl --fail --location --proto '=https' --tlsv1.2 \
--connect-timeout 15 --max-time 300 \
"${ARTIFACT_URL}" --output "${RUNNER_TEMP}/psmux.zip"
echo "${ARCHIVE_SHA256} ${RUNNER_TEMP}/psmux.zip" \
| sha256sum --check --strict
unzip -q -o "${RUNNER_TEMP}/psmux.zip" -d "${RUNNER_TEMP}/psmux"
echo "${CLIENT_SHA256} ${RUNNER_TEMP}/psmux/psmux.exe" \
| sha256sum --check --strict
# The archive carries psmux's licence, so nothing has to host a copy.
test -s "${RUNNER_TEMP}/psmux/LICENSE"
curl --fail --location --proto '=https' --tlsv1.2 \
--connect-timeout 15 --max-time 120 \
"${SOURCE_URL}" --output "${RUNNER_TEMP}/psmux-source-provenance"
test -s "${RUNNER_TEMP}/psmux-source-provenance"
psmux:
name: psmux native Windows and WSL
needs: [validate, psmux-metadata]
runs-on: [self-hosted, Windows, X64, psmux]
# The gate restores into a throwaway NuGet cache on purpose, so every run
# pays a full cold restore. That is the point of the isolation, and on a
# self-hosted Windows machine it is slower than the hosted equivalent.
timeout-minutes: 90
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
# global.json pins an exact SDK, so this runner installs it rather than
# using whatever Windows already has. A self-hosted runner is an ordinary
# user account that cannot write to C:\Program Files\dotnet, so the
# install goes to the runner's own tool cache, which also survives runs.
- uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
env:
DOTNET_INSTALL_DIR: ${{ runner.tool_cache }}\dotnet
with:
global-json-file: global.json
# The smoke runs the net8.0 assemblies as well, and global.json only
# brings the pinned 10.x SDK and its runtime. A hosted image happens to
# carry .NET 8 already; an isolated install directory does not, so the
# job installs it rather than depending on what the machine has.
- uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
env:
DOTNET_INSTALL_DIR: ${{ runner.tool_cache }}\dotnet
with:
dotnet-version: '8.0'
- name: Download the audited psmux release
shell: pwsh
env:
ARTIFACT_URL: https://github.com/psmux/psmux/releases/download/v3.3.8/psmux-v3.3.8-windows-x64.zip
CLIENT_SHA256: 54e5c54db259218348f966b5d0d0b5153fdef6350074855ea9ce627d20537b0d
run: |
$ErrorActionPreference = 'Stop'
$archive = Join-Path $env:RUNNER_TEMP 'psmux.zip'
$extracted = Join-Path $env:RUNNER_TEMP 'psmux-release'
Invoke-WebRequest -Uri $env:ARTIFACT_URL -OutFile $archive
Expand-Archive -Path $archive -DestinationPath $extracted -Force
$client = Join-Path $extracted 'psmux.exe'
$actual = (Get-FileHash -Path $client -Algorithm SHA256).Hash.ToLowerInvariant()
if ($actual -ne $env:CLIENT_SHA256) {
throw "psmux.exe is $actual rather than the audited $env:CLIENT_SHA256."
}
Copy-Item -Path $client -Destination (Join-Path $env:RUNNER_TEMP 'psmux.exe') -Force
- name: Build the native and packed consumers
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$env:NUGET_PACKAGES = Join-Path `
$env:RUNNER_TEMP `
"libtmux-psmux-nuget-$([Guid]::NewGuid().ToString('N'))"
dotnet restore LibTmux.slnx --locked-mode
dotnet build LibTmux.slnx `
--configuration Release `
--no-restore `
--warnaserror
dotnet pack LibTmux.slnx `
--configuration Release `
--no-build `
--output artifacts/packages
dotnet restore tests/LibTmux.PackageConsumer/LibTmux.PackageConsumer.csproj
foreach ($framework in @('net8.0', 'net10.0')) {
dotnet build tests/LibTmux.PackageConsumer/LibTmux.PackageConsumer.csproj `
--configuration Release `
--framework $framework `
--no-restore `
--warnaserror
}
- name: Run the native and WSL psmux harness
shell: pwsh
env:
WSL_DISTRIBUTION: ${{ vars.PSMUX_WSL_DISTRIBUTION }}
WSL_DOTNET_PATH: ${{ vars.PSMUX_WSL_DOTNET_PATH }}
run: |
$ErrorActionPreference = 'Stop'
if ([string]::IsNullOrWhiteSpace($env:WSL_DISTRIBUTION)) {
throw 'PSMUX_WSL_DISTRIBUTION must name the release runner distribution.'
}
if ([string]::IsNullOrWhiteSpace($env:WSL_DOTNET_PATH)) {
throw 'PSMUX_WSL_DOTNET_PATH must name the WSL dotnet executable.'
}
$dotnet = (Get-Command dotnet.exe -ErrorAction Stop).Source
foreach ($framework in @('net8.0', 'net10.0')) {
$nonce = [Guid]::NewGuid().ToString('N')
& .\eng\psmux\Invoke-PsmuxSmoke.ps1 `
-PsmuxPath (Join-Path $env:RUNNER_TEMP 'psmux.exe') `
-ExpectedSha256 '54e5c54db259218348f966b5d0d0b5153fdef6350074855ea9ce627d20537b0d' `
-DataDirectory (Join-Path $env:RUNNER_TEMP "libtmux-psmux-$nonce") `
-NamespaceName "libtmux_smoke_$($nonce.Substring(0, 16))" `
-DotnetPath $dotnet `
-TestAssembly (Join-Path $env:GITHUB_WORKSPACE "tests\LibTmux.UnitTests\bin\Release\$framework\LibTmux.UnitTests.dll") `
-ExampleAssembly (Join-Path $env:GITHUB_WORKSPACE "examples\LibTmux.Examples\bin\Release\$framework\LibTmux.Examples.dll") `
-PackageConsumerAssembly (Join-Path $env:GITHUB_WORKSPACE "tests\LibTmux.PackageConsumer\bin\Release\$framework\LibTmux.PackageConsumer.dll") `
-TargetFramework $framework `
-RunWslSmoke `
-WslDistribution $env:WSL_DISTRIBUTION `
-WslRepository $env:GITHUB_WORKSPACE `
-WslDotnetPath $env:WSL_DOTNET_PATH
}
publish:
name: publish to nuget.org
needs: [dotnet, compatibility, psmux]
runs-on: ubuntu-latest
timeout-minutes: 15
# The policy on nuget.org names this environment, so a workflow that ran
# outside it cannot exchange a token. It is also where a required reviewer
# and a tag restriction belong.
environment: nuget
permissions:
contents: read
# Lets the job ask GitHub for the short-lived token nuget.org trades for
# a one-hour API key. Nothing else here needs a credential.
id-token: write
attestations: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# SourceLink points a debugger at the commit that produced the
# assembly, which a shallow checkout does not have.
fetch-depth: 0
- uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: global.json
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
- name: Download the packages proved by the gate
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: packages
path: artifacts/packages
- name: Inspect the packages
run: uv run python eng/parity/inspect_packages.py
- name: NuGet login
# The key this returns lasts an hour, so it is asked for immediately
# before it is used. One token buys exactly one key.
uses: NuGet/login@8d196754b4036150537f80ac539e15c2f1028841 # v1.2.0
id: login
with:
user: ${{ vars.NUGET_USER }}
- name: Generate an SBOM for the packages
# A consumer deciding whether to take this needs to know what comes with
# it. The SBOM describes the packed artifacts rather than the tree, so it
# lists what actually ships.
uses: anchore/sbom-action@3ad7283483fc7af8ff2b4ea19663c2d5ca935e26 # v0.24.2
with:
path: artifacts/packages
format: cyclonedx-json
artifact-name: sbom-cyclonedx.json
output-file: artifacts/sbom-cyclonedx.json
- name: Attest what produced these packages
# Signed provenance says which workflow, at which commit, on which
# runner, built each file. Trusted publishing already proves the push
# came from this repository; this proves the same about the bytes.
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
with:
subject-path: 'artifacts/packages/*.nupkg'
- name: Push
env:
NUGET_API_KEY: ${{ steps.login.outputs.NUGET_API_KEY }}
run: >
dotnet nuget push "artifacts/packages/*.nupkg"
--api-key "${NUGET_API_KEY}"
--source https://api.nuget.org/v3/index.json