Skip to content

release

release #24

Workflow file for this run

name: release
on:
workflow_dispatch:
inputs:
channel:
description: Release policy channel
required: true
type: choice
options:
- preview
- stable
version:
description: Semantic release version (preview requires a prerelease)
required: true
type: string
trust:
description: Produce Developer ID-signed and notarized Apple artifacts
required: true
default: true
type: boolean
publish:
description: Enter the separately approved public publication job
required: true
default: false
type: boolean
permissions:
contents: read
concurrency:
group: portreeve-release-${{ inputs.version }}
cancel-in-progress: false
env:
BUN_VERSION: 1.3.14
NODE_VERSION: 22
RELEASE_ROOT: dist/releases/${{ inputs.version }}
TRUSTED_ROOT: dist/trusted/${{ inputs.version }}
PORTREEVE_HOMEPAGE_URL: ${{ github.server_url }}/${{ github.repository }}
PORTREEVE_RELEASE_BASE_URL:
${{ github.server_url }}/${{ github.repository }}/releases/download
jobs:
prepare:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: ${{ env.NODE_VERSION }}
- uses: oven-sh/setup-bun@v2
with:
bun-version: ${{ env.BUN_VERSION }}
- run: bun ci
- run: bun run check
- name: Prepare exact build-once release workspace
run: >-
bun run release:prepare -- --channel "${{ inputs.channel }}" --version "${{
inputs.version }}" ${{ inputs.trust && '--trusted' || '' }}
- uses: actions/upload-artifact@v7
with:
name: prepared-${{ inputs.version }}
path: ${{ env.RELEASE_ROOT }}
if-no-files-found: error
retention-days: 7
native-evidence:
needs: prepare
strategy:
fail-fast: false
matrix:
include:
- runner: macos-15
platform: macos-arm64
- runner: macos-15-intel
platform: macos-x64
- runner: ubuntu-24.04
platform: linux-x64
- runner: ubuntu-24.04-arm
platform: linux-arm64
runs-on: ${{ matrix.runner }}
name: native evidence (${{ matrix.platform }})
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: ${{ env.NODE_VERSION }}
- uses: oven-sh/setup-bun@v2
with:
bun-version: ${{ env.BUN_VERSION }}
- run: bun ci
- uses: actions/download-artifact@v8
with:
name: prepared-${{ inputs.version }}
path: ${{ env.RELEASE_ROOT }}
- name: Restore promoted executable modes
run: chmod 755 "$RELEASE_ROOT"/artifacts/portreeve-v*
- if: startsWith(matrix.platform, 'linux-')
name: Verify a real mixed process and Docker stack
run: bun run stacks:verify
- name: Execute the promoted native artifact and supervised lifecycle
run: >-
bun run release:native-evidence -- --record
"$RELEASE_ROOT/release-record.json"
- uses: actions/upload-artifact@v7
with:
name: native-${{ matrix.platform }}
path: ${{ env.RELEASE_ROOT }}/evidence/native-${{ matrix.platform }}.json
if-no-files-found: error
retention-days: 7
aggregate-native-evidence:
if: ${{ !inputs.trust }}
needs: native-evidence
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: ${{ env.NODE_VERSION }}
- uses: oven-sh/setup-bun@v2
with:
bun-version: ${{ env.BUN_VERSION }}
- run: bun ci
- uses: actions/download-artifact@v8
with:
name: prepared-${{ inputs.version }}
path: ${{ env.RELEASE_ROOT }}
- uses: actions/download-artifact@v8
with:
pattern: native-*
path: ${{ env.RELEASE_ROOT }}/evidence
merge-multiple: true
- name: Join the complete four-target evidence matrix once
run: >-
bun run release:merge-native-evidence -- --record
"$RELEASE_ROOT/release-record.json" --evidence
"$RELEASE_ROOT"/evidence/native-*.json
- uses: actions/upload-artifact@v7
with:
name: native-verified-${{ inputs.version }}
path: ${{ env.RELEASE_ROOT }}
if-no-files-found: error
retention-days: 7
qualify-trust:
if: inputs.trust
needs: native-evidence
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: ${{ env.NODE_VERSION }}
- uses: oven-sh/setup-bun@v2
with:
bun-version: ${{ env.BUN_VERSION }}
- run: bun ci
- uses: actions/download-artifact@v8
with:
name: prepared-${{ inputs.version }}
path: ${{ env.RELEASE_ROOT }}
- uses: actions/download-artifact@v8
with:
pattern: native-*
path: ${{ env.RELEASE_ROOT }}/evidence
merge-multiple: true
- name: Qualify all native candidates without credential access
run: >-
bun run release:qualify-trust -- --record "$RELEASE_ROOT/release-record.json"
--evidence "$RELEASE_ROOT"/evidence/native-*.json
- uses: actions/upload-artifact@v7
with:
name: trust-qualified-${{ inputs.version }}
path: ${{ env.RELEASE_ROOT }}
if-no-files-found: error
retention-days: 7
release-trust:
if: inputs.trust && github.ref == 'refs/heads/main'
needs: qualify-trust
runs-on: macos-15
environment: release-trust
permissions:
contents: read
env:
PORTREEVE_APPLE_SIGNING_IDENTITY: ${{ vars.PORTREEVE_APPLE_SIGNING_IDENTITY }}
PORTREEVE_APPLE_TEAM_ID: ${{ vars.PORTREEVE_APPLE_TEAM_ID }}
PORTREEVE_APPLE_NOTARY_KEY_ID: ${{ vars.PORTREEVE_APPLE_NOTARY_KEY_ID }}
PORTREEVE_APPLE_NOTARY_ISSUER_ID: ${{ vars.PORTREEVE_APPLE_NOTARY_ISSUER_ID }}
PORTREEVE_APPLE_NOTARY_KEY_NAME: ${{ vars.PORTREEVE_APPLE_NOTARY_KEY_NAME }}
PORTREEVE_APPLE_CERTIFICATE_P12_BASE64:
${{ secrets.PORTREEVE_APPLE_CERTIFICATE_P12_BASE64 }}
PORTREEVE_APPLE_CERTIFICATE_PASSWORD:
${{ secrets.PORTREEVE_APPLE_CERTIFICATE_PASSWORD }}
PORTREEVE_APPLE_NOTARY_KEY_P8_BASE64:
${{ secrets.PORTREEVE_APPLE_NOTARY_KEY_P8_BASE64 }}
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: ${{ env.NODE_VERSION }}
- uses: oven-sh/setup-bun@v2
with:
bun-version: ${{ env.BUN_VERSION }}
- run: bun ci
- uses: actions/download-artifact@v8
with:
name: trust-qualified-${{ inputs.version }}
path: ${{ env.RELEASE_ROOT }}
- name: Produce both trusted macOS artifact sets without publishing
run: >-
bun run release:produce-apple-trust -- --record
"$RELEASE_ROOT/release-record.json" --qualification
"$RELEASE_ROOT/evidence/trust-qualification.json" --output "$TRUSTED_ROOT"
- name: Preserve failed notarization recovery evidence
if: failure()
uses: actions/upload-artifact@v7
with:
name: trusted-recovery-${{ inputs.version }}-${{ github.run_attempt }}
path: ${{ env.TRUSTED_ROOT }}/recovery
if-no-files-found: warn
retention-days: 7
- uses: actions/upload-artifact@v7
with:
name: trusted-${{ inputs.version }}
path: ${{ env.TRUSTED_ROOT }}
if-no-files-found: error
retention-days: 7
trusted-native-evidence:
needs: release-trust
strategy:
fail-fast: false
matrix:
include:
- runner: macos-15
architecture: arm64
- runner: macos-15-intel
architecture: x64
runs-on: ${{ matrix.runner }}
name: Apple trust evidence (macos-${{ matrix.architecture }})
permissions:
contents: read
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: ${{ env.NODE_VERSION }}
- uses: oven-sh/setup-bun@v2
with:
bun-version: ${{ env.BUN_VERSION }}
- run: bun ci
- uses: actions/download-artifact@v8
with:
name: trusted-${{ inputs.version }}
path: ${{ env.TRUSTED_ROOT }}
- name: Restore authoritative executable modes
run: chmod 755 "$TRUSTED_ROOT"/artifacts/portreeve-v*
- name: Verify exact Apple artifacts on their native architecture
run: >-
bun run release:apple-native-evidence -- --record
"$TRUSTED_ROOT/release-record.json" --producer-evidence
"$TRUSTED_ROOT/evidence/apple-trust-producer.json"
- uses: actions/upload-artifact@v7
with:
name: apple-native-${{ matrix.architecture }}
path:
${{ env.TRUSTED_ROOT }}/evidence/apple-native-${{ matrix.architecture
}}.json
if-no-files-found: error
retention-days: 7
finalize-trusted-distribution:
needs: trusted-native-evidence
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: ${{ env.NODE_VERSION }}
- uses: oven-sh/setup-bun@v2
with:
bun-version: ${{ env.BUN_VERSION }}
- run: bun ci
- uses: actions/download-artifact@v8
with:
name: trusted-${{ inputs.version }}
path: ${{ env.TRUSTED_ROOT }}
- uses: actions/download-artifact@v8
with:
pattern: apple-native-*
path: ${{ env.TRUSTED_ROOT }}/evidence
merge-multiple: true
- name: Seal exact distribution metadata and publication plan
run: >-
bun run release:finalize-desktop -- --record
"$TRUSTED_ROOT/release-record.json" --producer-evidence
"$TRUSTED_ROOT/evidence/apple-trust-producer.json" --apple-evidence
"$TRUSTED_ROOT"/evidence/apple-native-*.json --current-update-manifest
distribution/desktop-update.json
- uses: actions/upload-artifact@v7
with:
name: distribution-${{ inputs.version }}
path: ${{ env.TRUSTED_ROOT }}
if-no-files-found: error
retention-days: 14
desktop-evidence:
needs: aggregate-native-evidence
strategy:
fail-fast: false
matrix:
include:
- runner: macos-15
architecture: arm64
- runner: macos-15-intel
architecture: x64
runs-on: ${{ matrix.runner }}
name: Desktop evidence (macos-${{ matrix.architecture }})
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: ${{ env.NODE_VERSION }}
- uses: oven-sh/setup-bun@v2
with:
bun-version: ${{ env.BUN_VERSION }}
- run: bun ci
- uses: actions/download-artifact@v8
with:
name: native-verified-${{ inputs.version }}
path: ${{ env.RELEASE_ROOT }}
- name: Restore promoted executable modes
run: chmod 755 "$RELEASE_ROOT"/artifacts/portreeve-v*
- name: Package, launch, mount, and verify the native Desktop DMG
run: >-
bun run release:package-desktop -- --record
"$RELEASE_ROOT/release-record.json" --arch "${{ matrix.architecture }}"
- uses: actions/upload-artifact@v7
with:
name: desktop-${{ matrix.architecture }}
path: |
${{ env.RELEASE_ROOT }}/artifacts/PortReeve-*-macos-${{ matrix.architecture }}.dmg
${{ env.RELEASE_ROOT }}/evidence/desktop-macos-${{ matrix.architecture }}.json
if-no-files-found: error
retention-days: 7
finalize-distribution:
if: ${{ !inputs.trust }}
needs: desktop-evidence
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: ${{ env.NODE_VERSION }}
- uses: oven-sh/setup-bun@v2
with:
bun-version: ${{ env.BUN_VERSION }}
- run: bun ci
- uses: actions/download-artifact@v8
with:
name: native-verified-${{ inputs.version }}
path: ${{ env.RELEASE_ROOT }}
- uses: actions/download-artifact@v8
with:
pattern: desktop-*
path: ${{ env.RELEASE_ROOT }}
merge-multiple: true
- name: Finalize cask, checksums, update metadata, and publication plan
run: >-
bun run release:finalize-desktop -- --record
"$RELEASE_ROOT/release-record.json" --evidence
"$RELEASE_ROOT"/evidence/desktop-macos-*.json --current-update-manifest
distribution/desktop-update.json
- uses: actions/upload-artifact@v7
with:
name: distribution-${{ inputs.version }}
path: ${{ env.RELEASE_ROOT }}
if-no-files-found: error
retention-days: 14
publish:
if: ${{ always() && inputs.publish && !failure() && !cancelled() }}
needs: [finalize-distribution, finalize-trusted-distribution]
runs-on: ubuntu-24.04
environment: release-publication
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: ${{ env.NODE_VERSION }}
- uses: oven-sh/setup-bun@v2
with:
bun-version: ${{ env.BUN_VERSION }}
- run: bun ci
- uses: actions/download-artifact@v8
with:
name: distribution-${{ inputs.version }}
path: ${{ env.RELEASE_ROOT }}
- name: Publish exact prepared bytes after environment approval
run: >-
bun run release:publish -- --record "$RELEASE_ROOT/release-record.json"
--approved-by "${{ github.actor }} via release-publication environment"
--confirm
env:
GH_TOKEN: ${{ secrets.PORTREEVE_RELEASE_TOKEN }}
- uses: actions/upload-artifact@v7
with:
name: published-record-${{ inputs.version }}
path: |
${{ env.RELEASE_ROOT }}/release-record.json
${{ env.RELEASE_ROOT }}/publication-complete.json
if-no-files-found: error
retention-days: 30