test: align ChatView browser running fixture #99
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*.*.*" | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: "Release version (for example 1.2.3 or v1.2.3)" | |
| required: true | |
| type: string | |
| permissions: | |
| contents: write | |
| jobs: | |
| preflight: | |
| name: Preflight | |
| runs-on: ubuntu-24.04 | |
| outputs: | |
| version: ${{ steps.release_meta.outputs.version }} | |
| tag: ${{ steps.release_meta.outputs.tag }} | |
| is_prerelease: ${{ steps.release_meta.outputs.is_prerelease }} | |
| make_latest: ${{ steps.release_meta.outputs.make_latest }} | |
| release_channel: ${{ steps.release_meta.outputs.release_channel }} | |
| npm_tag: ${{ steps.release_meta.outputs.npm_tag }} | |
| build_timestamp: ${{ steps.release_meta.outputs.build_timestamp }} | |
| ref: ${{ github.sha }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| - id: release_meta | |
| name: Resolve release version | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then | |
| raw="${{ github.event.inputs.version }}" | |
| else | |
| raw="${GITHUB_REF_NAME}" | |
| fi | |
| version="${raw#v}" | |
| if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?$ ]]; then | |
| echo "Invalid release version: $raw" >&2 | |
| exit 1 | |
| fi | |
| build_timestamp="$(date -u +"%Y-%m-%dT%H:%M:%SZ")" | |
| echo "version=$version" >> "$GITHUB_OUTPUT" | |
| echo "tag=v$version" >> "$GITHUB_OUTPUT" | |
| echo "build_timestamp=$build_timestamp" >> "$GITHUB_OUTPUT" | |
| if [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then | |
| echo "is_prerelease=false" >> "$GITHUB_OUTPUT" | |
| echo "make_latest=true" >> "$GITHUB_OUTPUT" | |
| echo "release_channel=stable" >> "$GITHUB_OUTPUT" | |
| echo "npm_tag=latest" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "is_prerelease=true" >> "$GITHUB_OUTPUT" | |
| echo "make_latest=false" >> "$GITHUB_OUTPUT" | |
| echo "release_channel=prerelease" >> "$GITHUB_OUTPUT" | |
| echo "npm_tag=next" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version-file: package.json | |
| - name: Setup Node | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version-file: package.json | |
| - name: Install dependencies | |
| run: bun install --frozen-lockfile | |
| - name: Install browser dependencies | |
| run: bun run --cwd apps/web test:browser:install | |
| - name: Format check | |
| run: bun run fmt:check | |
| - name: Lint | |
| run: bun run lint | |
| - name: Typecheck | |
| run: bun run typecheck | |
| - name: Test | |
| run: bun run test | |
| - name: Browser test | |
| run: bun run --cwd apps/web test:browser | |
| - name: Desktop smoke | |
| run: bun run test:desktop-smoke | |
| - name: Release smoke | |
| run: bun run release:smoke | |
| desktop_build: | |
| name: Desktop ${{ matrix.label }} | |
| needs: [preflight] | |
| runs-on: ${{ matrix.runner }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - label: macOS arm64 | |
| runner: macos-14 | |
| platform: mac | |
| target: dmg | |
| arch: arm64 | |
| - label: Linux x64 | |
| runner: ubuntu-24.04 | |
| platform: linux | |
| target: AppImage | |
| arch: x64 | |
| - label: Windows x64 | |
| runner: windows-2022 | |
| platform: win | |
| target: nsis | |
| arch: x64 | |
| env: | |
| OKCODE_COMMIT_HASH: ${{ github.sha }} | |
| OKCODE_BUILD_TIMESTAMP: ${{ needs.preflight.outputs.build_timestamp }} | |
| OKCODE_RELEASE_CHANNEL: ${{ needs.preflight.outputs.release_channel }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ needs.preflight.outputs.ref }} | |
| fetch-depth: 0 | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version-file: package.json | |
| - name: Setup Node | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version-file: package.json | |
| - name: Install dependencies | |
| run: bun install --frozen-lockfile | |
| - name: Align package versions to release version | |
| run: node scripts/update-release-package-versions.ts "${{ needs.preflight.outputs.version }}" | |
| - name: Build desktop artifact | |
| shell: bash | |
| env: | |
| CSC_LINK: ${{ secrets.CSC_LINK }} | |
| CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }} | |
| APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }} | |
| APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }} | |
| APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }} | |
| AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} | |
| AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} | |
| AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} | |
| AZURE_TRUSTED_SIGNING_ENDPOINT: ${{ secrets.AZURE_TRUSTED_SIGNING_ENDPOINT }} | |
| AZURE_TRUSTED_SIGNING_ACCOUNT_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_ACCOUNT_NAME }} | |
| AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME }} | |
| AZURE_TRUSTED_SIGNING_PUBLISHER_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_PUBLISHER_NAME }} | |
| run: | | |
| set -euo pipefail | |
| args=( | |
| --platform "${{ matrix.platform }}" | |
| --target "${{ matrix.target }}" | |
| --arch "${{ matrix.arch }}" | |
| --build-version "${{ needs.preflight.outputs.version }}" | |
| --verbose | |
| ) | |
| require_values() { | |
| for value in "$@"; do | |
| if [[ -z "$value" ]]; then | |
| return 1 | |
| fi | |
| done | |
| return 0 | |
| } | |
| if [[ "${{ matrix.platform }}" == "mac" ]]; then | |
| required_mac_signing_secrets=( | |
| CSC_LINK | |
| CSC_KEY_PASSWORD | |
| APPLE_API_KEY | |
| APPLE_API_KEY_ID | |
| APPLE_API_ISSUER | |
| ) | |
| missing_mac_signing_secrets=() | |
| for secret_name in "${required_mac_signing_secrets[@]}"; do | |
| if [[ -z "${!secret_name}" ]]; then | |
| missing_mac_signing_secrets+=("$secret_name") | |
| fi | |
| done | |
| if (( ${#missing_mac_signing_secrets[@]} > 0 )); then | |
| echo "Missing required macOS signing/notarization secrets: ${missing_mac_signing_secrets[*]}" >&2 | |
| exit 1 | |
| fi | |
| key_path="$RUNNER_TEMP/AuthKey_${APPLE_API_KEY_ID}.p8" | |
| printf '%s' "$APPLE_API_KEY" > "$key_path" | |
| export APPLE_API_KEY="$key_path" | |
| args+=(--signed --require-signed) | |
| elif [[ "${{ matrix.platform }}" == "win" ]]; then | |
| if require_values \ | |
| "$AZURE_TENANT_ID" \ | |
| "$AZURE_CLIENT_ID" \ | |
| "$AZURE_CLIENT_SECRET" \ | |
| "$AZURE_TRUSTED_SIGNING_ENDPOINT" \ | |
| "$AZURE_TRUSTED_SIGNING_ACCOUNT_NAME" \ | |
| "$AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME" \ | |
| "$AZURE_TRUSTED_SIGNING_PUBLISHER_NAME"; then | |
| args+=(--signed --require-signed) | |
| elif [[ "${{ needs.preflight.outputs.is_prerelease }}" == "false" ]]; then | |
| echo "Missing Azure Trusted Signing secrets for a stable Windows release." >&2 | |
| exit 1 | |
| else | |
| echo "Azure Trusted Signing secrets not configured; building unsigned Windows artifact." >&2 | |
| fi | |
| fi | |
| bun run dist:desktop:artifact -- "${args[@]}" | |
| - name: Validate packaged artifact outputs | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| shopt -s nullglob | |
| case "${{ matrix.platform }}" in | |
| mac) | |
| dmg=(release/*.dmg) | |
| manifest=(release/latest-mac*.yml) | |
| [[ ${#dmg[@]} -gt 0 ]] || { echo "Missing macOS DMG artifact" >&2; exit 1; } | |
| [[ ${#manifest[@]} -gt 0 ]] || { echo "Missing macOS updater manifest" >&2; exit 1; } | |
| ;; | |
| linux) | |
| appimage=(release/*.AppImage) | |
| [[ ${#appimage[@]} -gt 0 ]] || { echo "Missing Linux AppImage artifact" >&2; exit 1; } | |
| ;; | |
| win) | |
| installer=(release/*.exe) | |
| [[ ${#installer[@]} -gt 0 ]] || { echo "Missing Windows installer artifact" >&2; exit 1; } | |
| ;; | |
| esac | |
| - name: Collect release assets | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| mkdir -p release-publish | |
| shopt -s nullglob | |
| for pattern in \ | |
| "release/*.dmg" \ | |
| "release/*.zip" \ | |
| "release/*.AppImage" \ | |
| "release/*.exe" \ | |
| "release/*.blockmap" \ | |
| "release/latest*.yml"; do | |
| for file in $pattern; do | |
| cp "$file" release-publish/ | |
| done | |
| done | |
| - name: Upload desktop artifact bundle | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: desktop-${{ matrix.platform }}-${{ matrix.arch }} | |
| path: release-publish/* | |
| if-no-files-found: error | |
| ios_signing_preflight: | |
| name: iOS signing preflight | |
| needs: [preflight] | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - name: Check iOS signing secrets | |
| shell: bash | |
| env: | |
| IOS_CERTIFICATE_P12: ${{ secrets.IOS_CERTIFICATE_P12 }} | |
| IOS_CERTIFICATE_PASSWORD: ${{ secrets.IOS_CERTIFICATE_PASSWORD }} | |
| IOS_PROVISIONING_PROFILE: ${{ secrets.IOS_PROVISIONING_PROFILE }} | |
| IOS_PROVISIONING_PROFILE_NAME: ${{ secrets.IOS_PROVISIONING_PROFILE_NAME }} | |
| APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} | |
| APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }} | |
| APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }} | |
| APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }} | |
| run: | | |
| set -euo pipefail | |
| required_secrets=( | |
| IOS_CERTIFICATE_P12 | |
| IOS_CERTIFICATE_PASSWORD | |
| IOS_PROVISIONING_PROFILE | |
| IOS_PROVISIONING_PROFILE_NAME | |
| APPLE_TEAM_ID | |
| APPLE_API_KEY | |
| APPLE_API_KEY_ID | |
| APPLE_API_ISSUER | |
| ) | |
| missing=() | |
| for secret_name in "${required_secrets[@]}"; do | |
| if [[ -z "${!secret_name}" ]]; then | |
| missing+=("$secret_name") | |
| fi | |
| done | |
| if (( ${#missing[@]} > 0 )); then | |
| missing_csv="$(IFS=,; echo "${missing[*]}")" | |
| echo "Missing required iOS signing secrets: $missing_csv" >&2 | |
| exit 1 | |
| fi | |
| echo "All required iOS signing secrets are configured." | |
| ios_testflight: | |
| name: iOS TestFlight | |
| needs: [preflight, ios_signing_preflight] | |
| runs-on: macos-14 | |
| env: | |
| RELEASE_VERSION: ${{ needs.preflight.outputs.version }} | |
| OKCODE_COMMIT_HASH: ${{ github.sha }} | |
| OKCODE_BUILD_TIMESTAMP: ${{ needs.preflight.outputs.build_timestamp }} | |
| OKCODE_RELEASE_CHANNEL: ${{ needs.preflight.outputs.release_channel }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ needs.preflight.outputs.ref }} | |
| fetch-depth: 0 | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version-file: package.json | |
| - name: Setup Node | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version-file: package.json | |
| - name: Install dependencies | |
| run: bun install --frozen-lockfile | |
| - name: Align package versions to release version | |
| run: node scripts/update-release-package-versions.ts "$RELEASE_VERSION" | |
| - name: Update iOS version in Xcode project | |
| run: node scripts/update-ios-version.ts "$RELEASE_VERSION" --build-number "$GITHUB_RUN_NUMBER" | |
| - name: Build mobile web bundle | |
| run: bun run --cwd apps/mobile build | |
| - name: Sync Capacitor iOS | |
| run: bunx cap sync ios --deployment | |
| working-directory: apps/mobile | |
| - name: Log iOS build metadata | |
| run: | | |
| echo "version=$RELEASE_VERSION" | |
| echo "commit=$OKCODE_COMMIT_HASH" | |
| echo "build_timestamp=$OKCODE_BUILD_TIMESTAMP" | |
| echo "channel=$OKCODE_RELEASE_CHANNEL" | |
| - name: Install Apple certificate and provisioning profile | |
| env: | |
| IOS_CERTIFICATE_P12: ${{ secrets.IOS_CERTIFICATE_P12 }} | |
| IOS_CERTIFICATE_PASSWORD: ${{ secrets.IOS_CERTIFICATE_PASSWORD }} | |
| IOS_PROVISIONING_PROFILE: ${{ secrets.IOS_PROVISIONING_PROFILE }} | |
| run: | | |
| set -euo pipefail | |
| for secret_name in IOS_CERTIFICATE_P12 IOS_CERTIFICATE_PASSWORD IOS_PROVISIONING_PROFILE; do | |
| if [[ -z "${!secret_name}" ]]; then | |
| echo "Missing required secret: $secret_name" >&2 | |
| exit 1 | |
| fi | |
| done | |
| KEYCHAIN_PATH="$RUNNER_TEMP/app-signing.keychain-db" | |
| KEYCHAIN_PASSWORD="$(openssl rand -hex 16)" | |
| security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" | |
| security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH" | |
| security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" | |
| CERT_PATH="$RUNNER_TEMP/certificate.p12" | |
| echo "$IOS_CERTIFICATE_P12" | base64 --decode > "$CERT_PATH" | |
| security import "$CERT_PATH" \ | |
| -P "$IOS_CERTIFICATE_PASSWORD" \ | |
| -A -t cert -f pkcs12 \ | |
| -k "$KEYCHAIN_PATH" | |
| security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" | |
| security list-keychain -d user -s "$KEYCHAIN_PATH" | |
| PROFILE_PATH="$RUNNER_TEMP/profile.mobileprovision" | |
| echo "$IOS_PROVISIONING_PROFILE" | base64 --decode > "$PROFILE_PATH" | |
| mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles | |
| cp "$PROFILE_PATH" ~/Library/MobileDevice/Provisioning\ Profiles/ | |
| - name: Simulator smoke build | |
| run: | | |
| set -euo pipefail | |
| xcodebuild build \ | |
| -project apps/mobile/ios/App/App.xcodeproj \ | |
| -scheme App \ | |
| -configuration Debug \ | |
| -destination 'platform=iOS Simulator,name=iPhone 15' \ | |
| COMPILER_INDEX_STORE_ENABLE=NO | |
| - name: Build iOS archive | |
| env: | |
| APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} | |
| run: | | |
| set -euo pipefail | |
| xcodebuild archive \ | |
| -project apps/mobile/ios/App/App.xcodeproj \ | |
| -scheme App \ | |
| -configuration Release \ | |
| -destination 'generic/platform=iOS' \ | |
| -archivePath "$RUNNER_TEMP/App.xcarchive" \ | |
| CODE_SIGN_STYLE=Manual \ | |
| DEVELOPMENT_TEAM="$APPLE_TEAM_ID" \ | |
| CODE_SIGN_IDENTITY="iPhone Distribution" \ | |
| PROVISIONING_PROFILE_SPECIFIER="${{ secrets.IOS_PROVISIONING_PROFILE_NAME }}" \ | |
| -allowProvisioningUpdates \ | |
| COMPILER_INDEX_STORE_ENABLE=NO | |
| - name: Generate ExportOptions.plist | |
| env: | |
| APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} | |
| run: | | |
| cat > "$RUNNER_TEMP/ExportOptions.plist" <<PLIST | |
| <?xml version="1.0" encoding="UTF-8"?> | |
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |
| <plist version="1.0"> | |
| <dict> | |
| <key>method</key> | |
| <string>app-store-connect</string> | |
| <key>destination</key> | |
| <string>upload</string> | |
| <key>teamID</key> | |
| <string>${APPLE_TEAM_ID}</string> | |
| <key>uploadSymbols</key> | |
| <true/> | |
| <key>signingStyle</key> | |
| <string>manual</string> | |
| <key>provisioningProfiles</key> | |
| <dict> | |
| <key>com.openknots.okcode.mobile</key> | |
| <string>${{ secrets.IOS_PROVISIONING_PROFILE_NAME }}</string> | |
| </dict> | |
| </dict> | |
| </plist> | |
| PLIST | |
| - name: Export IPA | |
| run: | | |
| set -euo pipefail | |
| xcodebuild -exportArchive \ | |
| -archivePath "$RUNNER_TEMP/App.xcarchive" \ | |
| -exportPath "$RUNNER_TEMP/export" \ | |
| -exportOptionsPlist "$RUNNER_TEMP/ExportOptions.plist" | |
| - name: Write App Store Connect API key | |
| env: | |
| APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }} | |
| APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }} | |
| run: | | |
| set -euo pipefail | |
| KEY_DIR="$HOME/private_keys" | |
| mkdir -p "$KEY_DIR" | |
| printf '%s' "$APPLE_API_KEY" > "$KEY_DIR/AuthKey_${APPLE_API_KEY_ID}.p8" | |
| - name: Upload to TestFlight | |
| env: | |
| APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }} | |
| APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }} | |
| run: | | |
| set -euo pipefail | |
| IPA_FILE=$(find "$RUNNER_TEMP/export" -name "*.ipa" -print -quit) | |
| if [[ -z "$IPA_FILE" ]]; then | |
| echo "No IPA file found in export directory" >&2 | |
| exit 1 | |
| fi | |
| xcrun altool --upload-app \ | |
| -f "$IPA_FILE" \ | |
| -t ios \ | |
| --apiKey "$APPLE_API_KEY_ID" \ | |
| --apiIssuer "$APPLE_API_ISSUER" | |
| - name: Cleanup keychain | |
| if: always() | |
| run: | | |
| KEYCHAIN_PATH="$RUNNER_TEMP/app-signing.keychain-db" | |
| if [[ -f "$KEYCHAIN_PATH" ]]; then | |
| security delete-keychain "$KEYCHAIN_PATH" || true | |
| fi | |
| rm -f "$HOME/private_keys/AuthKey_${{ secrets.APPLE_API_KEY_ID }}.p8" || true | |
| publish_cli: | |
| name: Publish CLI | |
| needs: [preflight, desktop_build, ios_signing_preflight, ios_testflight] | |
| if: ${{ !cancelled() && needs.preflight.result == 'success' && needs.desktop_build.result == 'success' && needs.ios_signing_preflight.result == 'success' && needs.ios_testflight.result == 'success' }} | |
| runs-on: ubuntu-24.04 | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }} | |
| OKCODE_COMMIT_HASH: ${{ github.sha }} | |
| OKCODE_BUILD_TIMESTAMP: ${{ needs.preflight.outputs.build_timestamp }} | |
| OKCODE_RELEASE_CHANNEL: ${{ needs.preflight.outputs.release_channel }} | |
| steps: | |
| - name: Checkout release commit | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ needs.preflight.outputs.ref }} | |
| fetch-depth: 0 | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version-file: package.json | |
| - name: Setup Node | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version-file: package.json | |
| registry-url: "https://registry.npmjs.org" | |
| - name: Install dependencies | |
| run: bun install --frozen-lockfile | |
| - name: Validate npm publish token | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| [[ -n "$NODE_AUTH_TOKEN" ]] || { echo "Missing secret NODE_AUTH_TOKEN" >&2; exit 1; } | |
| - name: Align package versions to release version | |
| run: node scripts/update-release-package-versions.ts "${{ needs.preflight.outputs.version }}" | |
| - name: Build CLI package | |
| run: bun run build --filter=@okcode/web --filter=okcodes | |
| - name: Verify npm pack | |
| working-directory: apps/server | |
| run: npm pack | |
| - name: Verify local CLI entrypoints | |
| working-directory: apps/server | |
| run: | | |
| node dist/index.mjs --version | |
| node dist/index.mjs --help >/dev/null | |
| node dist/index.mjs doctor --help >/dev/null | |
| - name: Publish okcodes | |
| run: > | |
| node apps/server/scripts/cli.ts publish | |
| --tag "${{ needs.preflight.outputs.npm_tag }}" | |
| --app-version "${{ needs.preflight.outputs.version }}" | |
| --verbose | |
| - name: Verify published CLI | |
| run: | | |
| npx --yes okcodes@${{ needs.preflight.outputs.version }} --version | |
| npx --yes okcodes@${{ needs.preflight.outputs.version }} --help >/dev/null | |
| release: | |
| name: Publish GitHub Release | |
| needs: [preflight, desktop_build, ios_signing_preflight, ios_testflight, publish_cli] | |
| if: ${{ always() && !cancelled() && needs.preflight.result == 'success' && needs.desktop_build.result == 'success' && needs.ios_signing_preflight.result == 'success' && needs.ios_testflight.result == 'success' && needs.publish_cli.result == 'success' }} | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ needs.preflight.outputs.ref }} | |
| - name: Setup Node | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version-file: package.json | |
| - name: Download desktop artifacts | |
| uses: actions/download-artifact@v8 | |
| with: | |
| pattern: desktop-* | |
| merge-multiple: true | |
| path: release-assets | |
| - name: Stage release documentation | |
| env: | |
| RELEASE_VERSION: ${{ needs.preflight.outputs.version }} | |
| run: | | |
| set -euo pipefail | |
| notes="docs/releases/v${RELEASE_VERSION}.md" | |
| manifest="docs/releases/v${RELEASE_VERSION}/assets.md" | |
| [[ -f "$notes" ]] || { echo "Missing release notes: $notes" >&2; exit 1; } | |
| [[ -f "$manifest" ]] || { echo "Missing asset manifest: $manifest" >&2; exit 1; } | |
| cp CHANGELOG.md release-assets/okcode-CHANGELOG.md | |
| cp "$notes" release-assets/okcode-RELEASE-NOTES.md | |
| cp "$manifest" release-assets/okcode-ASSETS-MANIFEST.md | |
| - name: Validate release asset completeness | |
| run: node scripts/validate-release-assets.ts release-assets | |
| - name: Publish release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ needs.preflight.outputs.tag }} | |
| target_commitish: ${{ needs.preflight.outputs.ref }} | |
| name: OK Code v${{ needs.preflight.outputs.version }} | |
| body_path: docs/releases/v${{ needs.preflight.outputs.version }}.md | |
| prerelease: ${{ needs.preflight.outputs.is_prerelease }} | |
| make_latest: ${{ needs.preflight.outputs.make_latest }} | |
| files: release-assets/* | |
| fail_on_unmatched_files: true | |
| finalize: | |
| name: Finalize release | |
| needs: [preflight, release] | |
| if: ${{ !cancelled() && needs.release.result == 'success' }} | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - name: Validate release app secrets | |
| shell: bash | |
| env: | |
| RELEASE_APP_ID: ${{ secrets.RELEASE_APP_ID }} | |
| RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} | |
| run: | | |
| set -euo pipefail | |
| [[ -n "$RELEASE_APP_ID" ]] || { echo "Missing secret RELEASE_APP_ID"; exit 1; } | |
| [[ "$RELEASE_APP_ID" =~ ^[0-9]+$ ]] || { echo "RELEASE_APP_ID must be a numeric GitHub App ID"; exit 1; } | |
| [[ -n "$RELEASE_APP_PRIVATE_KEY" ]] || { echo "Missing secret RELEASE_APP_PRIVATE_KEY"; exit 1; } | |
| - id: app_token | |
| name: Mint release app token | |
| uses: actions/create-github-app-token@v2 | |
| with: | |
| app-id: ${{ secrets.RELEASE_APP_ID }} | |
| private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} | |
| owner: ${{ github.repository_owner }} | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: main | |
| fetch-depth: 0 | |
| token: ${{ steps.app_token.outputs.token }} | |
| persist-credentials: true | |
| - id: app_bot | |
| name: Resolve GitHub App bot identity | |
| env: | |
| GH_TOKEN: ${{ steps.app_token.outputs.token }} | |
| APP_SLUG: ${{ steps.app_token.outputs.app-slug }} | |
| run: | | |
| user_id="$(gh api "/users/${APP_SLUG}[bot]" --jq .id)" | |
| echo "name=${APP_SLUG}[bot]" >> "$GITHUB_OUTPUT" | |
| echo "email=${user_id}+${APP_SLUG}[bot]@users.noreply.github.com" >> "$GITHUB_OUTPUT" | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version-file: package.json | |
| - name: Setup Node | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version-file: package.json | |
| - id: update_versions | |
| name: Update version strings | |
| env: | |
| RELEASE_VERSION: ${{ needs.preflight.outputs.version }} | |
| run: node scripts/update-release-package-versions.ts "$RELEASE_VERSION" --github-output | |
| - name: Format package.json files | |
| if: steps.update_versions.outputs.changed == 'true' | |
| run: bunx oxfmt apps/server/package.json apps/desktop/package.json apps/web/package.json apps/mobile/package.json packages/contracts/package.json | |
| - name: Refresh lockfile | |
| if: steps.update_versions.outputs.changed == 'true' | |
| run: bun install --lockfile-only --ignore-scripts | |
| - name: Commit and push version bump | |
| if: steps.update_versions.outputs.changed == 'true' | |
| shell: bash | |
| env: | |
| RELEASE_TAG: ${{ needs.preflight.outputs.tag }} | |
| run: | | |
| if git diff --quiet -- apps/server/package.json apps/desktop/package.json apps/web/package.json apps/mobile/package.json packages/contracts/package.json bun.lock; then | |
| echo "No version changes to commit." | |
| exit 0 | |
| fi | |
| git config user.name "${{ steps.app_bot.outputs.name }}" | |
| git config user.email "${{ steps.app_bot.outputs.email }}" | |
| git add apps/server/package.json apps/desktop/package.json apps/web/package.json apps/mobile/package.json packages/contracts/package.json bun.lock | |
| git commit -m "chore(release): prepare $RELEASE_TAG" | |
| git push origin HEAD:main |