ci: validate release packaging without publishing #4
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 | |
| on: | |
| push: | |
| tags: | |
| - "v*" | |
| pull_request: | |
| paths: | |
| - ".github/workflows/release.yml" | |
| - "apps/headless/Dockerfile.linux" | |
| - "apps/headless/build.sh" | |
| - "apps/headless/build-linux.sh" | |
| - "apps/headless/install-linux.sh" | |
| - "apps/headless/Package.swift" | |
| workflow_dispatch: | |
| inputs: | |
| dry_run: | |
| description: Build and verify release packages without publishing | |
| required: true | |
| default: true | |
| type: boolean | |
| permissions: | |
| contents: read | |
| jobs: | |
| version: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| version: ${{ steps.version.outputs.version }} | |
| publish: ${{ steps.version.outputs.publish }} | |
| steps: | |
| - id: version | |
| env: | |
| DRY_RUN: ${{ inputs.dry_run }} | |
| run: | | |
| set -eu | |
| if [ "$GITHUB_EVENT_NAME" = "push" ]; then | |
| case "$GITHUB_REF" in | |
| refs/tags/v*) ;; | |
| *) echo "release publishing requires a v* tag" >&2; exit 64 ;; | |
| esac | |
| echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT" | |
| echo "publish=true" >> "$GITHUB_OUTPUT" | |
| else | |
| if [ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ] && [ "$DRY_RUN" != "true" ]; then | |
| echo "manual release runs must use dry_run=true; publish by pushing a v* tag" >&2 | |
| exit 64 | |
| fi | |
| echo "version=0.0.${GITHUB_RUN_NUMBER}" >> "$GITHUB_OUTPUT" | |
| echo "publish=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| macos: | |
| needs: version | |
| runs-on: macos-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Install FFmpeg | |
| run: brew install ffmpeg | |
| - name: Build | |
| env: | |
| HEADLESS_VERSION: ${{ needs.version.outputs.version }} | |
| run: ./apps/headless/build.sh | |
| - name: Unit tests | |
| run: ./apps/headless/test.sh | |
| - name: E2E | |
| run: zsh ./apps/headless/Tests/macos-e2e.sh | |
| - name: Package | |
| env: | |
| VERSION: ${{ needs.version.outputs.version }} | |
| run: | | |
| set -eu | |
| cd apps/headless | |
| ditto -c -k --keepParent Headless.app "Headless-${VERSION}-macos.zip" | |
| test -s "Headless-${VERSION}-macos.zip" | |
| unzip -t "Headless-${VERSION}-macos.zip" | |
| unzip -Z1 "Headless-${VERSION}-macos.zip" | grep -qx 'Headless.app/Contents/MacOS/Headless' | |
| unzip -Z1 "Headless-${VERSION}-macos.zip" | grep -qx 'Headless.app/Contents/Resources/bin/headless' | |
| unzip -Z1 "Headless-${VERSION}-macos.zip" | grep -qx 'Headless.app/Contents/Resources/bin/headless-mcp' | |
| - uses: actions/upload-artifact@v7 | |
| with: | |
| name: macos | |
| path: apps/headless/Headless-${{ needs.version.outputs.version }}-macos.zip | |
| linux-amd64: | |
| needs: version | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Build | |
| run: HEADLESS_LINUX_PLATFORM=linux/amd64 ./apps/headless/build-linux.sh | |
| - name: E2E | |
| run: ./apps/headless/Tests/linux-docker.sh | |
| - name: Package | |
| env: | |
| VERSION: ${{ needs.version.outputs.version }} | |
| run: | | |
| set -eu | |
| cp apps/headless/build/headless-linux-amd64.tar.gz \ | |
| "apps/headless/build/headless-${VERSION}-linux-amd64.tar.gz" | |
| archive="apps/headless/build/headless-${VERSION}-linux-amd64.tar.gz" | |
| test -s "$archive" | |
| tar -tzf "$archive" > archive-contents.txt | |
| for expected in headless headless-host headless-mcp install-linux.sh Headless_HeadlessProtocol.resources/AgentRuntime.js; do | |
| grep -qx "$expected" archive-contents.txt | |
| done | |
| - uses: actions/upload-artifact@v7 | |
| with: | |
| name: linux-amd64 | |
| path: apps/headless/build/headless-${{ needs.version.outputs.version }}-linux-amd64.tar.gz | |
| linux-arm64: | |
| needs: version | |
| runs-on: ubuntu-24.04-arm | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Build | |
| run: HEADLESS_LINUX_PLATFORM=linux/arm64 ./apps/headless/build-linux.sh | |
| - name: E2E | |
| run: ./apps/headless/Tests/linux-docker.sh | |
| - name: Package | |
| env: | |
| VERSION: ${{ needs.version.outputs.version }} | |
| run: | | |
| set -eu | |
| cp apps/headless/build/headless-linux-arm64.tar.gz \ | |
| "apps/headless/build/headless-${VERSION}-linux-arm64.tar.gz" | |
| archive="apps/headless/build/headless-${VERSION}-linux-arm64.tar.gz" | |
| test -s "$archive" | |
| tar -tzf "$archive" > archive-contents.txt | |
| for expected in headless headless-host headless-mcp install-linux.sh Headless_HeadlessProtocol.resources/AgentRuntime.js; do | |
| grep -qx "$expected" archive-contents.txt | |
| done | |
| - uses: actions/upload-artifact@v7 | |
| with: | |
| name: linux-arm64 | |
| path: apps/headless/build/headless-${{ needs.version.outputs.version }}-linux-arm64.tar.gz | |
| publish: | |
| needs: [version, macos, linux-amd64, linux-arm64] | |
| if: needs.version.outputs.publish == 'true' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - uses: actions/download-artifact@v8 | |
| with: | |
| path: dist | |
| merge-multiple: true | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@v3 | |
| with: | |
| tag_name: ${{ github.ref_name }} | |
| name: Headless ${{ needs.version.outputs.version }} | |
| body: | | |
| ## Downloads | |
| | File | Platform | | |
| | --- | --- | | |
| | `Headless-${{ needs.version.outputs.version }}-macos.zip` | macOS (ad-hoc signed `.app`) | | |
| | `headless-${{ needs.version.outputs.version }}-linux-amd64.tar.gz` | Linux x86_64 | | |
| | `headless-${{ needs.version.outputs.version }}-linux-arm64.tar.gz` | Linux arm64 | | |
| ### Notes | |
| - **macOS:** Unzip and run `Headless.app`. Gatekeeper may warn (ad-hoc signature; notarization not included yet). CLI: `Headless.app/Contents/Resources/bin/headless`. | |
| - **Linux tarball:** Does not bundle Chromium. Install with `./install-linux.sh` after ensuring non-Snap Chromium and FFmpeg are available. The Docker production image remains the self-contained runtime (not published by this release). | |
| files: | | |
| dist/Headless-${{ needs.version.outputs.version }}-macos.zip | |
| dist/headless-${{ needs.version.outputs.version }}-linux-amd64.tar.gz | |
| dist/headless-${{ needs.version.outputs.version }}-linux-arm64.tar.gz | |
| fail_on_unmatched_files: true |