Skip to content

Commit 3011347

Browse files
committed
Add trusted publishing release workflow
1 parent 544087d commit 3011347

5 files changed

Lines changed: 165 additions & 164 deletions

File tree

‎.github/workflows/release.yml‎

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
paths:
8+
- RELEASE_NOTES.md
9+
10+
concurrency:
11+
group: release-${{ github.ref }}
12+
cancel-in-progress: false
13+
14+
jobs:
15+
detect-release:
16+
name: Detect new release
17+
runs-on: ubuntu-latest
18+
permissions:
19+
contents: read
20+
outputs:
21+
is-release: ${{ steps.release.outputs.is-release }}
22+
version: ${{ steps.release.outputs.version }}
23+
prerelease: ${{ steps.release.outputs.prerelease }}
24+
steps:
25+
- uses: actions/checkout@v4
26+
with:
27+
fetch-depth: 0
28+
- name: Compare release headings
29+
id: release
30+
shell: pwsh
31+
env:
32+
BEFORE_SHA: ${{ github.event.before }}
33+
run: |
34+
$headingPattern = '^####\s+(\S+)'
35+
$currentHeading = Get-Content RELEASE_NOTES.md | Select-String -Pattern $headingPattern | Select-Object -First 1
36+
if (-not $currentHeading) {
37+
throw 'RELEASE_NOTES.md has no #### release heading.'
38+
}
39+
40+
$version = $currentHeading.Matches[0].Groups[1].Value
41+
$previousVersion = $null
42+
if ($env:BEFORE_SHA -and $env:BEFORE_SHA -notmatch '^0+$') {
43+
$previousNotes = git show "${env:BEFORE_SHA}:RELEASE_NOTES.md" 2>$null
44+
if ($LASTEXITCODE -eq 0) {
45+
$previousHeading = $previousNotes | Select-String -Pattern $headingPattern | Select-Object -First 1
46+
if ($previousHeading) {
47+
$previousVersion = $previousHeading.Matches[0].Groups[1].Value
48+
}
49+
}
50+
}
51+
52+
$isRelease = $version -ne $previousVersion
53+
"is-release=$($isRelease.ToString().ToLowerInvariant())" >> $env:GITHUB_OUTPUT
54+
"version=$version" >> $env:GITHUB_OUTPUT
55+
"prerelease=$($version.Contains('-').ToString().ToLowerInvariant())" >> $env:GITHUB_OUTPUT
56+
Write-Host "Current release: $version; previous release: $previousVersion; publish: $isRelease"
57+
58+
release:
59+
name: Build and publish ${{ needs.detect-release.outputs.version }}
60+
needs: detect-release
61+
if: needs.detect-release.outputs.is-release == 'true'
62+
runs-on: windows-latest
63+
timeout-minutes: 90
64+
permissions:
65+
contents: write
66+
id-token: write
67+
steps:
68+
- uses: actions/checkout@v4
69+
with:
70+
fetch-depth: 0
71+
- name: Configure git autocrlf
72+
run: git config --global core.autocrlf input
73+
- uses: actions/setup-dotnet@v4
74+
with:
75+
global-json-file: global.json
76+
- name: Cache NuGet packages
77+
uses: actions/cache@v4
78+
with:
79+
path: |
80+
~/.nuget/packages
81+
packages
82+
key: ${{ runner.os }}-nuget-${{ hashFiles('paket.lock', 'paket.dependencies', 'global.json') }}
83+
restore-keys: |
84+
${{ runner.os }}-nuget-
85+
- name: Build release artifacts
86+
shell: cmd
87+
run: .\build.cmd BuildPackage < NUL
88+
- name: Verify release artifacts
89+
shell: pwsh
90+
env:
91+
VERSION: ${{ needs.detect-release.outputs.version }}
92+
run: |
93+
$requiredFiles = @(
94+
'bin/merged/paket.exe',
95+
'bin/merged/paket-sha256.txt',
96+
'bin_bootstrapper/net461/paket.bootstrapper.exe',
97+
'src/FSharp.DependencyManager.Paket/bin/Release/netstandard2.0/FSharp.DependencyManager.Paket.dll',
98+
'.paket/paket.targets',
99+
'.paket/Paket.Restore.targets',
100+
"temp/Paket.$env:VERSION.nupkg",
101+
"temp/Paket.Core.$env:VERSION.nupkg",
102+
"temp/Paket.Bootstrapper.$env:VERSION.nupkg",
103+
"temp/FSharp.DependencyManager.Paket.$env:VERSION.nupkg"
104+
)
105+
$missingFiles = $requiredFiles | Where-Object { -not (Test-Path $_ -PathType Leaf) }
106+
if ($missingFiles) {
107+
throw "Missing release artifacts:`n$($missingFiles -join "`n")"
108+
}
109+
- name: Obtain NuGet key
110+
id: login
111+
# NuGet/login v1.1.0
112+
uses: NuGet/login@8d196754b4036150537f80ac539e15c2f1028841
113+
with:
114+
user: dsyme
115+
- name: Publish NuGet packages
116+
shell: pwsh
117+
env:
118+
NUGET_API_KEY: ${{ steps.login.outputs.NUGET_API_KEY }}
119+
run: dotnet nuget push 'temp/*.nupkg' --source https://api.nuget.org/v3/index.json --api-key $env:NUGET_API_KEY --skip-duplicate
120+
- name: Create release notes body
121+
shell: pwsh
122+
run: |
123+
$lines = Get-Content RELEASE_NOTES.md
124+
$start = 0..($lines.Count - 1) | Where-Object { $lines[$_] -match '^####\s+' } | Select-Object -First 1
125+
$end = (($start + 1)..($lines.Count - 1) | Where-Object { $lines[$_] -match '^####\s+' } | Select-Object -First 1)
126+
if ($null -eq $end) { $end = $lines.Count }
127+
$body = $lines[($start + 1)..($end - 1)] -join "`n"
128+
$body.Trim() | Set-Content release-notes-body.md
129+
- name: Create GitHub release
130+
shell: pwsh
131+
env:
132+
GH_TOKEN: ${{ github.token }}
133+
VERSION: ${{ needs.detect-release.outputs.version }}
134+
PRERELEASE: ${{ needs.detect-release.outputs.prerelease }}
135+
run: |
136+
$assets = @(
137+
'bin/merged/paket.exe',
138+
'bin/merged/paket-sha256.txt',
139+
'bin_bootstrapper/net461/paket.bootstrapper.exe',
140+
'src/FSharp.DependencyManager.Paket/bin/Release/netstandard2.0/FSharp.DependencyManager.Paket.dll',
141+
'.paket/paket.targets',
142+
'.paket/Paket.Restore.targets'
143+
) + @(Get-ChildItem temp/*.nupkg | ForEach-Object FullName)
144+
145+
gh release view $env:VERSION *> $null
146+
if ($LASTEXITCODE -eq 0) {
147+
gh release upload $env:VERSION $assets --clobber
148+
} else {
149+
$arguments = @(
150+
'release', 'create', $env:VERSION
151+
) + $assets + @(
152+
'--target', '${{ github.sha }}',
153+
'--title', $env:VERSION,
154+
'--notes-file', 'release-notes-body.md'
155+
)
156+
if ($env:PRERELEASE -eq 'true') {
157+
$arguments += '--prerelease'
158+
}
159+
& gh @arguments
160+
}

‎AGENTS.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ Repository build targets:
6060
./build.sh
6161
```
6262

63-
On Windows, use the corresponding `build.cmd` commands. Both scripts run `build.fsx` with `dotnet fsi`, so the build script itself only needs the .NET SDK declared in `global.json`. The full build restores tools and dependencies, builds all projects, and runs broad test suites; on Linux it still depends on Mono for the targets that run .NET Framework binaries, namely the `net461` test passes and `PublishNuGet`. If those prerequisites are unavailable, run the relevant `dotnet test` command and report what was not validated.
63+
On Windows, use the corresponding `build.cmd` commands. Both scripts run `build.fsx` with `dotnet fsi`, so the build script itself only needs the .NET SDK declared in `global.json`. The full build restores tools and dependencies, builds all projects, and runs broad test suites; on Linux it still depends on Mono for the `net461` test passes. If those prerequisites are unavailable, run the relevant `dotnet test` command and report what was not validated.
6464

6565
## Tests
6666

‎CLAUDE.md‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,8 @@ build.cmd BuildPackage SkipTests=true # Skip all tests
3333
build.cmd BuildPackage SkipIntegrationTests=true # Skip integration tests only
3434
```
3535

36-
Mono is still required on Linux for the targets that run .NET Framework binaries: the `net461`
37-
test passes and `PublishNuGet`, which pushes with the merged `net461` `paket.exe`.
38-
`MergePaketTool` no longer needs it: it repacks through the `dotnet-ilrepack` tool.
36+
Mono is still required on Linux for the `net461` test passes. `MergePaketTool` no longer needs it:
37+
it repacks through the `dotnet-ilrepack` tool.
3938

4039
## Testing
4140

‎DEV_GUIDE.md‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,8 @@ before: the script turns the `key=value` arguments into environment variables it
1818
the FAKE runner used to do, and passes the target name to FAKE as `--target` through the execution
1919
context it creates. Only one target name is accepted.
2020

21-
Mono is still needed on Linux for the targets that execute .NET Framework binaries: the `net461`
22-
test passes and `PublishNuGet`, which pushes with the merged `net461` `paket.exe`. `MergePaketTool`
23-
no longer needs it, it repacks through the `dotnet-ilrepack` tool of `.config/dotnet-tools.json`.
21+
Mono is still needed on Linux for the `net461` test passes. `MergePaketTool` no longer needs it; it
22+
repacks through the `dotnet-ilrepack` tool of `.config/dotnet-tools.json`.
2423
Removing the remaining Mono dependency is tracked separately in
2524
[#4348](https://github.com/fsprojects/Paket/issues/4348).
2625

0 commit comments

Comments
 (0)