Skip to content

Commit 87f1c48

Browse files
committed
update
1 parent 7601180 commit 87f1c48

18 files changed

Lines changed: 2160 additions & 37 deletions

.env.example

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
APPSTORE_CONNECT_API_KEY_PATH=/Users/your-user/opensource/AuthKey_XXXXXXXXXX.p8
2+
APPSTORE_CONNECT_API_KEY_ID=XXXXXXXXXX
3+
APPSTORE_CONNECT_API_ISSUER_ID=00000000-0000-0000-0000-000000000000
4+
PLATYPUS_APP_PATH=build/PlatypusGui.app
5+
PLATYPUS_DMG_PATH=build/PlatypusGui.dmg
6+
PLATYPUS_DMG_VOLNAME=Platypus

.github/workflows/build-and-release-windows.yml

Lines changed: 78 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
1-
name: Build and Release PlatypusGui
1+
name: Build and Release Platypus Windows Installer
22

33
on:
4+
workflow_dispatch:
45
push:
5-
branches:
6-
- main
6+
tags:
7+
- 'v*'
8+
9+
permissions:
10+
contents: write
11+
id-token: write
712

813
jobs:
914
build:
10-
name: Build and Upload Artifacts
11-
runs-on: windows-latest
15+
name: Build, Sign, and Release Windows Installer
16+
runs-on: windows-2022
17+
environment: release-windows
1218
env:
1319
# Indicates the location of the vcpkg as a Git submodule of the project repository.
1420
# Not using "VCPKG_ROOT" because a variable with the same name is defined in the VS's
@@ -52,27 +58,81 @@ jobs:
5258
# As used here the Developer Command Prompt created is targeting x64 and using the default the Windows SDK.
5359
- uses: ilammy/msvc-dev-cmd@v1
5460

