Skip to content

fix: allow unsigned macOS release packaging #23

fix: allow unsigned macOS release packaging

fix: allow unsigned macOS release packaging #23

Workflow file for this run

name: ci
on:
push:
branches:
- master
tags:
- v*.*.*
pull_request:
permissions:
contents: read
jobs:
verify:
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v6
with:
submodules: recursive
- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: 10.0.203
cache: true
cache-dependency-path: |
Directory.Packages.props
CodexSwitch/CodexSwitch.csproj
CodexSwitchUI/src/CodexSwitchUI/CodexSwitchUI.csproj
CodexSwitchUI/src/CodexSwitchUI.ECharts/CodexSwitchUI.ECharts.csproj
CodexSwitch.Tests/CodexSwitch.Tests.csproj
- name: Validate changelog
shell: pwsh
run: ./build/Validate-Changelog.ps1
- name: Restore
run: dotnet restore CodexSwitch.Tests/CodexSwitch.Tests.csproj
- name: Build
run: dotnet build CodexSwitch.Tests/CodexSwitch.Tests.csproj -c Release --no-restore
- name: Test
run: dotnet test CodexSwitch.Tests/CodexSwitch.Tests.csproj -c Release --no-build --no-restore --verbosity normal
publish:
needs: verify
strategy:
fail-fast: false
matrix:
include:
- runner: windows-latest
rid: win-x64
- runner: ubuntu-latest
rid: linux-x64
- runner: macos-14
rid: osx-arm64
- runner: macos-15-intel
rid: osx-x64
runs-on: ${{ matrix.runner }}
env:
MACOS_CERTIFICATE_INPUT: ${{ secrets.MACOS_CERTIFICATE_BASE64 || secrets.CSC_LINK || '' }}
MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD || secrets.CSC_KEY_PASSWORD || '' }}
MACOS_KEYCHAIN_PASSWORD: ${{ secrets.MACOS_KEYCHAIN_PASSWORD || '' }}
MACOS_SIGNING_IDENTITY: ${{ secrets.MACOS_SIGNING_IDENTITY || '' }}
MACOS_NOTARY_APPLE_ID: ${{ secrets.MACOS_NOTARY_APPLE_ID || secrets.APPLE_ID || '' }}
MACOS_NOTARY_TEAM_ID: ${{ secrets.MACOS_NOTARY_TEAM_ID || secrets.APPLE_TEAM_ID || '' }}
MACOS_NOTARY_PASSWORD: ${{ secrets.MACOS_NOTARY_PASSWORD || secrets.APPLE_APP_SPECIFIC_PASSWORD || '' }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
submodules: recursive
- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: 10.0.203
cache: true
cache-dependency-path: |
Directory.Packages.props
CodexSwitch/CodexSwitch.csproj
CodexSwitchUI/src/CodexSwitchUI/CodexSwitchUI.csproj
CodexSwitchUI/src/CodexSwitchUI.ECharts/CodexSwitchUI.ECharts.csproj
CodexSwitch.Tests/CodexSwitch.Tests.csproj
- name: Read version
id: version
shell: pwsh
run: |
[xml]$props = Get-Content -LiteralPath "Directory.Build.props"
$version = $props.Project.PropertyGroup.Version
"value=$version" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
- name: Publish ${{ matrix.rid }}
run: >
dotnet publish CodexSwitch/CodexSwitch.csproj
-c Release
-r ${{ matrix.rid }}
--self-contained true
-p:PublishAot=true
-p:SelfContained=true
-p:DebugType=none
-p:DebugSymbols=false
-o artifacts/publish/${{ matrix.rid }}
- name: Import macOS signing certificate
if: runner.os == 'macOS' && env.MACOS_CERTIFICATE_INPUT != ''
shell: pwsh
run: |
function Invoke-Security {
& security @args
if ($LASTEXITCODE -ne 0) {
throw "security $($args[0]) failed."
}
}
$certificatePath = Join-Path $env:RUNNER_TEMP "codexswitch_developer_id.p12"
$keychainPath = Join-Path $env:RUNNER_TEMP "codexswitch-signing.keychain-db"
$certificateInput = $env:MACOS_CERTIFICATE_INPUT
$keychainPassword = $env:MACOS_KEYCHAIN_PASSWORD
if ([string]::IsNullOrWhiteSpace($keychainPassword)) {
$keychainPassword = [guid]::NewGuid().ToString("N")
}
if ($certificateInput -match '^https?://') {
Invoke-WebRequest -Uri $certificateInput -OutFile $certificatePath
}
elseif (Test-Path -LiteralPath $certificateInput -PathType Leaf) {
Copy-Item -LiteralPath $certificateInput -Destination $certificatePath -Force
}
else {
$normalizedCertificate = $certificateInput -replace '\s', ''
try {
[IO.File]::WriteAllBytes($certificatePath, [Convert]::FromBase64String($normalizedCertificate))
}
catch {
throw "MACOS_CERTIFICATE_BASE64/CSC_LINK must be a certificate URL, local .p12 path, or base64-encoded .p12 data."
}
}
Invoke-Security create-keychain -p $keychainPassword $keychainPath
Invoke-Security set-keychain-settings -lut 21600 $keychainPath
Invoke-Security unlock-keychain -p $keychainPassword $keychainPath
Invoke-Security import $certificatePath -P $env:MACOS_CERTIFICATE_PASSWORD -A -t cert -f pkcs12 -k $keychainPath
Invoke-Security set-key-partition-list -S apple-tool:,apple: -s -k $keychainPassword $keychainPath
Invoke-Security list-keychains -d user -s $keychainPath
Invoke-Security default-keychain -s $keychainPath
- name: Resolve macOS signing identity
id: mac-signing
if: runner.os == 'macOS'
shell: pwsh
run: |
$identity = $env:MACOS_SIGNING_IDENTITY
if ([string]::IsNullOrWhiteSpace($identity)) {
$identityLine = & security find-identity -v -p codesigning 2>$null |
Select-String -Pattern "Developer ID Application" |
Select-Object -First 1
if ($null -ne $identityLine) {
$match = [regex]::Match($identityLine.Line, '"(?<identity>[^"]+)"')
if ($match.Success) {
$identity = $match.Groups["identity"].Value
}
}
}
if ([string]::IsNullOrWhiteSpace($identity)) {
Write-Host "No macOS Developer ID Application signing identity was found."
}
else {
Write-Host "Using macOS signing identity: $identity"
}
"identity=$identity" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
- name: Install Windows packaging tools
if: runner.os == 'Windows'
shell: pwsh
run: choco install innosetup --yes --no-progress
- name: Package installer
id: package
shell: pwsh
run: |
$version = "${{ steps.version.outputs.value }}"
./build/Package-ReleaseArtifact.ps1 `
-Version $version `
-RuntimeIdentifier "${{ matrix.rid }}" `
-PublishDirectory "artifacts/publish/${{ matrix.rid }}" `
-OutputDirectory "artifacts/package" `
-MacSigningIdentity "${{ steps.mac-signing.outputs.identity }}" `
-MacNotaryAppleId "$env:MACOS_NOTARY_APPLE_ID" `
-MacNotaryTeamId "$env:MACOS_NOTARY_TEAM_ID" `
-MacNotaryPassword "$env:MACOS_NOTARY_PASSWORD"
- name: Upload artifact
if: steps.package.outputs.path != ''
uses: actions/upload-artifact@v4
with:
name: ${{ steps.package.outputs.name }}
path: ${{ steps.package.outputs.path }}
if-no-files-found: error
release:
if: startsWith(github.ref, 'refs/tags/v')
needs: publish
runs-on: windows-latest
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v6
with:
submodules: recursive
- name: Read version
id: version
shell: pwsh
run: |
[xml]$props = Get-Content -LiteralPath "Directory.Build.props"
$version = $props.Project.PropertyGroup.Version
$expectedTag = "v$version"
if ($expectedTag -ne "${{ github.ref_name }}") {
throw "Release tag '${{ github.ref_name }}' does not match Directory.Build.props version '$expectedTag'."
}
"value=$version" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
- name: Download release artifacts
uses: actions/download-artifact@v4
with:
pattern: CodexSwitch-v${{ steps.version.outputs.value }}-*
path: artifacts/release
merge-multiple: true
- name: Build release notes
shell: pwsh
run: |
$version = "v${{ steps.version.outputs.value }}"
$content = Get-Content -LiteralPath "CHANGELOG.md" -Raw
$pattern = "(?s)## \[$([regex]::Escape($version))\].*?(?=\r?\n## \[v\d+\.\d+\.\d+\]|\z)"
$match = [regex]::Match($content, $pattern)
if (-not $match.Success) {
throw "Unable to find changelog section for $version."
}
$match.Value | Out-File -LiteralPath "release-notes.md" -Encoding utf8
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
env:
GITHUB_TOKEN: ${{ github.token }}
with:
tag_name: ${{ github.ref_name }}
name: ${{ github.ref_name }}
body_path: release-notes.md
files: artifacts/release/*
fail_on_unmatched_files: true