Build release artifacts #19
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 release artifacts | |
| "on": | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: "Release tag. Defaults to v<macOS app version>." | |
| required: false | |
| type: string | |
| publish_release: | |
| description: "Create or update the GitHub Release" | |
| required: true | |
| default: false | |
| type: boolean | |
| draft: | |
| description: "Create the GitHub Release as a draft" | |
| required: true | |
| default: true | |
| type: boolean | |
| prerelease: | |
| description: "Mark the GitHub Release as a prerelease" | |
| required: true | |
| default: false | |
| type: boolean | |
| permissions: | |
| contents: write | |
| concurrency: | |
| group: build-release-artifacts-${{ github.ref }} | |
| cancel-in-progress: false | |
| jobs: | |
| metadata: | |
| name: Resolve release metadata | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 10 | |
| outputs: | |
| version: ${{ steps.release.outputs.version }} | |
| build: ${{ steps.release.outputs.build }} | |
| tag: ${{ steps.release.outputs.tag }} | |
| artifact_path: ${{ steps.release.outputs.artifact_path }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - name: Resolve macOS app version | |
| id: release | |
| env: | |
| INPUT_TAG: ${{ inputs.tag }} | |
| run: | | |
| set -euo pipefail | |
| version="$(python3 - <<'PY' | |
| import plistlib | |
| from pathlib import Path | |
| with Path("macOS/Sources/ClipDock/Resources/AppInfo.plist").open("rb") as handle: | |
| info = plistlib.load(handle) | |
| print(info.get("CFBundleShortVersionString", "0.1.0")) | |
| PY | |
| )" | |
| build="$(python3 - <<'PY' | |
| import plistlib | |
| from pathlib import Path | |
| with Path("macOS/Sources/ClipDock/Resources/AppInfo.plist").open("rb") as handle: | |
| info = plistlib.load(handle) | |
| print(info.get("CFBundleVersion", "1")) | |
| PY | |
| )" | |
| tag="$INPUT_TAG" | |
| if [[ ! "$version" =~ ^[0-9]+(\.[0-9]+){1,2}$ ]]; then | |
| echo "version must be numeric, such as 0.1.7" >&2 | |
| exit 1 | |
| fi | |
| if [[ ! "$build" =~ ^[0-9]+$ ]]; then | |
| echo "build must be a positive integer" >&2 | |
| exit 1 | |
| fi | |
| if [[ -z "$tag" ]]; then | |
| tag="v$version" | |
| fi | |
| if [[ ! "$tag" =~ ^[A-Za-z0-9._/-]+$ ]]; then | |
| echo "tag may only contain letters, numbers, dot, underscore, slash, or dash" >&2 | |
| exit 1 | |
| fi | |
| artifact_path=".codex/artifacts/release/$version" | |
| { | |
| echo "version=$version" | |
| echo "build=$build" | |
| echo "tag=$tag" | |
| echo "artifact_path=$artifact_path" | |
| } >> "$GITHUB_OUTPUT" | |
| build-macos-app: | |
| name: Build macOS app DMGs | |
| runs-on: macos-15 | |
| needs: metadata | |
| timeout-minutes: 75 | |
| env: | |
| APP_BUNDLE_NAME: ClipDock | |
| APP_EXECUTABLE_NAME: ClipDock | |
| BUNDLE_IDENTIFIER: com.apkdv.clipdock | |
| APP_ARCHS: arm64 x86_64 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - name: Print toolchain | |
| working-directory: macOS | |
| run: | | |
| sw_vers | |
| xcodebuild -version | |
| swift --version | |
| cargo --version | |
| - name: Build release bundle | |
| working-directory: macOS | |
| env: | |
| APP_VERSION: ${{ needs.metadata.outputs.version }} | |
| APP_BUILD: ${{ needs.metadata.outputs.build }} | |
| RELEASE_DIR: ${{ github.workspace }}/${{ needs.metadata.outputs.artifact_path }}/macos | |
| CODESIGN_IDENTITY: ${{ secrets.CODESIGN_IDENTITY || '-' }} | |
| APPLE_ID: ${{ secrets.APPLE_ID }} | |
| APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} | |
| APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} | |
| run: scripts/release-macos.sh | |
| - name: Upload macOS workflow artifact | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: ClipDock-${{ needs.metadata.outputs.version }}-macos | |
| path: | | |
| .codex/artifacts/release/${{ needs.metadata.outputs.version }}/macos/ClipDock-${{ needs.metadata.outputs.version }}-*.dmg | |
| .codex/artifacts/release/${{ needs.metadata.outputs.version }}/macos/SHA256SUMS | |
| .codex/artifacts/release/${{ needs.metadata.outputs.version }}/macos/ClipDock-release-manifest.txt | |
| if-no-files-found: error | |
| retention-days: 30 | |
| build-server: | |
| name: Build server ${{ matrix.artifact_label }} | |
| needs: metadata | |
| runs-on: ${{ matrix.runner }} | |
| timeout-minutes: 45 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - artifact_label: linux-x86_64 | |
| runner: ubuntu-24.04 | |
| target: x86_64-unknown-linux-gnu | |
| - artifact_label: linux-arm64 | |
| runner: ubuntu-24.04-arm | |
| target: aarch64-unknown-linux-gnu | |
| - artifact_label: macos-x86_64 | |
| runner: macos-15-intel | |
| target: x86_64-apple-darwin | |
| - artifact_label: macos-arm64 | |
| runner: macos-15 | |
| target: aarch64-apple-darwin | |
| - artifact_label: windows-x86_64 | |
| runner: windows-2025 | |
| target: x86_64-pc-windows-msvc | |
| - artifact_label: windows-arm64 | |
| runner: windows-11-arm | |
| target: aarch64-pc-windows-msvc | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - name: Print Rust toolchain | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| uname -a || true | |
| rustc -vV | |
| cargo --version | |
| - name: Add Rust target | |
| shell: bash | |
| run: rustup target add "${{ matrix.target }}" | |
| - name: Build and package server | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| artifact_root="$PWD/${{ needs.metadata.outputs.artifact_path }}" | |
| Server/scripts/package-server.sh \ | |
| --target "${{ matrix.target }}" \ | |
| --version "${{ needs.metadata.outputs.version }}" \ | |
| --output-dir "$artifact_root/server/${{ matrix.artifact_label }}" | |
| - name: Upload server workflow artifact | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: ClipDock-Server-${{ needs.metadata.outputs.version }}-${{ matrix.artifact_label }} | |
| path: | | |
| .codex/artifacts/release/${{ needs.metadata.outputs.version }}/server/${{ matrix.artifact_label }}/* | |
| if-no-files-found: error | |
| retention-days: 30 | |
| publish-release: | |
| name: Publish GitHub Release | |
| if: ${{ inputs.publish_release }} | |
| needs: | |
| - metadata | |
| - build-macos-app | |
| - build-server | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 20 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - name: Download release artifacts | |
| uses: actions/download-artifact@v7 | |
| with: | |
| path: release-artifacts | |
| - name: Generate release notes from commits | |
| env: | |
| RELEASE_VERSION: ${{ needs.metadata.outputs.version }} | |
| RELEASE_BUILD: ${{ needs.metadata.outputs.build }} | |
| RELEASE_TAG: ${{ needs.metadata.outputs.tag }} | |
| RELEASE_NOTES: release-notes.md | |
| run: | | |
| set -euo pipefail | |
| target_commit="$GITHUB_SHA" | |
| if git rev-parse -q --verify "refs/tags/$RELEASE_TAG^{commit}" >/dev/null; then | |
| target_commit="$(git rev-list -n 1 "$RELEASE_TAG")" | |
| previous_tag="$(git describe --tags --abbrev=0 "$RELEASE_TAG^" 2>/dev/null || true)" | |
| else | |
| previous_tag="$(git describe --tags --abbrev=0 "$target_commit" 2>/dev/null || true)" | |
| fi | |
| if [[ -n "$previous_tag" ]]; then | |
| commit_range="$previous_tag..$target_commit" | |
| else | |
| commit_range="$target_commit" | |
| fi | |
| { | |
| echo "## ClipDock $RELEASE_VERSION" | |
| echo | |
| echo "- Build: $RELEASE_BUILD" | |
| echo "- Commit: $target_commit" | |
| if [[ -n "$previous_tag" ]]; then | |
| echo "- Changes since: $previous_tag" | |
| fi | |
| echo | |
| echo "## Commits" | |
| echo | |
| } > "$RELEASE_NOTES" | |
| commit_count="$(git rev-list --count "$commit_range" 2>/dev/null || true)" | |
| if [[ "${commit_count:-0}" =~ ^[0-9]+$ && "$commit_count" -gt 0 ]]; then | |
| git log --format="- %s (%h)" --reverse "$commit_range" >> "$RELEASE_NOTES" | |
| else | |
| git log -1 --format="- %s (%h)" "$target_commit" >> "$RELEASE_NOTES" | |
| fi | |
| cat "$RELEASE_NOTES" | |
| - name: Publish GitHub Release | |
| uses: softprops/action-gh-release@v3 | |
| with: | |
| tag_name: ${{ needs.metadata.outputs.tag }} | |
| target_commitish: ${{ github.sha }} | |
| name: ClipDock ${{ needs.metadata.outputs.version }} | |
| body_path: release-notes.md | |
| draft: ${{ inputs.draft }} | |
| prerelease: ${{ inputs.prerelease }} | |
| fail_on_unmatched_files: true | |
| overwrite_files: true | |
| files: | | |
| release-artifacts/**/*.dmg | |
| release-artifacts/**/*.tar.gz | |
| release-artifacts/**/*.zip | |
| release-artifacts/**/*.sha256 | |
| release-artifacts/**/*manifest.txt |