Release v0.3.5 #11
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: | |
| tag_name: | |
| description: 'Release tag to publish, for example v0.3.0' | |
| required: true | |
| type: string | |
| permissions: | |
| contents: write | |
| jobs: | |
| verify: | |
| name: Verify release gates | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 45 | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '22' | |
| cache: npm | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Audit release configuration | |
| run: npm run release:audit | |
| - name: Build | |
| run: npm run build | |
| - name: Runtime tests | |
| run: npm run test:runtime | |
| - name: UI smoke | |
| run: npm run ui:smoke | |
| - name: UI maturity gate | |
| run: npm run ui:maturity-gate | |
| - name: Agent benchmark | |
| run: npm run agent:benchmark | |
| mac: | |
| name: Build macOS release | |
| needs: verify | |
| runs-on: macos-latest | |
| timeout-minutes: 90 | |
| env: | |
| APPLE_ID: ${{ secrets.APPLE_ID }} | |
| APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} | |
| APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} | |
| CSC_NAME: Developer ID Application | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '22' | |
| cache: npm | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Import macOS signing certificate | |
| env: | |
| MAC_CSC_LINK: ${{ secrets.MAC_CSC_LINK }} | |
| MAC_CSC_KEY_PASSWORD: ${{ secrets.MAC_CSC_KEY_PASSWORD }} | |
| run: | | |
| set -euo pipefail | |
| if [ -z "${MAC_CSC_LINK}" ]; then | |
| echo "MAC_CSC_LINK must contain a base64-encoded Developer ID Application .p12 certificate." | |
| exit 1 | |
| fi | |
| certificate_path="$RUNNER_TEMP/funplay-mac-codesign.p12" | |
| keychain_path="$RUNNER_TEMP/funplay-signing.keychain-db" | |
| keychain_password="$(uuidgen)" | |
| certificate_data="$MAC_CSC_LINK" | |
| case "$certificate_data" in | |
| data:*base64,*) certificate_data="${certificate_data#*,}" ;; | |
| esac | |
| if ! printf '%s' "$certificate_data" | base64 -D > "$certificate_path"; then | |
| echo "MAC_CSC_LINK must be base64-encoded .p12 data, not a local path." | |
| exit 1 | |
| fi | |
| 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 "$certificate_path" -P "$MAC_CSC_KEY_PASSWORD" -f pkcs12 -A -k "$keychain_path" -T /usr/bin/codesign -T /usr/bin/productbuild -T /usr/bin/security | |
| security list-keychains -d user -s "$keychain_path" | |
| security default-keychain -s "$keychain_path" | |
| identities="$(security find-identity -v -p codesigning "$keychain_path")" | |
| echo "$identities" | |
| if ! echo "$identities" | grep -q "Developer ID Application"; then | |
| echo "The imported macOS certificate is not a Developer ID Application certificate." | |
| exit 1 | |
| fi | |
| security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$keychain_password" "$keychain_path" || echo "::warning::Could not update key partition list; continuing because the signing identity was imported and the keychain is unlocked." | |
| - name: Build split-architecture macOS artifacts | |
| run: npm run dist:mac:split | |
| - name: Verify packaged runtime dependencies | |
| run: npm run release:verify-runtime-deps | |
| - name: Verify macOS update metadata | |
| run: npm run release:verify-mac-updates | |
| - name: Restore Electron native ABI | |
| if: always() | |
| run: npm run rebuild:native:force | |
| - name: Upload macOS artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: funplay-macos | |
| if-no-files-found: error | |
| path: | | |
| release/*.dmg | |
| release/*.dmg.blockmap | |
| release/*.zip | |
| release/*.zip.blockmap | |
| release/latest-mac.yml | |
| release/latest-mac-arm64.yml | |
| release/latest-mac-x64.yml | |
| windows: | |
| name: Build Windows release | |
| needs: verify | |
| runs-on: windows-latest | |
| timeout-minutes: 75 | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '22' | |
| cache: npm | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Build Windows x64 artifacts | |
| run: npm run dist:win:x64 | |
| - name: Verify packaged runtime dependencies | |
| run: npm run release:verify-runtime-deps | |
| - name: Restore Electron native ABI | |
| if: always() | |
| run: npm run rebuild:native:force | |
| - name: Upload Windows artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: funplay-windows | |
| if-no-files-found: error | |
| path: | | |
| release/*.exe | |
| release/*.exe.blockmap | |
| release/latest.yml | |
| publish: | |
| name: Publish GitHub Release | |
| needs: | |
| - mac | |
| - windows | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| RELEASE_TAG: ${{ github.event_name == 'workflow_dispatch' && inputs.tag_name || github.ref_name }} | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@v4 | |
| - name: Download release artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| - name: Prepare release notes | |
| run: | | |
| set -euo pipefail | |
| node <<'NODE' | |
| const fs = require('node:fs'); | |
| const version = process.env.RELEASE_TAG.replace(/^v/, ''); | |
| const changelog = fs.readFileSync('CHANGELOG.md', 'utf8').split(/\r?\n/); | |
| const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); | |
| const heading = new RegExp(`^##\\s+${escapeRegExp(version)}(?:\\s|$)`); | |
| const start = changelog.findIndex((line) => heading.test(line)); | |
| if (start < 0) { | |
| throw new Error(`CHANGELOG.md does not contain a section for ${version}.`); | |
| } | |
| let end = changelog.length; | |
| for (let index = start + 1; index < changelog.length; index += 1) { | |
| if (/^##\s+\S/.test(changelog[index])) { | |
| end = index; | |
| break; | |
| } | |
| } | |
| const notes = changelog.slice(start + 1, end).join('\n').trim(); | |
| if (!notes) { | |
| throw new Error(`CHANGELOG.md section for ${version} is empty.`); | |
| } | |
| fs.writeFileSync('release-notes.md', `${notes}\n`, 'utf8'); | |
| NODE | |
| - name: Create or update release | |
| run: | | |
| set -euo pipefail | |
| mapfile -d '' files < <(find artifacts -type f -print0 | sort -z) | |
| if [ "${#files[@]}" -eq 0 ]; then | |
| echo "No release artifacts found." | |
| exit 1 | |
| fi | |
| title="$RELEASE_TAG" | |
| if gh release view "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then | |
| gh release upload "$RELEASE_TAG" "${files[@]}" --clobber --repo "$GITHUB_REPOSITORY" | |
| gh release edit "$RELEASE_TAG" --draft=false --title "$title" --notes-file release-notes.md --repo "$GITHUB_REPOSITORY" | |
| else | |
| gh release create "$RELEASE_TAG" "${files[@]}" --title "$title" --notes-file release-notes.md --repo "$GITHUB_REPOSITORY" | |
| fi |