build(mobile): 在移动构建中排除 SoundFlow 原生资产 #121
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build & Publish | |
| on: | |
| push: | |
| branches: | |
| - '**' | |
| workflow_dispatch: | |
| inputs: | |
| release_tag: | |
| description: "发布标签(例如 v2.3.100)" | |
| required: true | |
| type: string | |
| permissions: | |
| contents: read | |
| jobs: | |
| build_desktop: | |
| name: Build_${{ matrix.os }}_${{ matrix.arch }} | |
| runs-on: ${{ matrix.os == 'linux' && 'ubuntu-24.04' || (matrix.os == 'macos' && 'macos-15' || 'windows-2022') }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [windows, linux, macos] | |
| arch: [x64, x86, arm64] | |
| exclude: | |
| - os: linux | |
| arch: x86 | |
| - os: macos | |
| arch: x86 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| submodules: recursive | |
| ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.release_tag || github.ref }} | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v5 | |
| with: | |
| dotnet-version: 10.0.x | |
| - name: Restore | |
| shell: pwsh | |
| env: | |
| RID: ${{ matrix.os == 'windows' && format('win-{0}', matrix.arch) || (matrix.os == 'linux' && format('linux-{0}', matrix.arch) || format('osx-{0}', matrix.arch)) }} | |
| PLATFORM: ${{ matrix.os == 'windows' && 'Windows' || (matrix.os == 'linux' && 'Linux' || 'MacOs') }} | |
| run: | | |
| dotnet restore SecRandom.Desktop/SecRandom.Desktop.csproj -r $env:RID -p:SecRandomPlatform=$env:PLATFORM | |
| dotnet restore SecRandom.Launcher/SecRandom.Launcher.csproj -r $env:RID | |
| - name: Get Latest Tag | |
| id: get_tag | |
| if: ${{ github.event_name != 'workflow_dispatch' }} | |
| shell: pwsh | |
| run: | | |
| $latestTag = git describe --tags --abbrev=0 | |
| echo "LATEST_TAG=$latestTag" >> $env:GITHUB_OUTPUT | |
| echo "Latest tag: $latestTag" | |
| - name: Publish Desktop | |
| shell: pwsh | |
| env: | |
| RID: ${{ matrix.os == 'windows' && format('win-{0}', matrix.arch) || (matrix.os == 'linux' && format('linux-{0}', matrix.arch) || format('osx-{0}', matrix.arch)) }} | |
| PLATFORM: ${{ matrix.os == 'windows' && 'Windows' || (matrix.os == 'linux' && 'Linux' || 'MacOs') }} | |
| run: | | |
| $releaseRoot = 'artifacts/release' | |
| $fullDir = "$releaseRoot/publish/full/$env:RID" | |
| $lightDir = "$releaseRoot/publish/light/$env:RID" | |
| New-Item -ItemType Directory -Path $fullDir, $lightDir -Force | Out-Null | |
| dotnet publish SecRandom.Desktop/SecRandom.Desktop.csproj ` | |
| -c Release ` | |
| -r $env:RID ` | |
| -p:SecRandomPlatform=$env:PLATFORM ` | |
| --self-contained ` | |
| -p:PublishTrimmed=false ` | |
| -p:UseAppHost=true ` | |
| -p:DebugType=None ` | |
| -p:DebugSymbols=false ` | |
| --no-restore ` | |
| -o $fullDir | |
| dotnet publish SecRandom.Desktop/SecRandom.Desktop.csproj ` | |
| -c Release ` | |
| -r $env:RID ` | |
| -p:SecRandomPlatform=$env:PLATFORM ` | |
| --self-contained false ` | |
| -p:PublishTrimmed=false ` | |
| -p:UseAppHost=true ` | |
| -p:DebugType=None ` | |
| -p:DebugSymbols=false ` | |
| --no-restore ` | |
| -o $lightDir | |
| - name: Verify bundled audio runtime | |
| shell: pwsh | |
| env: | |
| RID: ${{ matrix.os == 'windows' && format('win-{0}', matrix.arch) || (matrix.os == 'linux' && format('linux-{0}', matrix.arch) || format('osx-{0}', matrix.arch)) }} | |
| run: | | |
| $nativeAsset = if ($env:RID.StartsWith('win-')) { 'miniaudio.dll' } elseif ($env:RID.StartsWith('linux-')) { 'libminiaudio.so' } else { 'libminiaudio.dylib' } | |
| $assetPath = Join-Path "artifacts/release/publish/full/$env:RID" $nativeAsset | |
| if (!(Test-Path -LiteralPath $assetPath)) { | |
| throw "SoundFlow native audio runtime was not published: $assetPath" | |
| } | |
| - name: Pack Portable Zips (Windows) | |
| if: ${{ matrix.os == 'windows' }} | |
| shell: pwsh | |
| env: | |
| VERSION: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.release_tag || steps.get_tag.outputs.LATEST_TAG }} | |
| RID: ${{ matrix.os == 'windows' && format('win-{0}', matrix.arch) || (matrix.os == 'linux' && format('linux-{0}', matrix.arch) || format('osx-{0}', matrix.arch)) }} | |
| WINDOWS_CODESIGN_PFX_BASE64: ${{ secrets.WINDOWS_CODESIGN_PFX_BASE64 }} | |
| WINDOWS_CODESIGN_PFX_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PFX_PASSWORD }} | |
| WINDOWS_CODESIGN_TIMESTAMP_URL: ${{ secrets.WINDOWS_CODESIGN_TIMESTAMP_URL }} | |
| run: | | |
| function Write-Marker([string]$path, [string]$runtimeKind) { | |
| @{ schemaVersion = 1; product = 'SecRandom'; rid = $env:RID; packageKind = 'portable-zip'; runtimeKind = $runtimeKind } | | |
| ConvertTo-Json -Compress | Set-Content -LiteralPath (Join-Path $path 'SecRandom.package.json') -Encoding utf8NoBOM | |
| } | |
| function Publish-Launcher([string]$path, [bool]$selfContained) { | |
| $assemblyVersion = $env:VERSION.TrimStart('v', 'V') | |
| $arguments = @('publish', 'SecRandom.Launcher/SecRandom.Launcher.csproj', '-c', 'Release', '-r', $env:RID, '-p:UseAppHost=true', '-p:PublishSingleFile=true', '-p:IncludeNativeLibrariesForSelfExtract=true', '-p:DebugType=None', '-p:DebugSymbols=false', "-p:Version=$assemblyVersion", '--no-restore', '-o', $path) | |
| if ($selfContained) { $arguments += '--self-contained' } else { $arguments += @('--self-contained', 'false') } | |
| & dotnet @arguments | |
| if ($LASTEXITCODE -ne 0) { throw "Launcher publish failed with exit code $LASTEXITCODE." } | |
| } | |
| function Sign-File([string]$path, [string]$signTool, [string]$certificatePath) { | |
| $arguments = @('sign', '/fd', 'SHA256', '/f', $certificatePath, '/p', $env:WINDOWS_CODESIGN_PFX_PASSWORD) | |
| if (-not [string]::IsNullOrWhiteSpace($env:WINDOWS_CODESIGN_TIMESTAMP_URL)) { | |
| $arguments += @('/tr', $env:WINDOWS_CODESIGN_TIMESTAMP_URL, '/td', 'SHA256') | |
| } | |
| $arguments += $path | |
| & $signTool @arguments | |
| if ($LASTEXITCODE -ne 0) { throw "signtool failed for $path with exit code $LASTEXITCODE." } | |
| } | |
| $releaseRoot = 'artifacts/release' | |
| $zipRoot = "$releaseRoot/portable/$env:RID" | |
| $fullRoot = "$zipRoot/full" | |
| $lightRoot = "$zipRoot/light" | |
| Remove-Item $zipRoot -Recurse -Force -ErrorAction SilentlyContinue | |
| New-Item -ItemType Directory -Path $fullRoot, $lightRoot, "$releaseRoot/dist" -Force | Out-Null | |
| $fullApp = Join-Path $fullRoot "app-$env:VERSION-0" | |
| $lightApp = Join-Path $lightRoot "app-$env:VERSION-0" | |
| Copy-Item "$releaseRoot/publish/full/$env:RID" $fullApp -Recurse | |
| Copy-Item "$releaseRoot/publish/light/$env:RID" $lightApp -Recurse | |
| Write-Marker $fullApp 'self-contained' | |
| Write-Marker $lightApp 'framework-dependent' | |
| Publish-Launcher $fullRoot $true | |
| Publish-Launcher $lightRoot $false | |
| if (-not [string]::IsNullOrWhiteSpace($env:WINDOWS_CODESIGN_PFX_BASE64) -and -not [string]::IsNullOrWhiteSpace($env:WINDOWS_CODESIGN_PFX_PASSWORD)) { | |
| $certificatePath = Join-Path $env:RUNNER_TEMP 'secrandom-portable.pfx' | |
| try { | |
| [IO.File]::WriteAllBytes($certificatePath, [Convert]::FromBase64String($env:WINDOWS_CODESIGN_PFX_BASE64)) | |
| $signTool = Get-ChildItem 'C:\Program Files (x86)\Windows Kits\10\bin\*\x64\signtool.exe' | Sort-Object FullName -Descending | Select-Object -First 1 -ExpandProperty FullName | |
| if ([string]::IsNullOrWhiteSpace($signTool)) { throw 'signtool.exe was not found on the Windows runner.' } | |
| foreach ($file in @( | |
| (Join-Path $fullRoot 'SecRandomLauncher.exe'), | |
| (Join-Path $lightRoot 'SecRandomLauncher.exe'), | |
| (Join-Path $fullApp 'SecRandom.Desktop.exe'), | |
| (Join-Path $lightApp 'SecRandom.Desktop.exe') | |
| )) { | |
| Sign-File $file $signTool $certificatePath | |
| } | |
| } | |
| finally { | |
| Remove-Item $certificatePath -Force -ErrorAction SilentlyContinue | |
| } | |
| } | |
| else { | |
| Write-Warning 'Windows code-signing secrets are unavailable; portable executables will be unsigned.' | |
| } | |
| Compress-Archive -Path "$fullRoot/*" -DestinationPath "$releaseRoot/dist/SecRandom-$env:VERSION-$env:RID-portable-full.zip" -Force | |
| Compress-Archive -Path "$lightRoot/*" -DestinationPath "$releaseRoot/dist/SecRandom-$env:VERSION-$env:RID-portable-light.zip" -Force | |
| - name: Pack Portable Zips (Unix) | |
| if: ${{ matrix.os != 'windows' }} | |
| shell: bash | |
| env: | |
| VERSION: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.release_tag || steps.get_tag.outputs.LATEST_TAG }} | |
| RID: ${{ matrix.os == 'windows' && format('win-{0}', matrix.arch) || (matrix.os == 'linux' && format('linux-{0}', matrix.arch) || format('osx-{0}', matrix.arch)) }} | |
| run: | | |
| set -euo pipefail | |
| release_root="artifacts/release" | |
| zip_root="${release_root}/portable/${RID}" | |
| full_root="${zip_root}/full" | |
| light_root="${zip_root}/light" | |
| rm -rf "$zip_root" | |
| mkdir -p "$full_root" "$light_root" "${release_root}/dist" | |
| full_app="$full_root/app-${VERSION}-0" | |
| light_app="$light_root/app-${VERSION}-0" | |
| assembly_version="${VERSION#v}" | |
| cp -a "${release_root}/publish/full/${RID}" "$full_app" | |
| cp -a "${release_root}/publish/light/${RID}" "$light_app" | |
| printf '{"schemaVersion":1,"product":"SecRandom","rid":"%s","packageKind":"portable-zip","runtimeKind":"self-contained"}\n' "$RID" > "$full_app/SecRandom.package.json" | |
| printf '{"schemaVersion":1,"product":"SecRandom","rid":"%s","packageKind":"portable-zip","runtimeKind":"framework-dependent"}\n' "$RID" > "$light_app/SecRandom.package.json" | |
| dotnet publish SecRandom.Launcher/SecRandom.Launcher.csproj -c Release -r "$RID" --self-contained -p:UseAppHost=true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:DebugType=None -p:DebugSymbols=false -p:Version="$assembly_version" --no-restore -o "$full_root" | |
| dotnet publish SecRandom.Launcher/SecRandom.Launcher.csproj -c Release -r "$RID" --self-contained false -p:UseAppHost=true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:DebugType=None -p:DebugSymbols=false -p:Version="$assembly_version" --no-restore -o "$light_root" | |
| (cd "$full_root" && zip -qr "../../../dist/SecRandom-${VERSION}-${RID}-portable-full.zip" .) | |
| (cd "$light_root" && zip -qr "../../../dist/SecRandom-${VERSION}-${RID}-portable-light.zip" .) | |
| - name: Install Inno Setup Languages | |
| if: ${{ matrix.os == 'windows' }} | |
| shell: pwsh | |
| run: | | |
| $langDir = "C:\Program Files (x86)\Inno Setup 6\Languages" | |
| if (!(Test-Path $langDir)) { | |
| New-Item -ItemType Directory -Path $langDir -Force | |
| } | |
| $languages = @{ | |
| 'ChineseSimplified.isl' = 'https://raw.githubusercontent.com/kira-96/Inno-Setup-Chinese-Simplified-Translation/main/ChineseSimplified.isl' | |
| 'Japanese.isl' = 'https://raw.githubusercontent.com/jrsoftware/issrc/main/Files/Languages/Japanese.isl' | |
| } | |
| foreach ($language in $languages.GetEnumerator()) { | |
| $path = Join-Path $langDir $language.Key | |
| Invoke-WebRequest -Uri $language.Value -OutFile $path | |
| Write-Host "Downloaded Inno Setup language: $($language.Key)" | |
| } | |
| - name: Build Windows Setup | |
| if: ${{ matrix.os == 'windows' }} | |
| shell: pwsh | |
| env: | |
| VERSION: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.release_tag || steps.get_tag.outputs.LATEST_TAG }} | |
| ARCH: ${{ matrix.arch }} | |
| RID: ${{ matrix.os == 'windows' && format('win-{0}', matrix.arch) || (matrix.os == 'linux' && format('linux-{0}', matrix.arch) || format('osx-{0}', matrix.arch)) }} | |
| PLATFORM: Windows | |
| WINDOWS_CODESIGN_PFX_BASE64: ${{ secrets.WINDOWS_CODESIGN_PFX_BASE64 }} | |
| WINDOWS_CODESIGN_PFX_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PFX_PASSWORD }} | |
| WINDOWS_CODESIGN_TIMESTAMP_URL: ${{ secrets.WINDOWS_CODESIGN_TIMESTAMP_URL }} | |
| run: | | |
| $uiAccessBuild = -not [string]::IsNullOrWhiteSpace($env:WINDOWS_CODESIGN_PFX_BASE64) -and -not [string]::IsNullOrWhiteSpace($env:WINDOWS_CODESIGN_PFX_PASSWORD) | |
| if ($uiAccessBuild) { | |
| $outDir = "artifacts/release/installer/$env:RID" | |
| Remove-Item $outDir -Recurse -Force -ErrorAction SilentlyContinue | |
| New-Item -ItemType Directory -Path $outDir -Force | Out-Null | |
| dotnet publish SecRandom.Desktop/SecRandom.Desktop.csproj ` | |
| -c Release ` | |
| -r $env:RID ` | |
| -p:SecRandomPlatform=$env:PLATFORM ` | |
| --self-contained ` | |
| -p:PublishTrimmed=false ` | |
| -p:UseAppHost=true ` | |
| -p:EnableUiAccess=true ` | |
| --no-restore ` | |
| -o $outDir | |
| $certificatePath = Join-Path $env:RUNNER_TEMP 'secrandom-uiaccess.pfx' | |
| try { | |
| [IO.File]::WriteAllBytes($certificatePath, [Convert]::FromBase64String($env:WINDOWS_CODESIGN_PFX_BASE64)) | |
| $signTool = Get-ChildItem 'C:\Program Files (x86)\Windows Kits\10\bin\*\x64\signtool.exe' | | |
| Sort-Object FullName -Descending | | |
| Select-Object -First 1 -ExpandProperty FullName | |
| if ([string]::IsNullOrWhiteSpace($signTool)) { | |
| throw 'signtool.exe was not found on the Windows runner.' | |
| } | |
| $arguments = @('sign', '/fd', 'SHA256', '/f', $certificatePath, '/p', $env:WINDOWS_CODESIGN_PFX_PASSWORD) | |
| if (-not [string]::IsNullOrWhiteSpace($env:WINDOWS_CODESIGN_TIMESTAMP_URL)) { | |
| $arguments += @('/tr', $env:WINDOWS_CODESIGN_TIMESTAMP_URL, '/td', 'SHA256') | |
| } | |
| $arguments += "$outDir/SecRandom.Desktop.exe" | |
| & $signTool @arguments | |
| if ($LASTEXITCODE -ne 0) { throw "signtool failed with exit code $LASTEXITCODE." } | |
| & $signTool verify /pa /v "$outDir/SecRandom.Desktop.exe" | |
| if ($LASTEXITCODE -ne 0) { throw "signtool verification failed with exit code $LASTEXITCODE." } | |
| } | |
| finally { | |
| Remove-Item $certificatePath -Force -ErrorAction SilentlyContinue | |
| } | |
| } | |
| else { | |
| $outDir = "artifacts/release/installer/$env:RID" | |
| Remove-Item $outDir -Recurse -Force -ErrorAction SilentlyContinue | |
| Copy-Item "artifacts/release/publish/full/$env:RID" $outDir -Recurse | |
| Write-Warning "Windows code-signing secrets are unavailable; building the installer without the UIAccess manifest." | |
| } | |
| @{ schemaVersion = 1; product = 'SecRandom'; rid = $env:RID; packageKind = 'windows-exe'; runtimeKind = 'self-contained' } | | |
| ConvertTo-Json -Compress | Set-Content -LiteralPath (Join-Path $outDir 'SecRandom.package.json') -Encoding utf8NoBOM | |
| $setupOutput = 'artifacts/release/setup' | |
| New-Item -ItemType Directory -Path $setupOutput -Force | Out-Null | |
| $isccArguments = @( | |
| "/DMyAppVersion=$env:VERSION" | |
| "/DMyAppOutDir=$outDir" | |
| "/FSecRandom-$env:VERSION-$env:RID-setup" | |
| "/O$setupOutput" | |
| ) | |
| if ($uiAccessBuild) { $isccArguments += '/DUiAccessBuild' } | |
| switch ($env:ARCH) { | |
| 'x86' { $isccArguments += '/DBuildArchX86' } | |
| 'x64' { $isccArguments += '/DBuildArchX64' } | |
| 'arm64' { $isccArguments += '/DBuildArchArm64' } | |
| default { throw "Unsupported Windows architecture: $env:ARCH" } | |
| } | |
| & "C:\Program Files (x86)\Inno Setup 6\ISCC.exe" Setup.iss @isccArguments | |
| if ($LASTEXITCODE -ne 0) { throw "ISCC failed with exit code $LASTEXITCODE." } | |
| - name: Build Linux deb | |
| if: ${{ matrix.os == 'linux' }} | |
| env: | |
| ARCH: ${{ matrix.arch }} | |
| VERSION: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.release_tag || steps.get_tag.outputs.LATEST_TAG }} | |
| RID: ${{ matrix.os == 'windows' && format('win-{0}', matrix.arch) || (matrix.os == 'linux' && format('linux-{0}', matrix.arch) || format('osx-{0}', matrix.arch)) }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| version_no_v="${VERSION#v}" | |
| release_root="artifacts/release" | |
| mkdir -p "${release_root}/dist" | |
| deb_arch="amd64" | |
| case "${{ matrix.arch }}" in | |
| x64) deb_arch="amd64" ;; | |
| arm64) deb_arch="arm64" ;; | |
| esac | |
| work="$(pwd)/${release_root}/linux/${RID}" | |
| pkgroot="${work}/package-root" | |
| rm -rf "$work" | |
| mkdir -p "$pkgroot/DEBIAN" | |
| mkdir -p "$pkgroot/usr/lib/secrandom" | |
| mkdir -p "$pkgroot/usr/bin" | |
| mkdir -p "$pkgroot/usr/share/applications" | |
| mkdir -p "$pkgroot/usr/share/icons/hicolor/256x256/apps" | |
| cp -a "${release_root}/publish/full/${RID}/." "$pkgroot/usr/lib/secrandom/" | |
| printf '{"schemaVersion":1,"product":"SecRandom","rid":"%s","packageKind":"linux-deb","runtimeKind":"self-contained"}\n' "$RID" > "$pkgroot/usr/lib/secrandom/SecRandom.package.json" | |
| chmod +x "$pkgroot/usr/lib/secrandom/SecRandom.Desktop" || true | |
| cat > "$pkgroot/usr/bin/secrandom" << 'EOF' | |
| #!/bin/sh | |
| exec /usr/lib/secrandom/SecRandom.Desktop "$@" | |
| EOF | |
| chmod +x "$pkgroot/usr/bin/secrandom" | |
| cat > "$pkgroot/usr/share/applications/secrandom.desktop" << 'EOF' | |
| [Desktop Entry] | |
| Type=Application | |
| Name=SecRandom | |
| Exec=secrandom | |
| Icon=secrandom | |
| Categories=Utility; | |
| Terminal=false | |
| EOF | |
| if [ -f "resources/secrandom-icon-paper.png" ]; then | |
| cp "resources/secrandom-icon-paper.png" "$pkgroot/usr/share/icons/hicolor/256x256/apps/secrandom.png" | |
| fi | |
| cat > "$pkgroot/DEBIAN/control" << EOF | |
| Package: secrandom | |
| Version: ${version_no_v} | |
| Section: utils | |
| Priority: optional | |
| Architecture: ${deb_arch} | |
| Maintainer: SECTL | |
| Description: SecRandom | |
| EOF | |
| dpkg-deb --build "$pkgroot" "${release_root}/dist/SecRandom-${VERSION}-${RID}.deb" | |
| - name: Build macOS pkg | |
| if: ${{ matrix.os == 'macos' }} | |
| env: | |
| ARCH: ${{ matrix.arch }} | |
| VERSION: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.release_tag || steps.get_tag.outputs.LATEST_TAG }} | |
| RID: ${{ matrix.os == 'windows' && format('win-{0}', matrix.arch) || (matrix.os == 'linux' && format('linux-{0}', matrix.arch) || format('osx-{0}', matrix.arch)) }} | |
| MACOS_SIGNING_P12_BASE64: ${{ secrets.MACOS_SIGNING_P12_BASE64 }} | |
| MACOS_SIGNING_P12_PASSWORD: ${{ secrets.MACOS_SIGNING_P12_PASSWORD }} | |
| MACOS_APPLICATION_SIGNING_IDENTITY: ${{ secrets.MACOS_APPLICATION_SIGNING_IDENTITY }} | |
| MACOS_INSTALLER_SIGNING_IDENTITY: ${{ secrets.MACOS_INSTALLER_SIGNING_IDENTITY }} | |
| MACOS_NOTARY_APPLE_ID: ${{ secrets.MACOS_NOTARY_APPLE_ID }} | |
| MACOS_NOTARY_TEAM_ID: ${{ secrets.MACOS_NOTARY_TEAM_ID }} | |
| MACOS_NOTARY_PASSWORD: ${{ secrets.MACOS_NOTARY_PASSWORD }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| version_no_v="${VERSION#v}" | |
| release_root="artifacts/release" | |
| mkdir -p "${release_root}/dist" | |
| work="$(pwd)/${release_root}/macos/${RID}" | |
| rm -rf "$work" | |
| mkdir -p "$work/bundle/SecRandom.app/Contents/MacOS" | |
| mkdir -p "$work/bundle/SecRandom.app/Contents/Resources" | |
| cp -a "${release_root}/publish/full/${RID}/." "$work/bundle/SecRandom.app/Contents/MacOS/" | |
| printf '{"schemaVersion":1,"product":"SecRandom","rid":"%s","packageKind":"macos-app","runtimeKind":"self-contained"}\n' "$RID" > "$work/bundle/SecRandom.app/Contents/MacOS/SecRandom.package.json" | |
| chmod +x "$work/bundle/SecRandom.app/Contents/MacOS/SecRandom.Desktop" || true | |
| cat > "$work/bundle/SecRandom.app/Contents/Info.plist" << EOF | |
| <?xml version="1.0" encoding="UTF-8"?> | |
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |
| <plist version="1.0"> | |
| <dict> | |
| <key>CFBundleName</key><string>SecRandom</string> | |
| <key>CFBundleDisplayName</key><string>SecRandom</string> | |
| <key>CFBundleIdentifier</key><string>top.sectl.secrandom</string> | |
| <key>CFBundleVersion</key><string>${version_no_v}</string> | |
| <key>CFBundleShortVersionString</key><string>${version_no_v}</string> | |
| <key>CFBundleExecutable</key><string>SecRandom.Desktop</string> | |
| <key>CFBundlePackageType</key><string>APPL</string> | |
| <key>LSMinimumSystemVersion</key><string>11.0</string> | |
| <key>CFBundleIconFile</key><string>AppIcon</string> | |
| </dict> | |
| </plist> | |
| EOF | |
| icon_png="resources/secrandom-icon-paper.png" | |
| if [ -f "$icon_png" ]; then | |
| iconset="$work/AppIcon.iconset" | |
| mkdir -p "$iconset" | |
| for size in 16 32 64 128 256 512; do | |
| sips -z "$size" "$size" "$icon_png" --out "$iconset/icon_${size}x${size}.png" >/dev/null | |
| done | |
| for size in 16 32 128 256 512; do | |
| double=$((size*2)) | |
| sips -z "$double" "$double" "$icon_png" --out "$iconset/icon_${size}x${size}@2x.png" >/dev/null | |
| done | |
| iconutil -c icns "$iconset" -o "$work/bundle/SecRandom.app/Contents/Resources/AppIcon.icns" | |
| fi | |
| if [ -z "$MACOS_SIGNING_P12_BASE64" ] || [ -z "$MACOS_SIGNING_P12_PASSWORD" ] || [ -z "$MACOS_APPLICATION_SIGNING_IDENTITY" ] || [ -z "$MACOS_INSTALLER_SIGNING_IDENTITY" ] || [ -z "$MACOS_NOTARY_APPLE_ID" ] || [ -z "$MACOS_NOTARY_TEAM_ID" ] || [ -z "$MACOS_NOTARY_PASSWORD" ]; then | |
| echo "macOS signing or notarization credentials are unavailable; keeping unsigned APP and skipping signed PKG." | |
| ditto -c -k --sequesterRsrc --keepParent \ | |
| "$work/bundle/SecRandom.app" \ | |
| "${release_root}/dist/SecRandom-${VERSION}-${RID}.app.zip" | |
| exit 0 | |
| fi | |
| keychain="$RUNNER_TEMP/secrandom-signing.keychain-db" | |
| certificate="$RUNNER_TEMP/secrandom-signing.p12" | |
| trap 'security delete-keychain "$keychain" >/dev/null 2>&1 || true; rm -f "$certificate"' EXIT | |
| echo "$MACOS_SIGNING_P12_BASE64" | base64 --decode > "$certificate" | |
| security create-keychain -p "$MACOS_SIGNING_P12_PASSWORD" "$keychain" | |
| security set-keychain-settings -lut 21600 "$keychain" | |
| security unlock-keychain -p "$MACOS_SIGNING_P12_PASSWORD" "$keychain" | |
| security import "$certificate" -k "$keychain" -P "$MACOS_SIGNING_P12_PASSWORD" -T /usr/bin/codesign -T /usr/bin/productbuild | |
| security list-keychain -d user -s "$keychain" | |
| security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$MACOS_SIGNING_P12_PASSWORD" "$keychain" | |
| codesign --force --deep --options runtime --sign "$MACOS_APPLICATION_SIGNING_IDENTITY" "$work/bundle/SecRandom.app" | |
| codesign --verify --deep --strict "$work/bundle/SecRandom.app" | |
| ditto -c -k --sequesterRsrc --keepParent \ | |
| "$work/bundle/SecRandom.app" \ | |
| "${release_root}/dist/SecRandom-${VERSION}-${RID}.app.zip" | |
| printf '{"schemaVersion":1,"product":"SecRandom","rid":"%s","packageKind":"macos-pkg","runtimeKind":"self-contained"}\n' "$RID" > "$work/bundle/SecRandom.app/Contents/MacOS/SecRandom.package.json" | |
| pkgroot="$work/pkgroot" | |
| mkdir -p "$pkgroot/Applications" | |
| cp -a "$work/bundle/SecRandom.app" "$pkgroot/Applications/" | |
| pkgbuild \ | |
| --root "$pkgroot" \ | |
| --identifier "top.sectl.secrandom" \ | |
| --version "$version_no_v" \ | |
| --install-location "/" \ | |
| --sign "$MACOS_INSTALLER_SIGNING_IDENTITY" \ | |
| "${release_root}/dist/SecRandom-${VERSION}-${RID}.pkg" | |
| xcrun notarytool submit "${release_root}/dist/SecRandom-${VERSION}-${RID}.pkg" --apple-id "$MACOS_NOTARY_APPLE_ID" --team-id "$MACOS_NOTARY_TEAM_ID" --password "$MACOS_NOTARY_PASSWORD" --wait | |
| xcrun stapler staple "${release_root}/dist/SecRandom-${VERSION}-${RID}.pkg" | |
| pkgutil --check-signature "${release_root}/dist/SecRandom-${VERSION}-${RID}.pkg" | |
| - name: Upload Portable Artifact | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: ${{ matrix.os == 'windows' && format('win-{0}', matrix.arch) || (matrix.os == 'linux' && format('linux-{0}', matrix.arch) || format('osx-{0}', matrix.arch)) }}_portable | |
| path: artifacts/release/dist/*.zip | |
| - name: Upload Setup Artifact | |
| if: ${{ matrix.os == 'windows' }} | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: ${{ matrix.os == 'windows' && format('win-{0}', matrix.arch) || (matrix.os == 'linux' && format('linux-{0}', matrix.arch) || format('osx-{0}', matrix.arch)) }}_setup | |
| path: artifacts/release/setup/*.exe | |
| - name: Upload deb Artifact | |
| if: ${{ matrix.os == 'linux' }} | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: ${{ matrix.os == 'windows' && format('win-{0}', matrix.arch) || (matrix.os == 'linux' && format('linux-{0}', matrix.arch) || format('osx-{0}', matrix.arch)) }}_deb | |
| path: artifacts/release/dist/*.deb | |
| - name: Upload pkg Artifact | |
| if: ${{ matrix.os == 'macos' }} | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: ${{ matrix.os == 'windows' && format('win-{0}', matrix.arch) || (matrix.os == 'linux' && format('linux-{0}', matrix.arch) || format('osx-{0}', matrix.arch)) }}_pkg | |
| path: artifacts/release/dist/*.pkg | |
| if-no-files-found: ignore | |
| build_mobile_shell: | |
| name: Build_Mobile_Android | |
| runs-on: ubuntu-24.04 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| target: [android] | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| submodules: recursive | |
| ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.release_tag || github.ref }} | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v5 | |
| with: | |
| dotnet-version: 10.0.102 | |
| - name: Pin Android SDK version | |
| shell: pwsh | |
| run: | | |
| @' | |
| { | |
| "sdk": { | |
| "version": "10.0.102", | |
| "rollForward": "disable" | |
| } | |
| } | |
| '@ | Set-Content -LiteralPath global.json -NoNewline | |
| dotnet --version | |
| - name: Get Latest Tag | |
| id: get_tag | |
| if: ${{ github.event_name != 'workflow_dispatch' }} | |
| shell: pwsh | |
| run: | | |
| $latestTag = git describe --tags --abbrev=0 | |
| "LATEST_TAG=$latestTag" >> $env:GITHUB_OUTPUT | |
| - name: Setup Android build tools | |
| if: ${{ matrix.target == 'android' }} | |
| uses: actions/setup-java@v5 | |
| with: | |
| distribution: temurin | |
| java-version: '21' | |
| - name: Install Android SDK packages | |
| if: ${{ matrix.target == 'android' }} | |
| shell: pwsh | |
| run: | | |
| & "$env:ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" "platform-tools" "platforms;android-36" "build-tools;36.0.0" | |
| - name: Install mobile workload | |
| shell: pwsh | |
| run: dotnet workload install ${{ matrix.target }} | |
| - name: Build mobile shell | |
| shell: pwsh | |
| env: | |
| VERSION: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.release_tag || steps.get_tag.outputs.LATEST_TAG }} | |
| ANDROID_SIGNING_KEYSTORE_BASE64: ${{ secrets.ANDROID_SIGNING_KEYSTORE_BASE64 }} | |
| ANDROID_SIGNING_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_SIGNING_KEYSTORE_PASSWORD }} | |
| ANDROID_SIGNING_KEY_ALIAS: ${{ secrets.ANDROID_SIGNING_KEY_ALIAS }} | |
| ANDROID_SIGNING_KEY_PASSWORD: ${{ secrets.ANDROID_SIGNING_KEY_PASSWORD }} | |
| run: | | |
| $version = $env:VERSION.TrimStart('v', 'V') | |
| $arguments = @('build', 'SecRandom.Android/SecRandom.Android.csproj', '-c', 'Release', '-r', 'android-arm64', '-p:BuildMobile=true', "-p:Version=$version", '-p:ApplicationVersion=${{ github.run_number }}') | |
| $hasSigningSecrets = @($env:ANDROID_SIGNING_KEYSTORE_BASE64, $env:ANDROID_SIGNING_KEYSTORE_PASSWORD, $env:ANDROID_SIGNING_KEY_ALIAS, $env:ANDROID_SIGNING_KEY_PASSWORD) | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | |
| if ($hasSigningSecrets.Count -gt 0 -and $hasSigningSecrets.Count -ne 4) { | |
| throw 'All Android signing secrets must be configured together.' | |
| } | |
| if ($hasSigningSecrets.Count -eq 4) { | |
| $keystorePath = Join-Path $env:RUNNER_TEMP 'secrandom-android.keystore' | |
| [IO.File]::WriteAllBytes($keystorePath, [Convert]::FromBase64String($env:ANDROID_SIGNING_KEYSTORE_BASE64)) | |
| $arguments += @('-p:AndroidKeyStore=true', "-p:AndroidSigningKeyStore=$keystorePath", "-p:AndroidSigningStorePass=$env:ANDROID_SIGNING_KEYSTORE_PASSWORD", "-p:AndroidSigningKeyAlias=$env:ANDROID_SIGNING_KEY_ALIAS", "-p:AndroidSigningKeyPass=$env:ANDROID_SIGNING_KEY_PASSWORD") | |
| } | |
| elseif ('${{ github.event_name }}' -eq 'workflow_dispatch') { | |
| throw 'Release publication requires all Android signing secrets.' | |
| } | |
| & dotnet @arguments | |
| if ($LASTEXITCODE -ne 0) { | |
| throw "Android build failed with exit code $LASTEXITCODE." | |
| } | |
| - name: Prepare Android APK | |
| shell: pwsh | |
| env: | |
| VERSION: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.release_tag || steps.get_tag.outputs.LATEST_TAG }} | |
| run: | | |
| $apk = Get-ChildItem 'SecRandom.Android/bin/Release/net10.0-android/android-arm64' -Filter '*-Signed.apk' | | |
| Select-Object -First 1 -ExpandProperty FullName | |
| if ([string]::IsNullOrWhiteSpace($apk)) { | |
| throw 'The Android build did not produce a signed APK.' | |
| } | |
| New-Item -ItemType Directory -Path 'artifacts/mobile' -Force | Out-Null | |
| Copy-Item $apk "artifacts/mobile/SecRandom-v$($env:VERSION.TrimStart('v', 'V'))-Android-arm64.apk" | |
| - name: Upload Android APK | |
| if: ${{ matrix.target == 'android' }} | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: SecRandom-Android-${{ github.event_name == 'workflow_dispatch' && github.event.inputs.release_tag || steps.get_tag.outputs.LATEST_TAG }} | |
| path: artifacts/mobile/SecRandom-v*-Android-arm64.apk | |
| retention-days: 14 | |
| if-no-files-found: error | |
| build_mobile_ios: | |
| name: Build_Mobile_iOS | |
| runs-on: macos-26 | |
| env: | |
| DEVELOPER_DIR: /Applications/Xcode_26.2.app/Contents/Developer | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| submodules: recursive | |
| persist-credentials: false | |
| ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.release_tag || github.ref }} | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v5 | |
| with: | |
| dotnet-version: 10.0.102 | |
| - name: Get Latest Tag | |
| id: get_tag | |
| if: ${{ github.event_name != 'workflow_dispatch' }} | |
| shell: bash | |
| run: echo "LATEST_TAG=$(git describe --tags --abbrev=0)" >> "$GITHUB_OUTPUT" | |
| - name: Pin iOS SDK version | |
| shell: bash | |
| run: | | |
| cat > global.json <<'EOF' | |
| { | |
| "sdk": { | |
| "version": "10.0.102", | |
| "rollForward": "disable" | |
| } | |
| } | |
| EOF | |
| dotnet --version | |
| - name: Verify iOS toolchain | |
| shell: bash | |
| run: | | |
| test "$(uname -m)" = arm64 | |
| test -d "$DEVELOPER_DIR" | |
| xcodebuild -version | |
| dotnet --version | |
| - name: Install iOS workload | |
| shell: bash | |
| run: dotnet workload install ios --version 10.0.101.1 | |
| - name: Build iOS simulator target | |
| shell: bash | |
| env: | |
| VERSION: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.release_tag || steps.get_tag.outputs.LATEST_TAG }} | |
| run: > | |
| dotnet build SecRandom.iOS/SecRandom.iOS.csproj | |
| -c Release | |
| -r iossimulator-arm64 | |
| -p:BuildMobile=true | |
| -p:Version=${VERSION#v} | |
| -p:EnableCodeSigning=false | |
| -p:CodesignRequireProvisioningProfile=false | |
| -p:BuildIpa=false | |
| -p:ArchiveOnBuild=false | |
| -p:EnableAssemblyILStripping=false | |
| - name: Publish unsigned iOS arm64 IPA | |
| shell: bash | |
| env: | |
| VERSION: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.release_tag || steps.get_tag.outputs.LATEST_TAG }} | |
| run: | | |
| version="${VERSION#v}" | |
| version="${version#V}" | |
| output="artifacts/mobile/SecRandom-v${version}-iOS-arm64-unsigned.ipa" | |
| mkdir -p artifacts/mobile | |
| dotnet publish SecRandom.iOS/SecRandom.iOS.csproj \ | |
| -c Release \ | |
| -f net10.0-ios \ | |
| -r ios-arm64 \ | |
| -p:BuildMobile=true \ | |
| -p:Version="$version" \ | |
| -p:ApplicationVersion=${{ github.run_number }} \ | |
| -p:EnableCodeSigning=false \ | |
| -p:CodesignRequireProvisioningProfile=false \ | |
| -p:BuildIpa=true \ | |
| -p:ArchiveOnBuild=false \ | |
| -p:EnableAssemblyILStripping=false \ | |
| -p:IpaPackagePath="$GITHUB_WORKSPACE/$output" | |
| test -s "$output" | |
| unzip -l "$output" | grep -q 'Payload/.*\.app/Info.plist' | |
| - name: Upload unsigned iOS IPA | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: SecRandom-iOS-${{ github.event_name == 'workflow_dispatch' && github.event.inputs.release_tag || steps.get_tag.outputs.LATEST_TAG }}-unsigned | |
| path: artifacts/mobile/SecRandom-v*-iOS-arm64-unsigned.ipa | |
| retention-days: 14 | |
| if-no-files-found: error | |
| publish: | |
| name: Publish_Release | |
| runs-on: windows-2022 | |
| needs: [build_desktop, build_mobile_shell, build_mobile_ios] | |
| if: ${{ always() && success('build_desktop') && success('build_mobile_shell') && success('build_mobile_ios') && github.event_name == 'workflow_dispatch' && github.event.inputs.release_tag && github.event_name != 'pull_request' }} | |
| permissions: | |
| contents: write | |
| concurrency: | |
| group: publish-public | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| submodules: recursive | |
| ref: ${{ github.event.inputs.release_tag }} | |
| - name: Download Artifacts | |
| uses: actions/download-artifact@v8 | |
| with: | |
| path: ./artifacts/release/downloaded | |
| - name: Combine Artifacts | |
| shell: pwsh | |
| run: | | |
| $releaseOutput = './artifacts/release/output' | |
| New-Item -ItemType Directory -Path $releaseOutput -Force | Out-Null | |
| Get-ChildItem ./artifacts/release/downloaded -Recurse -File -Include *.zip,*.exe,*.deb,*.pkg,*.apk,*.ipa | ForEach-Object { | |
| Copy-Item $_.FullName $releaseOutput -Force | |
| } | |
| Write-Host "Combined files:" | |
| Get-ChildItem $releaseOutput -File | Select-Object Name, Length | |
| - name: Generate signed update manifest | |
| shell: pwsh | |
| env: | |
| RELEASE_TAG: ${{ github.event.inputs.release_tag }} | |
| UPDATE_MANIFEST_PRIVATE_KEY_PEM_BASE64: ${{ secrets.UPDATE_MANIFEST_PRIVATE_KEY_PEM_BASE64 }} | |
| run: | | |
| if ([string]::IsNullOrWhiteSpace($env:UPDATE_MANIFEST_PRIVATE_KEY_PEM_BASE64)) { | |
| throw 'Release publication requires the UPDATE_MANIFEST_PRIVATE_KEY_PEM_BASE64 secret.' | |
| } | |
| $privateKeyPath = Join-Path $env:RUNNER_TEMP 'secrandom-update-ed25519.pem' | |
| $publicDerPath = Join-Path $env:RUNNER_TEMP 'secrandom-update-ed25519-public.der' | |
| try { | |
| [IO.File]::WriteAllBytes($privateKeyPath, [Convert]::FromBase64String($env:UPDATE_MANIFEST_PRIVATE_KEY_PEM_BASE64)) | |
| & openssl pkey -in $privateKeyPath -pubout -outform DER -out $publicDerPath | |
| if ($LASTEXITCODE -ne 0) { throw "OpenSSL Ed25519 public-key export failed with exit code $LASTEXITCODE." } | |
| $publicDer = [IO.File]::ReadAllBytes($publicDerPath) | |
| if ($publicDer.Length -lt 32) { throw 'The update-signing public key is invalid.' } | |
| $derivedPublicKey = [Convert]::ToBase64String($publicDer[($publicDer.Length - 32)..($publicDer.Length - 1)]) | |
| $embeddedPublicKey = (Get-Content -LiteralPath 'SecRandom/Assets/Updates/release-public-key.txt' -Raw).Trim() | |
| if ($derivedPublicKey -cne $embeddedPublicKey) { | |
| throw 'UPDATE_MANIFEST_PRIVATE_KEY_PEM_BASE64 does not match SecRandom/Assets/Updates/release-public-key.txt.' | |
| } | |
| $releaseTag = $env:RELEASE_TAG | |
| $channel = if ($releaseTag -match '-alpha(?:\.|$)') { 'alpha' } elseif ($releaseTag -match '-beta(?:\.|$)') { 'beta' } elseif ($releaseTag -match '-') { throw "Unsupported prerelease tag: $releaseTag" } else { 'release' } | |
| $metadata = Get-Content -LiteralPath metadata.yaml -Raw | |
| $channelMatch = [regex]::Match($metadata, "(?ms)^ $channel:\s*\r?\n\s+tag:\s*(?<tag>\S+)\s*$") | |
| if (-not $channelMatch.Success -or $channelMatch.Groups['tag'].Value -ne $releaseTag) { | |
| throw "metadata.yaml channel '$channel' must point to release tag $releaseTag." | |
| } | |
| $releaseOutput = './artifacts/release/output' | |
| $artifacts = Get-ChildItem $releaseOutput -File | ForEach-Object { | |
| $name = $_.Name | |
| $kind, $runtimeKind, $os, $arch = if ($name -match '^SecRandom-v.+-(?<rid>(win|linux|osx)-(?<arch>x64|x86|arm64))-portable-(?<runtime>full|light)\.zip$') { | |
| $platform = switch ($Matches[2]) { 'win' { 'windows' } 'linux' { 'linux' } 'osx' { 'macos' } } | |
| @('portable-zip', $(if ($Matches.runtime -eq 'full') { 'self-contained' } else { 'framework-dependent' }), $platform, $Matches.arch) | |
| } elseif ($name -match '^SecRandom-v.+-(?<rid>win-(?<arch>x64|x86|arm64))-setup\.exe$') { | |
| @('windows-exe', 'self-contained', 'windows', $Matches.arch) | |
| } elseif ($name -match '^SecRandom-v.+-(?<rid>linux-(?<arch>x64|arm64))\.deb$') { | |
| @('linux-deb', 'self-contained', 'linux', $Matches.arch) | |
| } elseif ($name -match '^SecRandom-v.+-(?<rid>osx-(?<arch>x64|arm64))\.pkg$') { | |
| @('macos-pkg', 'self-contained', 'macos', $Matches.arch) | |
| } elseif ($name -match '^SecRandom-v.+-(?<rid>osx-(?<arch>x64|arm64))\.app\.zip$') { | |
| @('macos-app', 'self-contained', 'macos', $Matches.arch) | |
| } elseif ($name -match '^SecRandom-v.+-Android-(?<arch>arm64)\.apk$') { | |
| @('android-apk', 'self-contained', 'android', $Matches.arch) | |
| } elseif ($name -match '^SecRandom-v.+-iOS-(?<arch>arm64)-unsigned\.ipa$') { | |
| @('ios-ipa', 'self-contained', 'ios', $Matches.arch) | |
| } else { | |
| throw "Unrecognized release artifact name: $name" | |
| } | |
| [ordered]@{ | |
| id = "$os-$arch-$kind-$runtimeKind" | |
| os = $os | |
| arch = $arch | |
| kind = $kind | |
| runtimeKind = $runtimeKind | |
| assetName = $name | |
| byteLength = $_.Length | |
| sha512 = (Get-FileHash -LiteralPath $_.FullName -Algorithm SHA512).Hash | |
| } | |
| } | |
| $manifest = [ordered]@{ | |
| schemaVersion = 1 | |
| product = 'SecRandom' | |
| channel = $channel | |
| tag = $releaseTag | |
| version = $releaseTag.TrimStart('v') | |
| publishedAt = [DateTimeOffset]::UtcNow.ToString('O') | |
| artifacts = @($artifacts) | |
| } | |
| $manifestPath = "$releaseOutput/SecRandom-update-manifest.v1.json" | |
| $signaturePath = "$releaseOutput/SecRandom-update-manifest.v1.sig" | |
| $manifest | ConvertTo-Json -Depth 5 -Compress | Set-Content -LiteralPath $manifestPath -Encoding utf8NoBOM -NoNewline | |
| & openssl pkeyutl -sign -rawin -inkey $privateKeyPath -in $manifestPath -out $signaturePath | |
| if ($LASTEXITCODE -ne 0) { throw "OpenSSL Ed25519 signing failed with exit code $LASTEXITCODE." } | |
| } | |
| finally { | |
| Remove-Item $privateKeyPath -Force -ErrorAction SilentlyContinue | |
| Remove-Item $publicDerPath -Force -ErrorAction SilentlyContinue | |
| } | |
| - name: Generate Release Note | |
| env: | |
| tagName: ${{ github.event.inputs.release_tag }} | |
| repoName: ${{ github.repository }} | |
| run: pwsh -ep bypass ./scripts/gen-release-note.ps1 | |
| - name: Upload APP to release | |
| uses: ncipollo/release-action@v1 | |
| with: | |
| name: SecRandom ${{ github.event.inputs.release_tag }} | |
| artifacts: "./artifacts/release/output/*.zip,./artifacts/release/output/*.exe,./artifacts/release/output/*.deb,./artifacts/release/output/*.pkg,./artifacts/release/output/*.apk,./artifacts/release/output/*.ipa,./artifacts/release/output/SecRandom-update-manifest.v1.json,./artifacts/release/output/SecRandom-update-manifest.v1.sig" | |
| draft: true | |
| bodyFile: ./release-note.md | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| tag: ${{ github.event.inputs.release_tag }} |