Main Artifact Release #75
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: Main Artifact Release | |
| on: | |
| # Manual, plus a nightly build at ~23:17 UTC. Landing a commit on main no longer | |
| # auto-publishes a release. (Cron is UTC; adjust for local time / DST.) | |
| # Minute offset (17, not 0) avoids GitHub's top-of-hour scheduler congestion, | |
| # where minute-zero runs are frequently delayed or dropped. | |
| workflow_dispatch: | |
| schedule: | |
| - cron: "17 23 * * *" | |
| permissions: | |
| contents: write | |
| jobs: | |
| metadata: | |
| name: Resolve release metadata | |
| runs-on: ubuntu-24.04 | |
| # Releases are built only from main, even though the trigger is manual: | |
| # workflow_dispatch can target any ref, so gate every job on the main branch. | |
| if: ${{ github.ref == 'refs/heads/main' }} | |
| outputs: | |
| release_tag: ${{ steps.nightly.outputs.tag }} | |
| release_name: ${{ steps.nightly.outputs.name }} | |
| release_version: ${{ steps.nightly.outputs.version }} | |
| stable_baseline_tag: ${{ steps.stable_baseline.outputs.latest_tag }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ github.sha }} | |
| fetch-depth: 0 | |
| - name: Setup Vite+ | |
| uses: voidzero-dev/setup-vp@v1 | |
| with: | |
| node-version-file: package.json | |
| cache: true | |
| run-install: true | |
| - id: nightly | |
| name: Resolve nightly prerelease metadata | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| nightly_date="$(date -u +%Y%m%d)" | |
| node scripts/resolve-nightly-release.ts \ | |
| --date "$nightly_date" \ | |
| --run-number "${{ github.run_number }}" \ | |
| --sha "$GITHUB_SHA" \ | |
| --github-output | |
| - id: stable_baseline | |
| name: Snapshot latest stable tag | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| git tag --list 'v*' > "$RUNNER_TEMP/stable-tags.txt" | |
| node scripts/resolve-stable-promotion.ts \ | |
| --tags-file "$RUNNER_TEMP/stable-tags.txt" \ | |
| --github-output | |
| public_config: | |
| name: Resolve artifact public config | |
| runs-on: ubuntu-24.04 | |
| if: ${{ github.ref == 'refs/heads/main' }} | |
| timeout-minutes: 5 | |
| environment: | |
| name: production | |
| outputs: | |
| clerk_publishable_key: ${{ steps.public_config.outputs.clerk_publishable_key }} | |
| clerk_jwt_template: ${{ steps.public_config.outputs.clerk_jwt_template }} | |
| clerk_cli_oauth_client_id: ${{ steps.public_config.outputs.clerk_cli_oauth_client_id }} | |
| relay_url: ${{ steps.public_config.outputs.relay_url }} | |
| env: | |
| T3CODE_RELAY_URL: ${{ vars.T3CODE_RELAY_URL }} | |
| RELAY_DOMAIN: ${{ vars.RELAY_DOMAIN }} | |
| RELAY_API_ZONE_NAME: ${{ vars.RELAY_API_ZONE_NAME }} | |
| CLERK_PUBLISHABLE_KEY: ${{ vars.CLERK_PUBLISHABLE_KEY }} | |
| CLERK_JWT_TEMPLATE: ${{ vars.CLERK_JWT_TEMPLATE }} | |
| CLERK_CLI_OAUTH_CLIENT_ID: ${{ vars.CLERK_CLI_OAUTH_CLIENT_ID }} | |
| steps: | |
| - id: public_config | |
| name: Resolve build-time cloud config | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| relay_url="${T3CODE_RELAY_URL:-}" | |
| if [[ -z "$relay_url" ]]; then | |
| relay_domain="${RELAY_DOMAIN:-}" | |
| if [[ -z "$relay_domain" && -n "${RELAY_API_ZONE_NAME:-}" ]]; then | |
| relay_domain="relay.$RELAY_API_ZONE_NAME" | |
| fi | |
| if [[ -n "$relay_domain" ]]; then | |
| relay_url="https://$relay_domain" | |
| fi | |
| fi | |
| # This fork ships local-only artifacts, so cloud sign-in config is | |
| # optional. Embed it only when the full set is present and the relay | |
| # URL is HTTPS; otherwise warn and emit empty values so desktop | |
| # builds still succeed with cloud features disabled. | |
| clerk_publishable_key="" | |
| clerk_jwt_template="" | |
| clerk_cli_oauth_client_id="" | |
| resolved_relay_url="" | |
| required=( | |
| relay_url | |
| CLERK_PUBLISHABLE_KEY | |
| CLERK_JWT_TEMPLATE | |
| CLERK_CLI_OAUTH_CLIENT_ID | |
| ) | |
| missing=() | |
| for name in "${required[@]}"; do | |
| if [[ -z "${!name:-}" ]]; then | |
| missing+=("$name") | |
| fi | |
| done | |
| if (( ${#missing[@]} > 0 )); then | |
| echo "::warning::Cloud public config incomplete (${missing[*]}); building local-only artifacts without cloud sign-in." | |
| elif [[ ! "$relay_url" =~ ^https:// ]]; then | |
| echo "::warning::Ignoring non-HTTPS relay_url ($relay_url); building local-only artifacts." | |
| else | |
| clerk_publishable_key="$CLERK_PUBLISHABLE_KEY" | |
| clerk_jwt_template="$CLERK_JWT_TEMPLATE" | |
| clerk_cli_oauth_client_id="$CLERK_CLI_OAUTH_CLIENT_ID" | |
| resolved_relay_url="$relay_url" | |
| fi | |
| { | |
| echo "clerk_publishable_key=$clerk_publishable_key" | |
| echo "clerk_jwt_template=$clerk_jwt_template" | |
| echo "clerk_cli_oauth_client_id=$clerk_cli_oauth_client_id" | |
| echo "relay_url=$resolved_relay_url" | |
| } >> "$GITHUB_OUTPUT" | |
| publish_artifacts: | |
| name: Build and publish artifacts | |
| if: ${{ github.ref == 'refs/heads/main' }} | |
| needs: [metadata, public_config] | |
| uses: ./.github/workflows/reusable-build-release-artifacts.yml | |
| with: | |
| ref: ${{ github.sha }} | |
| release_tag: ${{ needs.metadata.outputs.release_tag }} | |
| release_name: ${{ needs.metadata.outputs.release_name }} | |
| release_version: ${{ needs.metadata.outputs.release_version }} | |
| clerk_publishable_key: ${{ needs.public_config.outputs.clerk_publishable_key }} | |
| clerk_jwt_template: ${{ needs.public_config.outputs.clerk_jwt_template }} | |
| clerk_cli_oauth_client_id: ${{ needs.public_config.outputs.clerk_cli_oauth_client_id }} | |
| relay_url: ${{ needs.public_config.outputs.relay_url }} | |
| # Main artifacts use nightly-channel app versions, so every release must | |
| # be a GitHub prerelease for the nightly updater query to discover it. | |
| prerelease: true | |
| windows_signing: true | |
| secrets: inherit | |
| record_nightly_source: | |
| name: Record verified-nightly source | |
| needs: [metadata, publish_artifacts] | |
| if: ${{ github.ref == 'refs/heads/main' && needs.publish_artifacts.result == 'success' }} | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 5 | |
| steps: | |
| - name: Write immutable source metadata | |
| env: | |
| NIGHTLY_TAG: ${{ needs.metadata.outputs.release_tag }} | |
| NIGHTLY_VERSION: ${{ needs.metadata.outputs.release_version }} | |
| STABLE_BASELINE_TAG: ${{ needs.metadata.outputs.stable_baseline_tag }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| jq -n \ | |
| --arg commit "$GITHUB_SHA" \ | |
| --arg nightlyTag "$NIGHTLY_TAG" \ | |
| --arg nightlyVersion "$NIGHTLY_VERSION" \ | |
| --arg stableBaselineTag "$STABLE_BASELINE_TAG" \ | |
| --argjson sourceRunId "$GITHUB_RUN_ID" \ | |
| --arg sourceRunUrl "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" \ | |
| '{ | |
| schemaVersion: 1, | |
| commit: $commit, | |
| nightlyTag: $nightlyTag, | |
| nightlyVersion: $nightlyVersion, | |
| stableBaselineTag: $stableBaselineTag, | |
| sourceRunId: $sourceRunId, | |
| sourceRunUrl: $sourceRunUrl | |
| }' > verified-nightly-source.json | |
| - name: Upload verified-nightly source metadata | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: verified-nightly-source | |
| path: verified-nightly-source.json | |
| if-no-files-found: error | |
| retention-days: 14 | |
| prune_nightly: | |
| name: Prune old nightly releases | |
| needs: publish_artifacts | |
| # Only after a successful publish on main; keeps the Releases page from | |
| # accumulating a daily nightly prerelease forever. Stable v* tags without | |
| # nightly prerelease metadata are untouched. | |
| if: ${{ github.ref == 'refs/heads/main' && needs.publish_artifacts.result == 'success' }} | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 10 | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Keep only the 14 most recent nightly prereleases | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| GH_REPO: ${{ github.repository }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| pointer_json="$( | |
| gh api "repos/$GH_REPO/contents/client-verified-latest.json?ref=client-verified-latest" \ | |
| --jq '.content' | base64 -d | |
| )" || { | |
| echo "::warning::Unable to read client-verified-latest pointer; skipping nightly pruning." | |
| exit 0 | |
| } | |
| pinned_release_tag="$( | |
| jq -r '(.desktop.artifacts.release // "") | rtrimstr("/") | split("/")[-1]' \ | |
| <<< "$pointer_json" | |
| )" || { | |
| echo "::warning::Unable to parse client-verified-latest pointer; skipping nightly pruning." | |
| exit 0 | |
| } | |
| if [[ -z "$pinned_release_tag" || "$pinned_release_tag" == "null" ]]; then | |
| echo "::warning::No verified client release is pinned; skipping nightly pruning." | |
| exit 0 | |
| fi | |
| echo "Keeping verified client pointer release $pinned_release_tag" | |
| stale="$( | |
| gh release list --limit 300 --json tagName,isPrerelease,createdAt \ | |
| | jq -r --arg keep "$pinned_release_tag" \ | |
| '[.[] | select(.isPrerelease and ((.tagName | startswith("main-")) or (.tagName | test("^(?:nightly-)?v[0-9]+\\.[0-9]+\\.[0-9]+-nightly\\.[0-9]{8}\\.[0-9]+$"))) and .tagName != $keep)] | |
| | sort_by(.createdAt) | reverse | .[14:] | .[].tagName' | |
| )" | |
| if [[ -z "$stale" ]]; then | |
| echo "No stale nightly releases to prune." | |
| exit 0 | |
| fi | |
| while IFS= read -r tag; do | |
| [[ -n "$tag" ]] || continue | |
| echo "Deleting old nightly release $tag" | |
| gh release delete "$tag" --cleanup-tag --yes || true | |
| done <<< "$stale" |