-
Notifications
You must be signed in to change notification settings - Fork 0
254 lines (235 loc) · 10.5 KB
/
Copy pathpublish-plugins.yml
File metadata and controls
254 lines (235 loc) · 10.5 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
name: Publish plugins
# Plugin packages are released independently from the host:
# * The host's release workflow (release.yml) handles the Velopack
# installer + draft GitHub Release; it does NOT touch nuget.org.
# * This workflow handles plugin .nupkg publish. Three triggers:
# - Tag push like `pim-v0.3.1` / `contracts-v0.2.2` / `laps-v1.0.0`
# → authoritative: builds that plugin at the tag's version
# (overriding the csproj <Version>) and pushes to NuGet.
# - Branch push to main with changes inside a plugin folder
# → opportunistic: builds the plugin at its csproj <Version>;
# --skip-duplicate makes it a no-op if the version on NuGet
# is already current.
# - workflow_dispatch → manual selector for retry / out-of-band
# republish.
#
# Tag-driven is the recommended workflow because the tag IS the
# version-of-record (and matches the host's tag pattern). Bumping the
# csproj is optional and historical-only — useful for keeping the
# checked-in state in sync with what shipped, but not required for the
# publish itself.
on:
push:
branches: [main]
paths:
- 'src/AzureTray.Plugin.Contracts/**'
- 'src/AzureTray.Plugin.PIM/**'
- 'src/AzureTray.Plugin.LAPS/**'
tags:
- 'contracts-v*'
- 'pim-v*'
- 'laps-v*'
workflow_dispatch:
inputs:
plugin:
description: Which package to publish
type: choice
required: true
default: all
options:
- all
- Contracts
- PIM
- LAPS
version:
description: Optional override (leave blank to use the csproj <Version>)
type: string
required: false
default: ''
permissions:
contents: read
id-token: write # for attest-build-provenance
attestations: write # for attest-build-provenance
concurrency:
group: publish-plugins
cancel-in-progress: false
jobs:
publish:
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0 # SourceLink + paths-filter need history
# Path filter only runs for BRANCH pushes; tag pushes ignore paths.
- name: Detect changed plugin folders (branch push only)
id: filter
if: github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/')
uses: dorny/paths-filter@v3
with:
base: ${{ github.event.before }}
filters: |
contracts:
- 'src/AzureTray.Plugin.Contracts/**'
pim:
- 'src/AzureTray.Plugin.PIM/**'
laps:
- 'src/AzureTray.Plugin.LAPS/**'
# Resolve trigger → (selected plugins, optional version override).
# All three trigger modes funnel through this one step so the Pack
# step downstream stays trigger-agnostic.
- name: Resolve plugin selection + version
id: pick
shell: pwsh
env:
EVENT_NAME: ${{ github.event_name }}
REF: ${{ github.ref }}
MANUAL_PLUGIN: ${{ github.event.inputs.plugin }}
MANUAL_VERSION: ${{ github.event.inputs.version }}
CHANGED_CONTRACTS: ${{ steps.filter.outputs.contracts }}
CHANGED_PIM: ${{ steps.filter.outputs.pim }}
CHANGED_LAPS: ${{ steps.filter.outputs.laps }}
run: |
$selected = New-Object System.Collections.Generic.List[string]
$version = ''
if ($env:EVENT_NAME -eq 'push' -and $env:REF -like 'refs/tags/*') {
# Tag push: parse `<plugin>-v<version>` from the tag name.
# The lower-case plugin id maps to the title-case csproj key
# via $idMap below. Anything that doesn't match the pattern
# is rejected loudly — the tag is meant to ship something
# specific; ambiguity isn't safe.
$tag = $env:REF -replace '^refs/tags/', ''
$idMap = @{ 'contracts' = 'Contracts'; 'pim' = 'PIM'; 'laps' = 'LAPS' }
if ($tag -match '^(?<id>contracts|pim|laps)-v(?<ver>.+)$') {
$selected.Add($idMap[$Matches['id']])
$version = $Matches['ver']
Write-Host "Tag-driven publish: plugin=$($selected[0]) version=$version" -ForegroundColor Cyan
} else {
throw "Tag '$tag' doesn't match expected pattern '<contracts|pim|laps>-v<version>'."
}
}
elseif ($env:EVENT_NAME -eq 'workflow_dispatch') {
# Manual: honour the dropdown + optional version override.
switch ($env:MANUAL_PLUGIN) {
'all' { $selected.AddRange([string[]]@('Contracts','PIM','LAPS')) }
'Contracts' { $selected.Add('Contracts') }
'PIM' { $selected.Add('PIM') }
'LAPS' { $selected.Add('LAPS') }
}
if (-not [string]::IsNullOrWhiteSpace($env:MANUAL_VERSION)) {
$version = $env:MANUAL_VERSION.Trim()
if ($selected.Count -ne 1) {
throw "Version override is only valid when a single plugin is selected (got '$($env:MANUAL_PLUGIN)')."
}
Write-Host "Manual publish (override): plugin=$($selected[0]) version=$version" -ForegroundColor Cyan
} else {
Write-Host "Manual publish: [$($selected -join ', ')] at csproj <Version>" -ForegroundColor Cyan
}
}
else {
# Branch push: include every plugin folder paths-filter saw change.
if ($env:CHANGED_CONTRACTS -eq 'true') { $selected.Add('Contracts') }
if ($env:CHANGED_PIM -eq 'true') { $selected.Add('PIM') }
if ($env:CHANGED_LAPS -eq 'true') { $selected.Add('LAPS') }
if ($selected.Count -gt 0) {
Write-Host "Branch-push publish: [$($selected -join ', ')] at csproj <Version>" -ForegroundColor Cyan
}
}
"selected=$($selected -join ',')" >> $env:GITHUB_OUTPUT
"version=$version" >> $env:GITHUB_OUTPUT
"any=$(($selected.Count -gt 0).ToString().ToLower())" >> $env:GITHUB_OUTPUT
# Everything below is a no-op when nothing was selected — saves
# restore/build time on branch pushes that didn't touch a plugin.
- name: Setup .NET
if: steps.pick.outputs.any == 'true'
uses: actions/setup-dotnet@v4
with:
global-json-file: global.json
- name: Restore
if: steps.pick.outputs.any == 'true'
run: dotnet restore AzureTray.sln
- name: Build (Release)
if: steps.pick.outputs.any == 'true'
shell: pwsh
env:
PIN_VERSION: ${{ steps.pick.outputs.version }}
run: |
# Tag-driven and manual-override paths supply an explicit
# version; pass it as /p:Version so AssemblyFileVersion,
# AssemblyInformationalVersion, and PackageVersion all line up.
# Branch-push leaves the csproj <Version> as-is.
if ([string]::IsNullOrWhiteSpace($env:PIN_VERSION)) {
dotnet build AzureTray.sln --configuration Release --no-restore
} else {
dotnet build AzureTray.sln --configuration Release --no-restore /p:Version=$env:PIN_VERSION
}
- name: Pack selected plugin(s)
if: steps.pick.outputs.any == 'true'
shell: pwsh
env:
SELECTED: ${{ steps.pick.outputs.selected }}
PIN_VERSION: ${{ steps.pick.outputs.version }}
run: |
$projects = @{
'Contracts' = 'src/AzureTray.Plugin.Contracts/AzureTray.Plugin.Contracts.csproj'
'PIM' = 'src/AzureTray.Plugin.PIM/AzureTray.Plugin.PIM.csproj'
'LAPS' = 'src/AzureTray.Plugin.LAPS/AzureTray.Plugin.LAPS.csproj'
}
$picks = $env:SELECTED -split ',' | Where-Object { $_ }
if (-not (Test-Path NuGet)) { New-Item -ItemType Directory -Path NuGet | Out-Null }
foreach ($pick in $picks) {
$csproj = $projects[$pick]
if ([string]::IsNullOrWhiteSpace($env:PIN_VERSION)) {
Write-Host "Packing $pick ($csproj) at csproj <Version> ..." -ForegroundColor Cyan
dotnet pack $csproj --configuration Release --no-build --output NuGet
} else {
Write-Host "Packing $pick ($csproj) at pinned version $env:PIN_VERSION ..." -ForegroundColor Cyan
dotnet pack $csproj --configuration Release --no-build --output NuGet /p:Version=$env:PIN_VERSION
}
if ($LASTEXITCODE -ne 0) { throw "dotnet pack failed for $csproj" }
}
- name: Upload nupkgs as artifact
if: steps.pick.outputs.any == 'true'
uses: actions/upload-artifact@v4
with:
name: plugin-packages
path: NuGet/*.nupkg
if-no-files-found: error
- name: Attest build provenance
if: steps.pick.outputs.any == 'true'
uses: actions/attest-build-provenance@v2
with:
subject-path: NuGet/*.nupkg
- name: Check NuGet config
id: nugetcfg
if: steps.pick.outputs.any == 'true'
shell: pwsh
env:
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
run: |
$enabled = -not [string]::IsNullOrWhiteSpace($env:NUGET_API_KEY)
"enabled=$($enabled.ToString().ToLower())" >> $env:GITHUB_OUTPUT
Write-Host "Push to nuget.org enabled: $enabled"
- name: Push to nuget.org
if: steps.pick.outputs.any == 'true' && steps.nugetcfg.outputs.enabled == 'true'
shell: pwsh
env:
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
run: |
# --skip-duplicate keeps the push idempotent: a branch-push run
# that didn't bump the csproj's <Version> is a no-op rather than
# a workflow failure. Tag-driven and manual-override runs are
# expected to be unique versions.
$packages = Get-ChildItem NuGet\*.nupkg | Where-Object { $_.Name -notlike '*.symbols.nupkg' }
foreach ($pkg in $packages) {
Write-Host "Pushing $($pkg.Name)..."
dotnet nuget push $pkg.FullName `
--api-key $env:NUGET_API_KEY `
--source https://api.nuget.org/v3/index.json `
--skip-duplicate
if ($LASTEXITCODE -ne 0) { throw "nuget push failed for $($pkg.Name)" }
}
- name: Nothing to publish
if: steps.pick.outputs.any != 'true'
shell: pwsh
run: Write-Host "No plugin folder changed in this push and no manual selection — skipping." -ForegroundColor Yellow