Release macOS v0.3.5 #26
Workflow file for this run
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: Release macOS | |
| run-name: >- | |
| Release macOS ${{ github.event_name == 'push' && github.ref_name || github.event.inputs.tag }} | |
| on: | |
| push: | |
| tags: | |
| - 'v*' | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: Release tag to create and publish, for example v0.1.0 | |
| required: true | |
| type: string | |
| target_ref: | |
| description: Branch or commit SHA to release | |
| required: false | |
| default: main | |
| type: string | |
| prerelease: | |
| description: Mark the GitHub Release as a prerelease | |
| required: false | |
| default: false | |
| type: boolean | |
| permissions: | |
| contents: write | |
| concurrency: | |
| group: release-macos-${{ github.event_name == 'push' && github.ref_name || github.event.inputs.tag }} | |
| cancel-in-progress: false | |
| jobs: | |
| prepare: | |
| name: Prepare release tag | |
| runs-on: ubuntu-latest | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| outputs: | |
| tag: ${{ steps.meta.outputs.tag }} | |
| package_version: ${{ steps.package_version.outputs.value }} | |
| target_sha: ${{ steps.resolve_target.outputs.sha }} | |
| prerelease: ${{ steps.meta.outputs.prerelease }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: main | |
| fetch-depth: 0 | |
| - name: Derive release metadata | |
| id: meta | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| if [[ "${GITHUB_EVENT_NAME}" == "push" ]]; then | |
| TAG="${GITHUB_REF_NAME}" | |
| PRERELEASE=false | |
| if [[ "$TAG" == *-* ]]; then | |
| PRERELEASE=true | |
| fi | |
| else | |
| TAG="${{ github.event.inputs.tag }}" | |
| PRERELEASE="${{ github.event.inputs.prerelease }}" | |
| fi | |
| echo "tag=${TAG}" >> "$GITHUB_OUTPUT" | |
| echo "prerelease=${PRERELEASE}" >> "$GITHUB_OUTPUT" | |
| - name: Validate release tag | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| TAG="${{ steps.meta.outputs.tag }}" | |
| if [[ ! "$TAG" =~ ^v[0-9]+(\.[0-9]+)*([.-][A-Za-z0-9._-]+)?$ ]]; then | |
| echo "Release tag must look like v0.1.0 or v0.1.0-rc1" >&2 | |
| exit 1 | |
| fi | |
| - name: Create tag if needed | |
| if: ${{ github.event_name == 'workflow_dispatch' }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| TAG="${{ steps.meta.outputs.tag }}" | |
| TARGET_REF="${{ github.event.inputs.target_ref }}" | |
| git fetch --tags origin | |
| if ! git ls-remote --exit-code --tags origin "refs/tags/$TAG" >/dev/null 2>&1; then | |
| TARGET_SHA="$(git rev-parse --verify "${TARGET_REF}^{commit}")" | |
| git tag "$TAG" "${TARGET_SHA}" | |
| git push origin "refs/tags/$TAG" | |
| fi | |
| - name: Derive package version | |
| id: package_version | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| PACKAGE_VERSION="${{ steps.meta.outputs.tag }}" | |
| PACKAGE_VERSION="${PACKAGE_VERSION#v}" | |
| echo "value=${PACKAGE_VERSION}" >> "$GITHUB_OUTPUT" | |
| - name: Resolve target ref | |
| id: resolve_target | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| TAG="${{ steps.meta.outputs.tag }}" | |
| git fetch --tags origin | |
| TARGET_SHA="$(git rev-parse --verify "${TAG}^{commit}")" | |
| echo "sha=${TARGET_SHA}" >> "$GITHUB_OUTPUT" | |
| build: | |
| name: Build, sign, and notarize (${{ matrix.arch }}) | |
| needs: prepare | |
| runs-on: ${{ matrix.runner }} | |
| environment: release-macos | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - arch: arm64 | |
| runner: macos-15 | |
| cmake_arch: arm64 | |
| dmg_name: Platypus-macos-arm64.dmg | |
| checksum_name: Platypus-macos-arm64.sha256 | |
| photoshop_dmg_name: Platypus-photoshop-macos-arm64.dmg | |
| photoshop_checksum_name: Platypus-photoshop-macos-arm64.sha256 | |
| - arch: x86_64 | |
| runner: macos-15-intel | |
| cmake_arch: x86_64 | |
| dmg_name: Platypus-macos-x86_64.dmg | |
| checksum_name: Platypus-macos-x86_64.sha256 | |
| photoshop_dmg_name: Platypus-photoshop-macos-x86_64.dmg | |
| photoshop_checksum_name: Platypus-photoshop-macos-x86_64.sha256 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ needs.prepare.outputs.target_sha }} | |
| - name: Restore Homebrew download cache | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/Library/Caches/Homebrew/api | |
| ~/Library/Caches/Homebrew/downloads | |
| key: homebrew-${{ runner.os }}-${{ matrix.arch }}-${{ hashFiles('.github/homebrew/release-macos.txt') }} | |
| - name: Install build dependencies | |
| shell: bash | |
| env: | |
| HOMEBREW_NO_AUTO_UPDATE: "1" | |
| HOMEBREW_NO_INSTALL_CLEANUP: "1" | |
| run: | | |
| set -euo pipefail | |
| BREW_PACKAGES="$(grep -Ev '^[[:space:]]*(#|$)' .github/homebrew/release-macos.txt | tr '\n' ' ')" | |
| brew install ${BREW_PACKAGES} | |
| echo "$(brew --prefix qt)/bin" >> "$GITHUB_PATH" | |
| - name: Configure app CMake | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| rm -rf build | |
| QT_PREFIX="$(brew --prefix qt)" | |
| OPENCV_PREFIX="$(brew --prefix opencv)" | |
| cmake -B build -S . -G Ninja \ | |
| -DCMAKE_BUILD_TYPE=Release \ | |
| -DBUILD_APPLE_APP=ON \ | |
| -DBUILD_TESTING=OFF \ | |
| -DCMAKE_OSX_ARCHITECTURES="${{ matrix.cmake_arch }}" \ | |
| -DPLATYPUS_PACKAGE_VERSION_OVERRIDE="${{ needs.prepare.outputs.package_version }}" \ | |
| -DCMAKE_PREFIX_PATH="${QT_PREFIX};${OPENCV_PREFIX}" \ | |
| -DOpenCV_DIR="${OPENCV_PREFIX}/lib/cmake/opencv4" | |
| - name: Build PlatypusGui | |
| shell: bash | |
| run: cmake --build build --config Release --target PlatypusGui | |
| - name: Verify app architecture | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| APP_BIN="build/PlatypusGui.app/Contents/MacOS/PlatypusGui" | |
| APP_ARCHS="$(lipo -archs "$APP_BIN")" | |
| echo "App architectures: $APP_ARCHS" | |
| [[ "$APP_ARCHS" == *"${{ matrix.cmake_arch }}"* ]] || { | |
| echo "Missing expected architecture ${{ matrix.cmake_arch }}" >&2 | |
| exit 1 | |
| } | |
| - name: Install staged app bundle | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| STAGE_DIR="${GITHUB_WORKSPACE}/build/stage/${{ matrix.arch }}" | |
| RELEASE_DIR="${GITHUB_WORKSPACE}/build/release/${{ matrix.arch }}" | |
| VERIFY_DIR="${GITHUB_WORKSPACE}/build/mac-verification/${{ matrix.arch }}" | |
| rm -rf "${STAGE_DIR}" "${RELEASE_DIR}" "${VERIFY_DIR}" | |
| mkdir -p "${RELEASE_DIR}" "${VERIFY_DIR}" | |
| cmake --install build --prefix "${STAGE_DIR}" | |
| echo "APP_PATH=${STAGE_DIR}/PlatypusGui.app" >> "$GITHUB_ENV" | |
| echo "DMG_PATH=${RELEASE_DIR}/${{ matrix.dmg_name }}" >> "$GITHUB_ENV" | |
| echo "CHECKSUM_PATH=${RELEASE_DIR}/${{ matrix.checksum_name }}" >> "$GITHUB_ENV" | |
| echo "VERIFY_DIR=${VERIFY_DIR}" >> "$GITHUB_ENV" | |
| - name: Configure Photoshop CMake | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| rm -rf build-photoshop | |
| QT_PREFIX="$(brew --prefix qt)" | |
| OPENCV_PREFIX="$(brew --prefix opencv)" | |
| cmake -B build-photoshop -S . -G Ninja \ | |
| -DCMAKE_BUILD_TYPE=Release \ | |
| -DBUILD_PHOTOSHOP_PLUGIN=ON \ | |
| -DBUILD_TESTING=OFF \ | |
| -DCMAKE_OSX_ARCHITECTURES="${{ matrix.cmake_arch }}" \ | |
| -DPLATYPUS_PACKAGE_VERSION_OVERRIDE="${{ needs.prepare.outputs.package_version }}" \ | |
| -DCMAKE_PREFIX_PATH="${QT_PREFIX};${OPENCV_PREFIX}" \ | |
| -DOpenCV_DIR="${OPENCV_PREFIX}/lib/cmake/opencv4" | |
| - name: Build Photoshop release payload | |
| shell: bash | |
| run: cmake --build build-photoshop --config Release --target stage_photoshop_release | |
| - name: Set Photoshop release paths | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| PHOTOSHOP_RELEASE_DIR="${GITHUB_WORKSPACE}/build/release/${{ matrix.arch }}" | |
| PHOTOSHOP_VERIFY_DIR="${GITHUB_WORKSPACE}/build/mac-photoshop-verification/${{ matrix.arch }}" | |
| PHOTOSHOP_PAYLOAD_DIR="${GITHUB_WORKSPACE}/build-photoshop/photoshop-release/photoshop/Platypus v${{ needs.prepare.outputs.package_version }}" | |
| rm -rf "${PHOTOSHOP_VERIFY_DIR}" | |
| mkdir -p "${PHOTOSHOP_VERIFY_DIR}" | |
| echo "PHOTOSHOP_PAYLOAD_DIR=${PHOTOSHOP_PAYLOAD_DIR}" >> "$GITHUB_ENV" | |
| echo "PHOTOSHOP_APP_PATH=${PHOTOSHOP_PAYLOAD_DIR}/Platypus.app" >> "$GITHUB_ENV" | |
| echo "PHOTOSHOP_PLUGIN_PATH=${PHOTOSHOP_PAYLOAD_DIR}/Platypus.plugin" >> "$GITHUB_ENV" | |
| echo "PHOTOSHOP_DMG_PATH=${PHOTOSHOP_RELEASE_DIR}/${{ matrix.photoshop_dmg_name }}" >> "$GITHUB_ENV" | |
| echo "PHOTOSHOP_CHECKSUM_PATH=${PHOTOSHOP_RELEASE_DIR}/${{ matrix.photoshop_checksum_name }}" >> "$GITHUB_ENV" | |
| echo "PHOTOSHOP_VERIFY_DIR=${PHOTOSHOP_VERIFY_DIR}" >> "$GITHUB_ENV" | |
| - name: Verify Photoshop payload architecture | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| PS_APP_BIN="${PHOTOSHOP_APP_PATH}/Contents/MacOS/Platypus" | |
| PS_PLUGIN_BIN="${PHOTOSHOP_PLUGIN_PATH}/Contents/MacOS/Platypus" | |
| PS_APP_ARCHS="$(lipo -archs "$PS_APP_BIN")" | |
| PS_PLUGIN_ARCHS="$(lipo -archs "$PS_PLUGIN_BIN")" | |
| echo "Photoshop app architectures: $PS_APP_ARCHS" | |
| echo "Photoshop plugin architectures: $PS_PLUGIN_ARCHS" | |
| [[ "$PS_APP_ARCHS" == *"${{ matrix.cmake_arch }}"* ]] || { | |
| echo "Photoshop companion app is missing expected architecture ${{ matrix.cmake_arch }}" >&2 | |
| exit 1 | |
| } | |
| [[ "$PS_PLUGIN_ARCHS" == *"${{ matrix.cmake_arch }}"* ]] || { | |
| echo "Photoshop plugin is missing expected architecture ${{ matrix.cmake_arch }}" >&2 | |
| exit 1 | |
| } | |
| - name: Import Apple signing certificate | |
| shell: bash | |
| env: | |
| MACOS_CERT_P12_BASE64: ${{ secrets.MACOS_CERT_P12_BASE64 }} | |
| MACOS_CERT_P12_PASSWORD: ${{ secrets.MACOS_CERT_P12_PASSWORD }} | |
| run: | | |
| set -euo pipefail | |
| CERT_PATH="$RUNNER_TEMP/dev_app.p12" | |
| KEYCHAIN_PATH="$RUNNER_TEMP/platypus-build.keychain-db" | |
| KEYCHAIN_PASSWORD="$(uuidgen)" | |
| echo "$MACOS_CERT_P12_BASE64" | base64 --decode > "$CERT_PATH" | |
| security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" | |
| security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH" | |
| security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" | |
| security import "$CERT_PATH" -k "$KEYCHAIN_PATH" -P "$MACOS_CERT_P12_PASSWORD" -T /usr/bin/codesign -T /usr/bin/security | |
| security list-keychains -d user -s "$KEYCHAIN_PATH" $(security list-keychains -d user | tr -d '"') | |
| security default-keychain -d user -s "$KEYCHAIN_PATH" | |
| security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" | |
| echo "KEYCHAIN_PATH=$KEYCHAIN_PATH" >> "$GITHUB_ENV" | |
| - name: Materialize App Store Connect API key | |
| shell: bash | |
| env: | |
| APPSTORE_CONNECT_API_KEY_BASE64: ${{ secrets.APPSTORE_CONNECT_API_KEY_BASE64 }} | |
| run: | | |
| set -euo pipefail | |
| API_KEY_PATH="$RUNNER_TEMP/AuthKey_${{ secrets.APPSTORE_CONNECT_API_KEY_ID }}.p8" | |
| echo "$APPSTORE_CONNECT_API_KEY_BASE64" | base64 --decode > "$API_KEY_PATH" | |
| echo "APPSTORE_CONNECT_API_KEY_PATH=$API_KEY_PATH" >> "$GITHUB_ENV" | |
| - name: Sign app bundle | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| bash scripts/mac_sign_app.sh \ | |
| --app "${APP_PATH}" \ | |
| --identity "${{ vars.APPLE_SIGNING_IDENTITY }}" \ | |
| --skip-deploy | |
| - name: Sign Photoshop companion app | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| bash scripts/mac_sign_bundle.sh \ | |
| --bundle "${PHOTOSHOP_APP_PATH}" \ | |
| --identity "${{ vars.APPLE_SIGNING_IDENTITY }}" | |
| - name: Sign Photoshop plugin bundle | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| bash scripts/mac_sign_bundle.sh \ | |
| --bundle "${PHOTOSHOP_PLUGIN_PATH}" \ | |
| --identity "${{ vars.APPLE_SIGNING_IDENTITY }}" | |
| - name: Verify app bundle | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| bash scripts/mac_verify_release.sh \ | |
| --phase pre-notarize \ | |
| --app "${APP_PATH}" \ | |
| --report-dir "${VERIFY_DIR}/pre-notarize" | |
| - name: Verify Photoshop payload | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| bash scripts/mac_verify_photoshop_release.sh \ | |
| --phase pre-notarize \ | |
| --payload-dir "${PHOTOSHOP_PAYLOAD_DIR}" \ | |
| --report-dir "${PHOTOSHOP_VERIFY_DIR}/pre-notarize" | |
| - name: Create signed DMG | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| bash scripts/mac_package_dmg.sh \ | |
| --app "${APP_PATH}" \ | |
| --dmg "${DMG_PATH}" \ | |
| --identity "${{ vars.APPLE_SIGNING_IDENTITY }}" \ | |
| --volname "Platypus" | |
| - name: Create signed Photoshop DMG | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| bash scripts/mac_package_dir_dmg.sh \ | |
| --source-dir "${PHOTOSHOP_PAYLOAD_DIR}" \ | |
| --dmg "${PHOTOSHOP_DMG_PATH}" \ | |
| --identity "${{ vars.APPLE_SIGNING_IDENTITY }}" \ | |
| --volname "Platypus Photoshop" | |
| - name: Notarize DMG | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| bash scripts/mac_notarize_dmg.sh \ | |
| --dmg "${DMG_PATH}" \ | |
| --api-key "$APPSTORE_CONNECT_API_KEY_PATH" \ | |
| --key-id "${{ secrets.APPSTORE_CONNECT_API_KEY_ID }}" \ | |
| --issuer "${{ secrets.APPSTORE_CONNECT_API_ISSUER_ID }}" \ | |
| --skip-dmg | |
| - name: Notarize Photoshop DMG | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| bash scripts/mac_notarize_dmg.sh \ | |
| --dmg "${PHOTOSHOP_DMG_PATH}" \ | |
| --api-key "$APPSTORE_CONNECT_API_KEY_PATH" \ | |
| --key-id "${{ secrets.APPSTORE_CONNECT_API_KEY_ID }}" \ | |
| --issuer "${{ secrets.APPSTORE_CONNECT_API_ISSUER_ID }}" \ | |
| --skip-dmg | |
| - name: Verify final release artifacts | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| bash scripts/mac_verify_release.sh \ | |
| --phase post-staple \ | |
| --app "${APP_PATH}" \ | |
| --dmg "${DMG_PATH}" \ | |
| --report-dir "${VERIFY_DIR}/post-staple" | |
| - name: Verify final Photoshop release artifacts | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| bash scripts/mac_verify_photoshop_release.sh \ | |
| --phase post-staple \ | |
| --payload-dir "${PHOTOSHOP_PAYLOAD_DIR}" \ | |
| --dmg "${PHOTOSHOP_DMG_PATH}" \ | |
| --report-dir "${PHOTOSHOP_VERIFY_DIR}/post-staple" | |
| - name: Generate checksum | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| shasum -a 256 "${DMG_PATH}" > "${CHECKSUM_PATH}" | |
| - name: Generate Photoshop checksum | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| shasum -a 256 "${PHOTOSHOP_DMG_PATH}" > "${PHOTOSHOP_CHECKSUM_PATH}" | |
| - name: Upload workflow artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: macos-${{ matrix.arch }}-release | |
| path: | | |
| build/release/${{ matrix.arch }}/${{ matrix.dmg_name }} | |
| build/release/${{ matrix.arch }}/${{ matrix.checksum_name }} | |
| build/mac-verification/${{ matrix.arch }} | |
| build/release/${{ matrix.arch }}/${{ matrix.photoshop_dmg_name }} | |
| build/release/${{ matrix.arch }}/${{ matrix.photoshop_checksum_name }} | |
| build/mac-photoshop-verification/${{ matrix.arch }} | |
| - name: Cleanup temporary keychain | |
| if: always() | |
| shell: bash | |
| run: | | |
| set +e | |
| if [[ -n "${KEYCHAIN_PATH:-}" ]]; then | |
| security delete-keychain "$KEYCHAIN_PATH" | |
| fi | |
| build_macos13: | |
| name: Build, sign, and notarize macOS 13 (${{ matrix.arch }}) | |
| needs: prepare | |
| runs-on: ${{ matrix.runner }} | |
| environment: release-macos | |
| env: | |
| _VCPKG_: ${{ github.workspace }}/vcpkg | |
| MACOSX_DEPLOYMENT_TARGET: "13.0" | |
| PLATYPUS_MACOS_DEPLOYMENT_TARGET: "13.0" | |
| VCPKG_DEFAULT_BINARY_CACHE: ${{ github.workspace }}/vcpkg/bincache | |
| VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" | |
| VCPKG_OVERLAY_TRIPLETS: ${{ github.workspace }}/.github/vcpkg-triplets | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - arch: arm64 | |
| runner: macos-15 | |
| cmake_arch: arm64 | |
| vcpkg_triplet: arm64-osx-dynamic-13-release | |
| vcpkg_host_triplet: arm64-osx-dynamic-13-release | |
| dmg_name: Platypus-macos13-arm64.dmg | |
| checksum_name: Platypus-macos13-arm64.sha256 | |
| photoshop_dmg_name: Platypus-photoshop-macos13-arm64.dmg | |
| photoshop_checksum_name: Platypus-photoshop-macos13-arm64.sha256 | |
| - arch: x86_64 | |
| runner: macos-15-intel | |
| cmake_arch: x86_64 | |
| vcpkg_triplet: x64-osx-dynamic-13-release | |
| vcpkg_host_triplet: x64-osx-dynamic-13-release | |
| dmg_name: Platypus-macos13-x86_64.dmg | |
| checksum_name: Platypus-macos13-x86_64.sha256 | |
| photoshop_dmg_name: Platypus-photoshop-macos13-x86_64.dmg | |
| photoshop_checksum_name: Platypus-photoshop-macos13-x86_64.sha256 | |
| steps: | |
| - uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || ''); | |
| core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ needs.prepare.outputs.target_sha }} | |
| - name: Restore Homebrew download cache | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/Library/Caches/Homebrew/api | |
| ~/Library/Caches/Homebrew/downloads | |
| key: homebrew-macos13-${{ matrix.arch }}-${{ hashFiles('.github/homebrew/release-macos13.txt') }} | |
| - name: Install build dependencies | |
| shell: bash | |
| env: | |
| HOMEBREW_NO_AUTO_UPDATE: "1" | |
| HOMEBREW_NO_INSTALL_CLEANUP: "1" | |
| run: | | |
| set -euo pipefail | |
| BREW_PACKAGES="$(grep -Ev '^[[:space:]]*(#|$)' .github/homebrew/release-macos13.txt | tr '\n' ' ')" | |
| brew install ${BREW_PACKAGES} | |
| - name: Create vcpkg binary cache directory | |
| shell: bash | |
| run: mkdir -p "${VCPKG_DEFAULT_BINARY_CACHE}" | |
| - name: Restore from cache and setup vcpkg | |
| uses: lukka/run-vcpkg@v11 | |
| with: | |
| vcpkgJsonGlob: ".github/vcpkg-manifests/macos13/vcpkg.json" | |
| vcpkgGitCommitId: "c21f78db5a402441bfc8052462233a2aff079ac0" | |
| - name: Prepare vcpkg paths | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| VCPKG_INSTALLED_DIR="${GITHUB_WORKSPACE}/vcpkg_installed/macos13/${{ matrix.arch }}" | |
| VCPKG_QT_TOOLS_DIR="${VCPKG_INSTALLED_DIR}/${{ matrix.vcpkg_host_triplet }}/tools/Qt6/bin" | |
| VCPKG_DIAGNOSTIC_DIR="${GITHUB_WORKSPACE}/vcpkg-diagnostics/${{ matrix.arch }}" | |
| mkdir -p "${VCPKG_INSTALLED_DIR}" "${VCPKG_DEFAULT_BINARY_CACHE}" "${VCPKG_DIAGNOSTIC_DIR}" | |
| echo "VCPKG_INSTALLED_DIR=${VCPKG_INSTALLED_DIR}" >> "$GITHUB_ENV" | |
| echo "VCPKG_QT_TOOLS_DIR=${VCPKG_QT_TOOLS_DIR}" >> "$GITHUB_ENV" | |
| echo "VCPKG_DIAGNOSTIC_DIR=${VCPKG_DIAGNOSTIC_DIR}" >> "$GITHUB_ENV" | |
| - name: Build vcpkg dependencies | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| if "${_VCPKG_}/vcpkg" install \ | |
| --clean-buildtrees-after-build \ | |
| --x-manifest-root="${GITHUB_WORKSPACE}/.github/vcpkg-manifests/macos13" \ | |
| --x-install-root="${VCPKG_INSTALLED_DIR}" \ | |
| --overlay-triplets="${VCPKG_OVERLAY_TRIPLETS}" \ | |
| --triplet="${{ matrix.vcpkg_triplet }}" \ | |
| --host-triplet="${{ matrix.vcpkg_host_triplet }}"; then | |
| exit 0 | |
| else | |
| status=$? | |
| fi | |
| set +e | |
| echo "vcpkg install failed with exit code ${status}" >&2 | |
| echo "Collecting vcpkg diagnostics into ${VCPKG_DIAGNOSTIC_DIR}" >&2 | |
| if [[ -d "${_VCPKG_}/buildtrees" ]]; then | |
| find "${_VCPKG_}/buildtrees" -type f \ | |
| \( -name '*.log' -o -name '*.out' -o -name '*.err' -o -name 'issue_body.md' \) \ | |
| | while IFS= read -r src; do | |
| rel="${src#${_VCPKG_}/buildtrees/}" | |
| dest="${VCPKG_DIAGNOSTIC_DIR}/buildtrees/${rel}" | |
| mkdir -p "$(dirname "${dest}")" | |
| cp "${src}" "${dest}" | |
| done | |
| fi | |
| if [[ -d "${VCPKG_INSTALLED_DIR}/vcpkg" ]]; then | |
| find "${VCPKG_INSTALLED_DIR}/vcpkg" -type f \ | |
| | while IFS= read -r src; do | |
| rel="${src#${VCPKG_INSTALLED_DIR}/vcpkg/}" | |
| dest="${VCPKG_DIAGNOSTIC_DIR}/installed-vcpkg/${rel}" | |
| mkdir -p "$(dirname "${dest}")" | |
| cp "${src}" "${dest}" | |
| done | |
| fi | |
| if [[ -f "${VCPKG_DIAGNOSTIC_DIR}/installed-vcpkg/issue_body.md" ]]; then | |
| echo "::group::vcpkg issue_body.md" | |
| cat "${VCPKG_DIAGNOSTIC_DIR}/installed-vcpkg/issue_body.md" | |
| echo "::endgroup::" | |
| fi | |
| find "${VCPKG_DIAGNOSTIC_DIR}" -type f \ | |
| \( -name '*.log' -o -name '*.out' -o -name '*.err' \) \ | |
| -exec ls -t {} + 2>/dev/null \ | |
| | head -n 10 \ | |
| | while IFS= read -r log; do | |
| echo "::group::${log#${GITHUB_WORKSPACE}/}" | |
| tail -n 200 "${log}" || true | |
| echo "::endgroup::" | |
| done || true | |
| exit "${status}" | |
| - name: Upload vcpkg diagnostics | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: macos13-${{ matrix.arch }}-vcpkg-diagnostics | |
| path: vcpkg-diagnostics/${{ matrix.arch }} | |
| if-no-files-found: warn | |
| - name: Stash vcpkg dependencies | |
| if: success() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: macos13-${{ matrix.arch }}-vcpkg-installed | |
| path: vcpkg_installed/macos13/${{ matrix.arch }} | |
| if-no-files-found: error | |
| compression-level: 0 | |
| - name: Resolve Qt deployment tools | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| QT_PATHS="" | |
| if [[ -x "${VCPKG_QT_TOOLS_DIR}/qtpaths6" ]]; then | |
| QT_PATHS="${VCPKG_QT_TOOLS_DIR}/qtpaths6" | |
| else | |
| QT_PATHS="${VCPKG_QT_TOOLS_DIR}/qtpaths" | |
| fi | |
| [[ -x "${QT_PATHS}" ]] || { | |
| echo "qtpaths not found under ${VCPKG_QT_TOOLS_DIR}" >&2 | |
| exit 1 | |
| } | |
| MACDEPLOYQT="" | |
| if [[ -x "${VCPKG_QT_TOOLS_DIR}/macdeployqt6" ]]; then | |
| MACDEPLOYQT="${VCPKG_QT_TOOLS_DIR}/macdeployqt6" | |
| else | |
| MACDEPLOYQT="${VCPKG_QT_TOOLS_DIR}/macdeployqt" | |
| fi | |
| [[ -x "${MACDEPLOYQT}" ]] || { | |
| echo "macdeployqt not found under ${VCPKG_QT_TOOLS_DIR}" >&2 | |
| exit 1 | |
| } | |
| echo "QT_PATHS_EXECUTABLE=${QT_PATHS}" >> "$GITHUB_ENV" | |
| echo "MACDEPLOYQT_EXECUTABLE=${MACDEPLOYQT}" >> "$GITHUB_ENV" | |
| - name: Configure app CMake | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| rm -rf build | |
| cmake -B build -S . -G Ninja \ | |
| -DCMAKE_BUILD_TYPE=Release \ | |
| -DBUILD_APPLE_APP=ON \ | |
| -DBUILD_TESTING=OFF \ | |
| -DCMAKE_OSX_DEPLOYMENT_TARGET="${PLATYPUS_MACOS_DEPLOYMENT_TARGET}" \ | |
| -DCMAKE_OSX_ARCHITECTURES="${{ matrix.cmake_arch }}" \ | |
| -DPLATYPUS_PACKAGE_VERSION_OVERRIDE="${{ needs.prepare.outputs.package_version }}" \ | |
| -DCMAKE_TOOLCHAIN_FILE="${_VCPKG_}/scripts/buildsystems/vcpkg.cmake" \ | |
| -DVCPKG_MANIFEST_MODE=OFF \ | |
| -DVCPKG_OVERLAY_TRIPLETS="${VCPKG_OVERLAY_TRIPLETS}" \ | |
| -DVCPKG_INSTALLED_DIR="${VCPKG_INSTALLED_DIR}" \ | |
| -DVCPKG_TARGET_TRIPLET="${{ matrix.vcpkg_triplet }}" \ | |
| -DVCPKG_HOST_TRIPLET="${{ matrix.vcpkg_host_triplet }}" \ | |
| -DCMAKE_PREFIX_PATH="${VCPKG_INSTALLED_DIR}/${{ matrix.vcpkg_triplet }}" \ | |
| -DQt6_DIR="${VCPKG_INSTALLED_DIR}/${{ matrix.vcpkg_triplet }}/share/Qt6" \ | |
| -DOpenCV_DIR="${VCPKG_INSTALLED_DIR}/${{ matrix.vcpkg_triplet }}/share/opencv4" \ | |
| -DMACDEPLOYQT_EXECUTABLE="${MACDEPLOYQT_EXECUTABLE}" \ | |
| -DQT_PATHS_EXECUTABLE="${QT_PATHS_EXECUTABLE}" | |
| - name: Build PlatypusGui | |
| shell: bash | |
| run: cmake --build build --config Release --target PlatypusGui | |
| - name: Verify app architecture | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| APP_BIN="build/PlatypusGui.app/Contents/MacOS/PlatypusGui" | |
| APP_ARCHS="$(lipo -archs "$APP_BIN")" | |
| echo "App architectures: $APP_ARCHS" | |
| [[ "$APP_ARCHS" == *"${{ matrix.cmake_arch }}"* ]] || { | |
| echo "Missing expected architecture ${{ matrix.cmake_arch }}" >&2 | |
| exit 1 | |
| } | |
| - name: Install staged app bundle | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| STAGE_DIR="${GITHUB_WORKSPACE}/build/stage/${{ matrix.arch }}" | |
| RELEASE_DIR="${GITHUB_WORKSPACE}/build/release/${{ matrix.arch }}" | |
| VERIFY_DIR="${GITHUB_WORKSPACE}/build/mac-verification/${{ matrix.arch }}" | |
| rm -rf "${STAGE_DIR}" "${RELEASE_DIR}" "${VERIFY_DIR}" | |
| mkdir -p "${RELEASE_DIR}" "${VERIFY_DIR}" | |
| cmake --install build --prefix "${STAGE_DIR}" | |
| echo "APP_PATH=${STAGE_DIR}/PlatypusGui.app" >> "$GITHUB_ENV" | |
| echo "DMG_PATH=${RELEASE_DIR}/${{ matrix.dmg_name }}" >> "$GITHUB_ENV" | |
| echo "CHECKSUM_PATH=${RELEASE_DIR}/${{ matrix.checksum_name }}" >> "$GITHUB_ENV" | |
| echo "VERIFY_DIR=${VERIFY_DIR}" >> "$GITHUB_ENV" | |
| - name: Configure Photoshop CMake | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| rm -rf build-photoshop | |
| cmake -B build-photoshop -S . -G Ninja \ | |
| -DCMAKE_BUILD_TYPE=Release \ | |
| -DBUILD_PHOTOSHOP_PLUGIN=ON \ | |
| -DBUILD_TESTING=OFF \ | |
| -DCMAKE_OSX_DEPLOYMENT_TARGET="${PLATYPUS_MACOS_DEPLOYMENT_TARGET}" \ | |
| -DCMAKE_OSX_ARCHITECTURES="${{ matrix.cmake_arch }}" \ | |
| -DPLATYPUS_PACKAGE_VERSION_OVERRIDE="${{ needs.prepare.outputs.package_version }}" \ | |
| -DCMAKE_TOOLCHAIN_FILE="${_VCPKG_}/scripts/buildsystems/vcpkg.cmake" \ | |
| -DVCPKG_MANIFEST_MODE=OFF \ | |
| -DVCPKG_OVERLAY_TRIPLETS="${VCPKG_OVERLAY_TRIPLETS}" \ | |
| -DVCPKG_INSTALLED_DIR="${VCPKG_INSTALLED_DIR}" \ | |
| -DVCPKG_TARGET_TRIPLET="${{ matrix.vcpkg_triplet }}" \ | |
| -DVCPKG_HOST_TRIPLET="${{ matrix.vcpkg_host_triplet }}" \ | |
| -DCMAKE_PREFIX_PATH="${VCPKG_INSTALLED_DIR}/${{ matrix.vcpkg_triplet }}" \ | |
| -DQt6_DIR="${VCPKG_INSTALLED_DIR}/${{ matrix.vcpkg_triplet }}/share/Qt6" \ | |
| -DOpenCV_DIR="${VCPKG_INSTALLED_DIR}/${{ matrix.vcpkg_triplet }}/share/opencv4" \ | |
| -DMACDEPLOYQT_EXECUTABLE="${MACDEPLOYQT_EXECUTABLE}" \ | |
| -DQT_PATHS_EXECUTABLE="${QT_PATHS_EXECUTABLE}" | |
| - name: Build Photoshop release payload | |
| shell: bash | |
| run: cmake --build build-photoshop --config Release --target stage_photoshop_release | |
| - name: Set Photoshop release paths | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| PHOTOSHOP_RELEASE_DIR="${GITHUB_WORKSPACE}/build/release/${{ matrix.arch }}" | |
| PHOTOSHOP_VERIFY_DIR="${GITHUB_WORKSPACE}/build/mac-photoshop-verification/${{ matrix.arch }}" | |
| PHOTOSHOP_PAYLOAD_DIR="${GITHUB_WORKSPACE}/build-photoshop/photoshop-release/photoshop/Platypus v${{ needs.prepare.outputs.package_version }}" | |
| rm -rf "${PHOTOSHOP_VERIFY_DIR}" | |
| mkdir -p "${PHOTOSHOP_VERIFY_DIR}" | |
| echo "PHOTOSHOP_PAYLOAD_DIR=${PHOTOSHOP_PAYLOAD_DIR}" >> "$GITHUB_ENV" | |
| echo "PHOTOSHOP_APP_PATH=${PHOTOSHOP_PAYLOAD_DIR}/Platypus.app" >> "$GITHUB_ENV" | |
| echo "PHOTOSHOP_PLUGIN_PATH=${PHOTOSHOP_PAYLOAD_DIR}/Platypus.plugin" >> "$GITHUB_ENV" | |
| echo "PHOTOSHOP_DMG_PATH=${PHOTOSHOP_RELEASE_DIR}/${{ matrix.photoshop_dmg_name }}" >> "$GITHUB_ENV" | |
| echo "PHOTOSHOP_CHECKSUM_PATH=${PHOTOSHOP_RELEASE_DIR}/${{ matrix.photoshop_checksum_name }}" >> "$GITHUB_ENV" | |
| echo "PHOTOSHOP_VERIFY_DIR=${PHOTOSHOP_VERIFY_DIR}" >> "$GITHUB_ENV" | |
| - name: Verify Photoshop payload architecture | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| PS_APP_BIN="${PHOTOSHOP_APP_PATH}/Contents/MacOS/Platypus" | |
| PS_PLUGIN_BIN="${PHOTOSHOP_PLUGIN_PATH}/Contents/MacOS/Platypus" | |
| PS_APP_ARCHS="$(lipo -archs "$PS_APP_BIN")" | |
| PS_PLUGIN_ARCHS="$(lipo -archs "$PS_PLUGIN_BIN")" | |
| echo "Photoshop app architectures: $PS_APP_ARCHS" | |
| echo "Photoshop plugin architectures: $PS_PLUGIN_ARCHS" | |
| [[ "$PS_APP_ARCHS" == *"${{ matrix.cmake_arch }}"* ]] || { | |
| echo "Photoshop companion app is missing expected architecture ${{ matrix.cmake_arch }}" >&2 | |
| exit 1 | |
| } | |
| [[ "$PS_PLUGIN_ARCHS" == *"${{ matrix.cmake_arch }}"* ]] || { | |
| echo "Photoshop plugin is missing expected architecture ${{ matrix.cmake_arch }}" >&2 | |
| exit 1 | |
| } | |
| - name: Import Apple signing certificate | |
| shell: bash | |
| env: | |
| MACOS_CERT_P12_BASE64: ${{ secrets.MACOS_CERT_P12_BASE64 }} | |
| MACOS_CERT_P12_PASSWORD: ${{ secrets.MACOS_CERT_P12_PASSWORD }} | |
| run: | | |
| set -euo pipefail | |
| CERT_PATH="$RUNNER_TEMP/dev_app.p12" | |
| KEYCHAIN_PATH="$RUNNER_TEMP/platypus-build.keychain-db" | |
| KEYCHAIN_PASSWORD="$(uuidgen)" | |
| echo "$MACOS_CERT_P12_BASE64" | base64 --decode > "$CERT_PATH" | |
| security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" | |
| security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH" | |
| security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" | |
| security import "$CERT_PATH" -k "$KEYCHAIN_PATH" -P "$MACOS_CERT_P12_PASSWORD" -T /usr/bin/codesign -T /usr/bin/security | |
| security list-keychains -d user -s "$KEYCHAIN_PATH" $(security list-keychains -d user | tr -d '"') | |
| security default-keychain -d user -s "$KEYCHAIN_PATH" | |
| security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" | |
| echo "KEYCHAIN_PATH=$KEYCHAIN_PATH" >> "$GITHUB_ENV" | |
| - name: Materialize App Store Connect API key | |
| shell: bash | |
| env: | |
| APPSTORE_CONNECT_API_KEY_BASE64: ${{ secrets.APPSTORE_CONNECT_API_KEY_BASE64 }} | |
| run: | | |
| set -euo pipefail | |
| API_KEY_PATH="$RUNNER_TEMP/AuthKey_${{ secrets.APPSTORE_CONNECT_API_KEY_ID }}.p8" | |
| echo "$APPSTORE_CONNECT_API_KEY_BASE64" | base64 --decode > "$API_KEY_PATH" | |
| echo "APPSTORE_CONNECT_API_KEY_PATH=$API_KEY_PATH" >> "$GITHUB_ENV" | |
| - name: Sign app bundle | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| bash scripts/mac_sign_app.sh \ | |
| --app "${APP_PATH}" \ | |
| --identity "${{ vars.APPLE_SIGNING_IDENTITY }}" \ | |
| --skip-deploy | |
| - name: Sign Photoshop companion app | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| bash scripts/mac_sign_bundle.sh \ | |
| --bundle "${PHOTOSHOP_APP_PATH}" \ | |
| --identity "${{ vars.APPLE_SIGNING_IDENTITY }}" | |
| - name: Sign Photoshop plugin bundle | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| bash scripts/mac_sign_bundle.sh \ | |
| --bundle "${PHOTOSHOP_PLUGIN_PATH}" \ | |
| --identity "${{ vars.APPLE_SIGNING_IDENTITY }}" | |
| - name: Verify app bundle | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| bash scripts/mac_verify_release.sh \ | |
| --phase pre-notarize \ | |
| --app "${APP_PATH}" \ | |
| --deployment-target "${PLATYPUS_MACOS_DEPLOYMENT_TARGET}" \ | |
| --report-dir "${VERIFY_DIR}/pre-notarize" | |
| - name: Verify Photoshop payload | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| bash scripts/mac_verify_photoshop_release.sh \ | |
| --phase pre-notarize \ | |
| --payload-dir "${PHOTOSHOP_PAYLOAD_DIR}" \ | |
| --deployment-target "${PLATYPUS_MACOS_DEPLOYMENT_TARGET}" \ | |
| --report-dir "${PHOTOSHOP_VERIFY_DIR}/pre-notarize" | |
| - name: Create signed DMG | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| bash scripts/mac_package_dmg.sh \ | |
| --app "${APP_PATH}" \ | |
| --dmg "${DMG_PATH}" \ | |
| --identity "${{ vars.APPLE_SIGNING_IDENTITY }}" \ | |
| --volname "Platypus" | |
| - name: Create signed Photoshop DMG | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| bash scripts/mac_package_dir_dmg.sh \ | |
| --source-dir "${PHOTOSHOP_PAYLOAD_DIR}" \ | |
| --dmg "${PHOTOSHOP_DMG_PATH}" \ | |
| --identity "${{ vars.APPLE_SIGNING_IDENTITY }}" \ | |
| --volname "Platypus Photoshop" | |
| - name: Notarize DMG | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| bash scripts/mac_notarize_dmg.sh \ | |
| --dmg "${DMG_PATH}" \ | |
| --api-key "$APPSTORE_CONNECT_API_KEY_PATH" \ | |
| --key-id "${{ secrets.APPSTORE_CONNECT_API_KEY_ID }}" \ | |
| --issuer "${{ secrets.APPSTORE_CONNECT_API_ISSUER_ID }}" \ | |
| --skip-dmg | |
| - name: Notarize Photoshop DMG | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| bash scripts/mac_notarize_dmg.sh \ | |
| --dmg "${PHOTOSHOP_DMG_PATH}" \ | |
| --api-key "$APPSTORE_CONNECT_API_KEY_PATH" \ | |
| --key-id "${{ secrets.APPSTORE_CONNECT_API_KEY_ID }}" \ | |
| --issuer "${{ secrets.APPSTORE_CONNECT_API_ISSUER_ID }}" \ | |
| --skip-dmg | |
| - name: Verify final release artifacts | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| bash scripts/mac_verify_release.sh \ | |
| --phase post-staple \ | |
| --app "${APP_PATH}" \ | |
| --dmg "${DMG_PATH}" \ | |
| --deployment-target "${PLATYPUS_MACOS_DEPLOYMENT_TARGET}" \ | |
| --report-dir "${VERIFY_DIR}/post-staple" | |
| - name: Verify final Photoshop release artifacts | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| bash scripts/mac_verify_photoshop_release.sh \ | |
| --phase post-staple \ | |
| --payload-dir "${PHOTOSHOP_PAYLOAD_DIR}" \ | |
| --dmg "${PHOTOSHOP_DMG_PATH}" \ | |
| --deployment-target "${PLATYPUS_MACOS_DEPLOYMENT_TARGET}" \ | |
| --report-dir "${PHOTOSHOP_VERIFY_DIR}/post-staple" | |
| - name: Generate checksum | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| shasum -a 256 "${DMG_PATH}" > "${CHECKSUM_PATH}" | |
| - name: Generate Photoshop checksum | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| shasum -a 256 "${PHOTOSHOP_DMG_PATH}" > "${PHOTOSHOP_CHECKSUM_PATH}" | |
| - name: Upload workflow artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: macos13-${{ matrix.arch }}-release | |
| path: | | |
| build/release/${{ matrix.arch }}/${{ matrix.dmg_name }} | |
| build/release/${{ matrix.arch }}/${{ matrix.checksum_name }} | |
| build/mac-verification/${{ matrix.arch }} | |
| build/release/${{ matrix.arch }}/${{ matrix.photoshop_dmg_name }} | |
| build/release/${{ matrix.arch }}/${{ matrix.photoshop_checksum_name }} | |
| build/mac-photoshop-verification/${{ matrix.arch }} | |
| - name: Cleanup temporary keychain | |
| if: always() | |
| shell: bash | |
| run: | | |
| set +e | |
| if [[ -n "${KEYCHAIN_PATH:-}" ]]; then | |
| security delete-keychain "$KEYCHAIN_PATH" | |
| fi | |
| publish: | |
| name: Publish GitHub Release | |
| needs: | |
| - prepare | |
| - build | |
| - build_macos13 | |
| runs-on: ubuntu-latest | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| steps: | |
| - name: Download release artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: dist | |
| - name: Validate required macOS assets | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| REQUIRED_FILES=( | |
| Platypus-macos-arm64.dmg | |
| Platypus-macos-arm64.sha256 | |
| Platypus-macos-x86_64.dmg | |
| Platypus-macos-x86_64.sha256 | |
| Platypus-photoshop-macos-arm64.dmg | |
| Platypus-photoshop-macos-arm64.sha256 | |
| Platypus-photoshop-macos-x86_64.dmg | |
| Platypus-photoshop-macos-x86_64.sha256 | |
| Platypus-macos13-arm64.dmg | |
| Platypus-macos13-arm64.sha256 | |
| Platypus-macos13-x86_64.dmg | |
| Platypus-macos13-x86_64.sha256 | |
| Platypus-photoshop-macos13-arm64.dmg | |
| Platypus-photoshop-macos13-arm64.sha256 | |
| Platypus-photoshop-macos13-x86_64.dmg | |
| Platypus-photoshop-macos13-x86_64.sha256 | |
| ) | |
| : > release-files.txt | |
| for filename in "${REQUIRED_FILES[@]}"; do | |
| filepath="$(find dist -type f -name "${filename}" | head -n1)" | |
| if [[ -z "${filepath}" ]]; then | |
| echo "Missing required macOS asset: ${filename}" >&2 | |
| exit 1 | |
| fi | |
| printf '%s\n' "${filepath}" >> release-files.txt | |
| done | |
| sort -o release-files.txt release-files.txt | |
| echo "Validated macOS release asset set:" | |
| cat release-files.txt | |
| - name: Create or update GitHub Release | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| TAG="${{ needs.prepare.outputs.tag }}" | |
| mapfile -t RELEASE_FILES < release-files.txt | |
| if [[ "${#RELEASE_FILES[@]}" -eq 0 ]]; then | |
| echo "No release artifacts were downloaded." >&2 | |
| exit 1 | |
| fi | |
| if gh release view "${TAG}" --repo "${GITHUB_REPOSITORY}" >/dev/null 2>&1; then | |
| gh release upload "${TAG}" "${RELEASE_FILES[@]}" --clobber --repo "${GITHUB_REPOSITORY}" | |
| exit 0 | |
| fi | |
| RELEASE_ARGS=() | |
| if [[ "${{ needs.prepare.outputs.prerelease }}" == "true" ]]; then | |
| RELEASE_ARGS+=(--prerelease) | |
| fi | |
| if gh release create "$TAG" \ | |
| "${RELEASE_FILES[@]}" \ | |
| --target "${{ needs.prepare.outputs.target_sha }}" \ | |
| --title "$TAG" \ | |
| --generate-notes \ | |
| --repo "${GITHUB_REPOSITORY}" \ | |
| "${RELEASE_ARGS[@]}"; then | |
| exit 0 | |
| fi | |
| echo "Release creation failed, retrying as upload in case another workflow created it first." >&2 | |
| gh release upload "${TAG}" "${RELEASE_FILES[@]}" --clobber --repo "${GITHUB_REPOSITORY}" |