Repository navigation
199 lines (184 loc) · 8.88 KB
/
Copy pathrelease.yml
File metadata and controls
199 lines (184 loc) · 8.88 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
name: Release
# Two ways to publish a release (a version with a hyphen, like 1.6.0-alpha.3, is a pre-release):
# - Actions > Release > Run workflow on main, with the version and "publish" ticked: the tag and the
# release are created here, from main.
# - Push a tag like v1.3.0 (stable) or v1.3.0-beta.2 (pre-release).
# With "publish" unticked the workflow only builds and uploads the files as an artifact.
# The app's update check (UpdateService) reads these releases and looks for the
# ConsoleMode-Setup-*.exe / ConsoleMode-Portable-*.exe assets built here.
on:
push:
tags:
- "v*.*.*"
workflow_dispatch:
inputs:
version:
description: "Version (e.g. 1.6.0-alpha.3). A hyphen makes it a pre-release."
required: true
publish:
description: "Publish: create the tag and the GitHub release (only from main). Unticked, just build."
type: boolean
default: false
permissions:
contents: write
jobs:
build:
runs-on: windows-latest
outputs:
version: ${{ steps.version.outputs.version }}
prerelease: ${{ steps.version.outputs.prerelease }}
publish: ${{ steps.version.outputs.publish }}
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Setup .NET
uses: actions/setup-dotnet@v6
with:
dotnet-version: "8.0.x"
- name: Resolve version
id: version
shell: pwsh
env:
EVENT_NAME: ${{ github.event_name }}
REF_NAME: ${{ github.ref_name }}
REF: ${{ github.ref }}
INPUT_VERSION: ${{ github.event.inputs.version }}
INPUT_PUBLISH: ${{ github.event.inputs.publish }}
run: |
$v = if ($env:EVENT_NAME -eq "push") { $env:REF_NAME } else { $env:INPUT_VERSION }
$v = $v.TrimStart("v")
if ($v -notmatch '^\d+\.\d+\.\d+(-[0-9A-Za-z.\-]+)?$') { throw "Versão inválida: $v" }
$publish = ($env:EVENT_NAME -eq "push") -or ($env:INPUT_PUBLISH -eq "true")
if ($publish -and $env:EVENT_NAME -ne "push") {
# Publishing by hand creates the tag here: only from main, and never over a tag that exists.
if ($env:REF -ne "refs/heads/main") { throw "Só dá para publicar a partir da main (rodando em $($env:REF))." }
if (git ls-remote --tags origin "refs/tags/v$v") { throw "A tag v$v já existe." }
}
"version=$v" >> $env:GITHUB_OUTPUT
"prerelease=$($v.Contains('-').ToString().ToLower())" >> $env:GITHUB_OUTPUT
"publish=$($publish.ToString().ToLower())" >> $env:GITHUB_OUTPUT
- name: Prepare release notes (English, with a link to the Portuguese changelog)
if: steps.version.outputs.publish == 'true'
shell: pwsh
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
function Get-Notes([string]$file) {
$changelog = Get-Content -LiteralPath $file -Raw -Encoding utf8
$pattern = "(?ms)^## \[$([regex]::Escape($env:VERSION))\]\r?\n(?<notes>.*?)(?=^## \[|\z)"
$match = [regex]::Match($changelog, $pattern)
if (-not $match.Success) { throw "$file precisa conter a seção ## [$($env:VERSION)]." }
$notes = $match.Groups["notes"].Value.Trim()
if ([string]::IsNullOrWhiteSpace($notes)) { throw "A seção $($env:VERSION) de $file está vazia." }
return $notes
}
# Both changelogs must have the section; the Portuguese one is linked, not pasted.
$null = Get-Notes "CHANGELOG.md"
$en = Get-Notes "CHANGELOG.en-US.md"
# English only, no language headings: ReleaseNotes.cs shows the first item to every
# language. A "(#NN)" at the end of an item becomes a link to that PR on GitHub.
$tag = "v$($env:VERSION)"
$releaseNotes = @(
$en
""
"### Downloads"
""
# Single quotes: in double-quoted pwsh strings the backtick is an escape and the Markdown code spans vanish.
'- **Installer:** `ConsoleMode-Setup-x64.exe` — installs the app and offers shortcuts and starting with Windows.'
'- **Portable:** `ConsoleMode-Portable-x64.exe` — a single executable; keeps its data in the `ConsoleMode_Data` folder next to the file.'
""
"---"
""
"🇧🇷 Notas em português: [CHANGELOG.md](https://github.com/lippdev/consolemode/blob/$tag/CHANGELOG.md)"
) -join "`n"
Set-Content -LiteralPath "release-notes.md" -Value $releaseNotes -Encoding utf8
- name: Run tests
shell: pwsh
run: dotnet test tests/ConsoleMode.Tests/ConsoleMode.Tests.csproj -c Release --nologo
- name: Install Inno Setup (if missing)
shell: pwsh
run: |
if (-not (Test-Path "${env:ProgramFiles(x86)}\Inno Setup 6\ISCC.exe")) { choco install innosetup -y --no-progress }
- name: Build portable + installer
shell: pwsh
run: .\build\Publish-ConsoleMode.ps1 -Runtime x64 -Target All -Version "${{ steps.version.outputs.version }}"
# Code signing (docs/CODE_SIGNING.md). Off until the SignPath repository variables exist:
# SIGNPATH_ORGANIZATION_ID, SIGNPATH_PROJECT_SLUG, SIGNPATH_POLICY_SLUG, plus the
# SIGNPATH_API_TOKEN secret. Only published releases are signed; each request waits for manual approval.
- name: Upload unsigned build for signing
id: unsigned
if: steps.version.outputs.publish == 'true' && vars.SIGNPATH_ORGANIZATION_ID != ''
uses: actions/upload-artifact@v7
with:
name: unsigned-${{ steps.version.outputs.version }}
path: |
dist/ConsoleMode-Setup-x64.exe
dist/ConsoleMode-Portable-x64.exe
if-no-files-found: error
- name: Sign with SignPath
if: steps.version.outputs.publish == 'true' && vars.SIGNPATH_ORGANIZATION_ID != ''
uses: signpath/github-action-submit-signing-request@v3
with:
api-token: ${{ secrets.SIGNPATH_API_TOKEN }}
organization-id: ${{ vars.SIGNPATH_ORGANIZATION_ID }}
project-slug: ${{ vars.SIGNPATH_PROJECT_SLUG }}
signing-policy-slug: ${{ vars.SIGNPATH_POLICY_SLUG }}
github-artifact-id: ${{ steps.unsigned.outputs.artifact-id }}
wait-for-completion: true
wait-for-completion-timeout-in-seconds: 3600
output-artifact-directory: dist-signed
- name: Use the signed files
if: steps.version.outputs.publish == 'true' && vars.SIGNPATH_ORGANIZATION_ID != ''
shell: pwsh
run: |
foreach ($name in "ConsoleMode-Setup-x64.exe", "ConsoleMode-Portable-x64.exe") {
$signed = Join-Path "dist-signed" $name
if (-not (Test-Path -LiteralPath $signed)) { throw "SignPath did not return $name" }
if ((Get-AuthenticodeSignature -LiteralPath $signed).Status -ne "Valid") { throw "$name is not validly signed" }
Copy-Item -LiteralPath $signed -Destination (Join-Path "dist" $name) -Force
}
- name: Upload artifacts
uses: actions/upload-artifact@v7
with:
name: ConsoleMode-${{ steps.version.outputs.version }}
path: |
dist/ConsoleMode-Setup-x64.exe
dist/ConsoleMode-Portable-x64.exe
if-no-files-found: error
- name: Publish GitHub release
if: steps.version.outputs.publish == 'true'
uses: softprops/action-gh-release@v3
with:
# Pushed tag: it already exists. Run by hand: it is created here, on the commit that was built.
tag_name: v${{ steps.version.outputs.version }}
target_commitish: ${{ github.sha }}
name: Console Mode ${{ steps.version.outputs.version }}
prerelease: ${{ steps.version.outputs.prerelease }}
fail_on_unmatched_files: true
generate_release_notes: false
append_body: false
body_path: release-notes.md
files: |
dist/ConsoleMode-Setup-x64.exe
dist/ConsoleMode-Portable-x64.exe
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Opens the update PR on microsoft/winget-pkgs for stable releases (pre-releases are skipped).
# Off until the WINGET_TOKEN secret exists: a classic PAT with the public_repo scope, from the
# account that owns the lippdev/winget-pkgs fork.
winget:
needs: build
if: needs.build.outputs.publish == 'true' && needs.build.outputs.prerelease == 'false'
runs-on: ubuntu-latest
env:
WINGET_TOKEN: ${{ secrets.WINGET_TOKEN }}
steps:
- name: Submit to winget-pkgs
if: env.WINGET_TOKEN != ''
uses: vedantmgoyal9/winget-releaser@v2
with:
identifier: lippdev.ConsoleMode
release-tag: v${{ needs.build.outputs.version }}
installers-regex: 'ConsoleMode-Setup-x64\.exe$'
fork-user: lippdev
token: ${{ env.WINGET_TOKEN }}