55-
# Configure CMake (Windows)
61+
- name: Install NSIS
62+
shell: pwsh
63+
run: |
64+
choco install nsis -y --no-progress
65+
66+
- name: Install Artifact Signing Client Tools
67+
shell: pwsh
68+
run: |
69+
./scripts/windows_install_artifact_signing_tools.ps1
70+
71+
- name: Azure Login
72+
uses: azure/login@v2
73+
with:
74+
client-id: ${{ vars.AZURE_CLIENT_ID }}
75+
tenant-id: ${{ vars.AZURE_TENANT_ID }}
76+
subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}
77+
5678
- name: Configure CMake (Windows)
5779
shell: cmd
5880
run: |
59-
cmake --preset ninja-multi-vcpkg -B build -S . -G Ninja -DCMAKE_BUILD_TYPE=Release
81+
set PACKAGE_VERSION=0.0.1-local
82+
if "%GITHUB_REF_TYPE%"=="tag" set PACKAGE_VERSION=%GITHUB_REF_NAME:v=%
83+
cmake --preset ninja-multi-vcpkg -B build -S . -G Ninja -DCMAKE_BUILD_TYPE=Release -DPLATYPUS_PACKAGE_VERSION_OVERRIDE=%PACKAGE_VERSION%
6084
61-
# 4. Build the project
62-
- name: Build PlatypusGui
85+
- name: Write Trusted Signing Metadata
86+
shell: pwsh
6387
run: |
64-
cd build && ninja PlatypusGui
88+
New-Item -ItemType Directory -Force -Path build | Out-Null
89+
@"
90+
{
91+
"Endpoint": "${{ vars.TRUSTED_SIGNING_ENDPOINT }}",
92+
"CodeSigningAccountName": "${{ vars.TRUSTED_SIGNING_ACCOUNT_NAME }}",
93+
"CertificateProfileName": "${{ vars.TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME }}",
94+
"CorrelationId": "${{ github.run_id }}",
95+
"ExcludeCredentials": [
96+
"EnvironmentCredential",
97+
"ManagedIdentityCredential",
98+
"WorkloadIdentityCredential",
99+
"SharedTokenCacheCredential",
100+
"VisualStudioCredential",
101+
"VisualStudioCodeCredential",
102+
"AzurePowerShellCredential",
103+
"AzureDeveloperCliCredential",
104+
"InteractiveBrowserCredential"
105+
]
106+
}
107+
"@ | Set-Content -Path build\trusted-signing-metadata.json -Encoding utf8NoBOM
65108
66-
- name: Deploy Qt Dependencies (Windows)
67-
shell: cmd
109+
- name: Build and Package Windows Installer
110+
shell: pwsh
68111
run: |
69-
set PATH=%PATH%;%CD%\vcpkg_installed\x64-windows\tools\qttools\bin
70-
windeployqt.exe --compiler-runtime --dir build\deployment build\PlatypusGui.exe
112+
./scripts/windows_package.ps1 `
113+
-BuildDir build `
114+
-Configuration Release `
115+
-PackageDir build\package `
116+
-Sign `
117+
-SigningMetadataPath build\trusted-signing-metadata.json
71118
72-
# 6. Upload artifacts for release
73-
- name: Upload Release Artifacts
119+
- name: Upload Installer Artifacts
74120
uses: actions/upload-artifact@v3
75121
with:
76-
name: windows-latest-platypus
77-
path: build\deployment\PlatypusGui.exe
122+
name: platypus-windows-installer
123+
path: |
124+
build\package\*.exe
125+
build\package\*.sha256
78126
127+
- name: Publish GitHub Release
128+
if: startsWith(github.ref, 'refs/tags/')
129+
shell: pwsh
130+
env:
131+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
132+
run: |
133+
$tag = "${{ github.ref_name }}"
134+
$installer = Get-ChildItem -Path build\package -Filter *.exe | Select-Object -First 1
135+
$checksum = Get-ChildItem -Path build\package -Filter *.sha256 | Select-Object -First 1
136+
if (-not $installer) { throw "Installer artifact not found." }
137+
if (-not $checksum) { throw "Checksum artifact not found." }
138+
gh release create $tag $installer.FullName $checksum.FullName --title "Platypus $tag" --notes "Signed Windows installer for $tag"
Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
name: Release macOS
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
tag:
7+
description: Release tag to create and publish, for example v0.1.0
8+
required: true
9+
type: string
10+
target_ref:
11+
description: Branch or commit SHA to release
12+
required: false
13+
default: main
14+
type: string
15+
prerelease:
16+
description: Mark the GitHub Release as a prerelease
17+
required: false
18+
default: false
19+
type: boolean
20+
21+
permissions:
22+
contents: write
23+
24+
concurrency:
25+
group: release-macos-${{ github.event.inputs.tag }}
26+
cancel-in-progress: false
27+
28+
jobs:
29+
release:
30+
name: Build, sign, notarize, and publish macOS release
31+
runs-on: macos-latest
32+
environment: release-macos
33+
env:
34+
GH_TOKEN: ${{ github.token }}
35+
_VCPKG_: ${{ github.workspace }}/vcpkg
36+
VCPKG_DEFAULT_BINARY_CACHE: ${{ github.workspace }}/vcpkg/bincache
37+
VCPKG_BINARY_SOURCES: clear;x-gha,readwrite
38+
APP_NAME: PlatypusGui
39+
DMG_NAME: Platypus-macos-universal.dmg
40+
41+
steps:
42+
- uses: actions/github-script@v7
43+
with:
44+
script: |
45+
core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || '');
46+
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
47+
48+
- name: Checkout code
49+
uses: actions/checkout@v4
50+
with:
51+
ref: ${{ github.event.inputs.target_ref }}
52+
fetch-depth: 0
53+
submodules: true
54+
55+
- name: Validate release tag
56+
shell: bash
57+
run: |
58+
set -euo pipefail
59+
TAG="${{ github.event.inputs.tag }}"
60+
if [[ ! "$TAG" =~ ^v[0-9]+(\.[0-9]+)*([.-][A-Za-z0-9._-]+)?$ ]]; then
61+
echo "Release tag must look like v0.1.0 or v0.1.0-rc1" >&2
62+
exit 1
63+
fi
64+
65+
- name: Check for existing release
66+
shell: bash
67+
run: |
68+
set -euo pipefail
69+
TAG="${{ github.event.inputs.tag }}"
70+
if gh release view "$TAG" >/dev/null 2>&1; then
71+
echo "Release $TAG already exists" >&2
72+
exit 1
73+
fi
74+
75+
- name: Create tag if needed
76+
shell: bash
77+
run: |
78+
set -euo pipefail
79+
TAG="${{ github.event.inputs.tag }}"
80+
TARGET_REF="${{ github.event.inputs.target_ref }}"
81+
if ! git ls-remote --exit-code --tags origin "refs/tags/$TAG" >/dev/null 2>&1; then
82+
git tag "$TAG" "$TARGET_REF"
83+
git push origin "refs/tags/$TAG"
84+
fi
85+
86+
- name: Prepare vcpkg cache directory
87+
shell: bash
88+
run: mkdir -p "$VCPKG_DEFAULT_BINARY_CACHE"
89+
90+
- name: Get CMake
91+
uses: lukka/get-cmake@latest
92+
93+
- name: Get Ninja
94+
uses: seanmiddleditch/gha-setup-ninja@master
95+
96+
- name: Restore from cache and setup vcpkg
97+
uses: lukka/run-vcpkg@v11
98+
with:
99+
vcpkgJsonGlob: vcpkg.json
100+
101+
- name: Import Apple signing certificate
102+
shell: bash
103+
env:
104+
MACOS_CERT_P12_BASE64: ${{ secrets.MACOS_CERT_P12_BASE64 }}
105+
MACOS_CERT_P12_PASSWORD: ${{ secrets.MACOS_CERT_P12_PASSWORD }}
106+
run: |
107+
set -euo pipefail
108+
CERT_PATH="$RUNNER_TEMP/dev_app.p12"
109+
KEYCHAIN_PATH="$RUNNER_TEMP/platypus-build.keychain-db"
110+
KEYCHAIN_PASSWORD="$(uuidgen)"
111+
112+
echo "$MACOS_CERT_P12_BASE64" | base64 --decode > "$CERT_PATH"
113+
114+
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
115+
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
116+
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
117+
security import "$CERT_PATH" -k "$KEYCHAIN_PATH" -P "$MACOS_CERT_P12_PASSWORD" -T /usr/bin/codesign -T /usr/bin/security
118+
security list-keychains -d user -s "$KEYCHAIN_PATH" $(security list-keychains -d user | tr -d '"')
119+
security default-keychain -d user -s "$KEYCHAIN_PATH"
120+
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
121+
122+
{
123+
echo "KEYCHAIN_PATH=$KEYCHAIN_PATH"
124+
echo "KEYCHAIN_PASSWORD=$KEYCHAIN_PASSWORD"
125+
} >> "$GITHUB_ENV"
126+
127+
- name: Materialize App Store Connect API key
128+
shell: bash
129+
env:
130+
APPSTORE_CONNECT_API_KEY_BASE64: ${{ secrets.APPSTORE_CONNECT_API_KEY_BASE64 }}
131+
run: |
132+
set -euo pipefail
133+
API_KEY_PATH="$RUNNER_TEMP/AuthKey_${{ secrets.APPSTORE_CONNECT_API_KEY_ID }}.p8"
134+
echo "$APPSTORE_CONNECT_API_KEY_BASE64" | base64 --decode > "$API_KEY_PATH"
135+
echo "APPSTORE_CONNECT_API_KEY_PATH=$API_KEY_PATH" >> "$GITHUB_ENV"
136+
137+
- name: Configure CMake
138+
shell: bash
139+
run: |
140+
set -euo pipefail
141+
PACKAGE_VERSION="${{ github.event.inputs.tag }}"
142+
PACKAGE_VERSION="${PACKAGE_VERSION#v}"
143+
cmake -B build -S . -G Ninja \
144+
-DCMAKE_BUILD_TYPE=Release \
145+
-DBUILD_APPLE_APP=ON \
146+
-DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" \
147+
-DVCPKG_OSX_ARCHITECTURES="x86_64;arm64" \
148+
-DPLATYPUS_PACKAGE_VERSION_OVERRIDE="$PACKAGE_VERSION" \
149+
-DCMAKE_TOOLCHAIN_FILE="${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake"
150+
151+
- name: Build PlatypusGui
152+
shell: bash
153+
run: cmake --build build --config Release --target PlatypusGui
154+
155+
- name: Verify universal binary
156+
shell: bash
157+
run: |
158+
set -euo pipefail
159+
APP_BIN="build/PlatypusGui.app/Contents/MacOS/PlatypusGui"
160+
APP_ARCHS="$(lipo -archs "$APP_BIN")"
161+
echo "App architectures: $APP_ARCHS"
162+
[[ "$APP_ARCHS" == *"x86_64"* ]] || { echo "Missing x86_64 slice" >&2; exit 1; }
163+
[[ "$APP_ARCHS" == *"arm64"* ]] || { echo "Missing arm64 slice" >&2; exit 1; }
164+
165+
- name: Locate macdeployqt
166+
shell: bash
167+
run: |
168+
set -euo pipefail
169+
MACDEPLOYQT_BIN="$(find "$GITHUB_WORKSPACE/vcpkg_installed" -type f -name macdeployqt | head -n 1 || true)"
170+
if [[ -z "$MACDEPLOYQT_BIN" ]]; then
171+
MACDEPLOYQT_BIN="$(command -v macdeployqt || true)"
172+
fi
173+
if [[ -z "$MACDEPLOYQT_BIN" ]]; then
174+
echo "Unable to locate macdeployqt" >&2
175+
exit 1
176+
fi
177+
echo "MACDEPLOYQT_BIN=$MACDEPLOYQT_BIN" >> "$GITHUB_ENV"
178+
179+
- name: Sign app bundle
180+
shell: bash
181+
run: |
182+
set -euo pipefail
183+
bash scripts/mac_sign_app.sh \
184+
--app build/PlatypusGui.app \
185+
--identity "${{ vars.APPLE_SIGNING_IDENTITY }}" \
186+
--macdeployqt "$MACDEPLOYQT_BIN"
187+
188+
- name: Create DMG payload
189+
shell: bash
190+
run: |
191+
set -euo pipefail
192+
STAGING_DIR="$RUNNER_TEMP/dmg-root"
193+
rm -rf "$STAGING_DIR"
194+
mkdir -p "$STAGING_DIR"
195+
cp -R build/PlatypusGui.app "$STAGING_DIR/"
196+
ln -s /Applications "$STAGING_DIR/Applications"
197+
hdiutil create \
198+
-volname "Platypus" \
199+
-srcfolder "$STAGING_DIR" \
200+
-ov \
201+
-format UDZO \
202+
"build/$DMG_NAME"
203+
204+
- name: Notarize DMG
205+
shell: bash
206+
run: |
207+
set -euo pipefail
208+
xcrun notarytool submit "build/$DMG_NAME" \
209+
--key "$APPSTORE_CONNECT_API_KEY_PATH" \
210+
--key-id "${{ secrets.APPSTORE_CONNECT_API_KEY_ID }}" \
211+
--issuer "${{ secrets.APPSTORE_CONNECT_API_ISSUER_ID }}" \
212+
--wait
213+
214+
- name: Staple DMG
215+
shell: bash
216+
run: |
217+
set -euo pipefail
218+
xcrun stapler staple "build/$DMG_NAME"
219+
spctl -a -t open --context context:primary-signature -v "build/$DMG_NAME"
220+
221+
- name: Generate checksums
222+
shell: bash
223+
run: |
224+
set -euo pipefail
225+
(
226+
cd build
227+
shasum -a 256 "$DMG_NAME" > SHA256SUMS.txt
228+
)
229+
230+
- name: Publish GitHub Release
231+
shell: bash
232+
run: |
233+
set -euo pipefail
234+
TAG="${{ github.event.inputs.tag }}"
235+
RELEASE_ARGS=()
236+
if [[ "${{ github.event.inputs.prerelease }}" == "true" ]]; then
237+
RELEASE_ARGS+=(--prerelease)
238+
fi
239+
gh release create "$TAG" \
240+
"build/$DMG_NAME" \
241+
"build/SHA256SUMS.txt" \
242+
--target "${{ github.event.inputs.target_ref }}" \
243+
--title "$TAG" \
244+
--generate-notes \
245+
"${RELEASE_ARGS[@]}"
246+
247+
- name: Upload workflow artifacts
248+
uses: actions/upload-artifact@v4
249+
with:
250+
name: macos-release-artifacts
251+
path: |
252+
build/PlatypusGui.app
253+
build/${{ env.DMG_NAME }}
254+
build/SHA256SUMS.txt
255+
256+
- name: Cleanup temporary keychain
257+
if: always()
258+
shell: bash
259+
run: |
260+
set +e
261+
if [[ -n "${KEYCHAIN_PATH:-}" ]]; then
262+
security delete-keychain "$KEYCHAIN_PATH"
263+
fi

0 commit comments

Comments
 (0)