Files
open-design/.github/workflows/release-beta.yml
T

1039 lines
53 KiB
YAML

name: release-beta
on:
workflow_dispatch:
inputs:
exact_name:
description: "Exact name: 1-12 lowercase letters or digits."
required: true
type: string
default: beta
ref:
description: "Branch, tag, or full commit to build. Empty uses the dispatched ref."
required: false
type: string
default: ""
amr_profile:
description: "Optional AMR profile override; exact channels default to test."
required: false
type: string
default: test
publish:
description: "Publish immutable assets and staged metadata."
required: true
type: boolean
default: true
promote:
description: "Accept public artifacts and move the exact latest pointer."
required: true
type: boolean
default: true
release_public_origin:
description: "Optional public release origin override."
required: false
type: string
default: ""
workflow_call:
inputs:
exact_name:
description: "Exact release name: 1-12 lowercase letters or digits."
required: false
type: string
default: beta
publish:
required: false
type: boolean
default: true
promote:
description: "Promote this build to the exact channel latest pointer after publication."
required: false
type: boolean
default: true
release_public_origin:
required: false
type: string
default: ""
ref:
required: false
type: string
default: ""
amr_profile:
required: false
type: string
default: test
notification_stream:
required: false
type: string
default: release
permissions:
actions: write
contents: read
concurrency:
group: open-design-release-beta-${{ inputs.exact_name }}
cancel-in-progress: false
env:
OPEN_DESIGN_AMR_PROFILE: ${{ inputs.amr_profile || 'test' }}
RELEASE_MAC_ARM64_SIGN_MODE: notarized
RELEASE_MAC_X64_SIGN_MODE: notarized
RELEASE_WIN_X64_SIGN_MODE: unsigned
# Release runners repeatedly install the same workspace before metadata and
# platform work. Reuse the repository's dependency-aware postinstall scheduler
# at a deliberately conservative, cross-platform fixed width; this keeps the
# release hot path simple while avoiding the serial workspace rebuild default.
OPEN_DESIGN_POSTINSTALL_CONCURRENCY: 2
LEGACY_MAC_ARM64_DIGEST: sha256:6d9c2fea2106fc0207acd4ee78bc8d08f9563f967121dbffd399b8d510d0c37f
LEGACY_MAC_ARM64_VERSION: 0.16.2-beta.155
LEGACY_WIN_X64_DIGEST: sha256:a0c2ef5d8b0da1ef7f93d375211fff396aa73d90b2cc3c591c27268fe6966330
LEGACY_WIN_X64_VERSION: 0.16.2-beta.155
OPEN_DESIGN_TELEMETRY_RELAY_URL: ${{ vars.OPEN_DESIGN_TELEMETRY_RELAY_URL }}
# `publish` governs distribution metadata only. Build inputs must remain
# identical between publish=false QA and the final publish=true lane so the
# verified immutable Shell bytes can be reused without a signing/notary pass.
POSTHOG_KEY: ${{ secrets.POSTHOG_KEY }}
POSTHOG_HOST: ${{ vars.POSTHOG_HOST }}
POSTHOG_CLI_API_KEY: ${{ secrets.POSTHOG_CLI_API_KEY }}
POSTHOG_CLI_PROJECT_ID: ${{ vars.POSTHOG_CLI_PROJECT_ID }}
# Vela web console origin for the AMR profile this build targets. Held in a
# per-profile repository secret instead of the source tree: the non-prod AMR
# environments are internal deployments and this repository is public, so
# neither the workflow nor the product code may name their hostnames.
#
# tools-pack bakes it into open-design-config.json and the packaged runtime
# forwards it to the daemon as OD_VELA_WEB_URL, which is the second half of the
# workspace-team gate (the first being the AMR profile allowlist). A profile
# whose secret is not configured therefore ships with workspace-team dormant
# rather than pointed at an unknown backend. Exact channels default to test;
# an explicit profile override selects its matching origin as one transaction.
#
# Secrets read here: VELA_WEB_URL_FEATURE_TEST, VELA_WEB_URL_TEST,
# VELA_WEB_URL_PROD.
OD_VELA_WEB_URL: ${{ inputs.amr_profile == 'feature-test' && secrets.VELA_WEB_URL_FEATURE_TEST || (inputs.amr_profile == 'test' || inputs.amr_profile == '') && secrets.VELA_WEB_URL_TEST || inputs.amr_profile == 'prod' && secrets.VELA_WEB_URL_PROD || '' }}
RELEASE_BRANCH: ${{ inputs.ref != '' && inputs.ref || github.ref_name }}
RELEASE_REPOSITORY: ${{ github.repository }}
RELEASE_RUN_ATTEMPT: ${{ github.run_attempt }}
RELEASE_RUN_ID: ${{ github.run_id }}
RELEASE_WORKFLOW: ${{ github.workflow }}
jobs:
metadata:
name: Prepare exact metadata and shared Closure
if: github.repository == 'nexu-io/open-design'
runs-on: ubuntu-latest
env:
OPEN_DESIGN_POSTINSTALL_LEVEL: release-prepare
RELEASE_PUBLIC_ORIGIN: ${{ inputs.release_public_origin != '' && inputs.release_public_origin || vars.CLOUDFLARE_R2_RELEASES_PUBLIC_ORIGIN }}
OPEN_DESIGN_EXACT_METADATA_URL: ${{ inputs.release_public_origin != '' && inputs.release_public_origin || vars.CLOUDFLARE_R2_RELEASES_PUBLIC_ORIGIN }}/${{ inputs.exact_name }}/latest/metadata.json
OPEN_DESIGN_STABLE_METADATA_URL: ${{ inputs.release_public_origin != '' && inputs.release_public_origin || vars.CLOUDFLARE_R2_RELEASES_PUBLIC_ORIGIN }}/stable/latest/metadata.json
outputs:
candidate_id: ${{ steps.candidate.outputs.candidate_id }}
base_version: ${{ steps.exact.outputs.base_version }}
release_version: ${{ steps.reservation.outputs.release_version || steps.exact.outputs.release_version }}
shell_version: ${{ steps.reservation.outputs.release_version || steps.exact.outputs.release_version }}
closure_version: ${{ steps.reservation.outputs.release_version || steps.exact.outputs.release_version }}
closure_min_shell_version: ${{ steps.exact.outputs.closure_min_shell_version }}
closure_shared_identity_digest: ${{ steps.execution.outputs.shared_closure_digest }}
mac_arm64_closure_identity_digest: ${{ steps.execution.outputs.mac_arm64_closure_digest }}
mac_arm64_shell_identity_digest: ${{ steps.execution.outputs.mac_arm64_shell_digest }}
mac_x64_closure_identity_digest: ${{ steps.execution.outputs.mac_x64_closure_digest }}
mac_x64_shell_identity_digest: ${{ steps.execution.outputs.mac_x64_shell_digest }}
win_x64_closure_identity_digest: ${{ steps.execution.outputs.win_x64_closure_digest }}
win_x64_shell_identity_digest: ${{ steps.execution.outputs.win_x64_shell_digest }}
branch: ${{ steps.identity.outputs.branch }}
commit: ${{ steps.identity.outputs.commit }}
previous_commit: ${{ steps.prev.outputs.previous_commit }}
promote: ${{ inputs.promote }}
release_name: ${{ steps.exact.outputs.release_name }}
state_source: ${{ steps.reservation.outputs.state_source || steps.exact.outputs.state_source }}
version_lock_key: ${{ steps.reservation.outputs.version_lock_key }}
build_matrix: ${{ steps.selection.outputs.build_matrix }}
build_count: ${{ steps.selection.outputs.build_count }}
hot_activated: ${{ steps.hot_activate.outcome == 'success' }}
hot_activation_completed_at: ${{ steps.hot_activate.outputs.activated_at }}
hot_staged: ${{ steps.hot_stage.outcome == 'success' }}
hot_acceptance_latest_url: ${{ steps.hot_stage.outputs.acceptance_latest_url }}
hot_release_state: ${{ steps.hot_stage.outputs.release_state }}
hot_version_metadata_url: ${{ steps.hot_stage.outputs.version_metadata_url }}
hot_mac_arm64_url: ${{ steps.hot_stage.outputs.mac_arm64_url }}
hot_mac_x64_url: ${{ steps.hot_stage.outputs.mac_x64_url }}
hot_win_x64_url: ${{ steps.hot_stage.outputs.win_x64_url }}
steps:
- name: Validate checkout ref shape
env:
REQUESTED_REF: ${{ inputs.ref }}
run: |
set -euo pipefail
if [[ "$REQUESTED_REF" =~ ^[0-9a-fA-F]{7,39}$ ]]; then
echo "release-beta ref must be a branch, tag, or full 40-character commit SHA; abbreviated SHA '$REQUESTED_REF' is ambiguous" >&2
exit 1
fi
- name: Checkout
uses: actions/checkout@v6.0.2
with:
ref: ${{ inputs.ref != '' && inputs.ref || github.ref }}
fetch-depth: 1
- name: Setup pnpm
uses: pnpm/action-setup@v5
with:
version: 10.33.2
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 24.18.0
cache: pnpm
cache-dependency-path: pnpm-lock.yaml
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Resolve built identity
id: identity
env:
BUILD_REF: ${{ inputs.ref != '' && inputs.ref || github.ref_name }}
DISPLAY_BRANCH: ${{ github.ref_name }}
run: |
set -euo pipefail
built_sha="$(git rev-parse HEAD)"
display_branch="$BUILD_REF"
if [[ "$BUILD_REF" =~ ^[0-9a-fA-F]{40}$ ]]; then
display_branch="$DISPLAY_BRANCH"
fi
echo "BUILT_SHA=$built_sha" >> "$GITHUB_ENV"
{
echo "branch=$display_branch"
echo "commit=$built_sha"
} >> "$GITHUB_OUTPUT"
- name: Validate exact publisher inputs and ref
if: ${{ inputs.publish }}
env:
REQUESTED_REF: ${{ inputs.ref != '' && inputs.ref || github.ref_name }}
run: |
set -euo pipefail
built_sha="$(git rev-parse HEAD)"
if [ -z "$REQUESTED_REF" ]; then
echo "release-beta publish requires an explicit branch, tag, or full commit identity" >&2
exit 1
fi
if [[ "$REQUESTED_REF" =~ ^[0-9a-fA-F]{40}$ ]]; then
expected_sha="${REQUESTED_REF,,}"
else
remote_refs="$(git ls-remote origin "refs/heads/$REQUESTED_REF" "refs/tags/$REQUESTED_REF^{}" "refs/tags/$REQUESTED_REF")"
expected_sha="$(printf '%s\n' "$remote_refs" | awk '
$2 ~ /\^\{\}$/ { print $1; found = 1; exit }
first == "" { first = $1 }
END { if (!found) print first }
')"
fi
if [ -z "$expected_sha" ]; then
echo "Could not resolve exact publisher ref '$REQUESTED_REF'" >&2
exit 1
fi
if [ "$built_sha" != "$expected_sha" ]; then
echo "release-beta publisher ref drifted: expected $expected_sha, built $built_sha" >&2
exit 1
fi
- name: Capture previous exact commit
id: prev
run: |
set -euo pipefail
prev="$(curl -fsSL "$OPEN_DESIGN_EXACT_METADATA_URL" | jq -r '.github.commit // ""' 2>/dev/null || true)"
echo "previous exact commit: ${prev:-<none>}"
echo "previous_commit=$prev" >> "$GITHUB_OUTPUT"
- name: Validate exact inputs
env:
EXACT_NAME: ${{ inputs.exact_name }}
run: |
set -euo pipefail
if [[ ! "$EXACT_NAME" =~ ^[a-z0-9]{1,12}$ ]] || [ "$EXACT_NAME" = "stable" ] || [ "$EXACT_NAME" = "prerelease" ] || [ "$EXACT_NAME" = "local" ]; then
echo "exact_name must be 1-12 lowercase letters or digits and cannot be stable, prerelease, or local" >&2
exit 1
fi
if [ "${{ inputs.promote }}" = "true" ] && [ "${{ inputs.publish }}" != "true" ]; then
echo "promote=true requires publish=true" >&2
exit 1
fi
- name: Validate R2 release access
env:
GITHUB_SHA: ${{ env.BUILT_SHA }}
R2_ACCESS_PROBE_NAME: release-${{ inputs.exact_name }}
RELEASE_CHANNEL: ${{ inputs.exact_name }}
RELEASE_PUBLIC_ORIGIN: ${{ env.RELEASE_PUBLIC_ORIGIN }}
RELEASE_STORAGE_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_R2_RELEASES_AK }}
RELEASE_STORAGE_BUCKET: ${{ secrets.CLOUDFLARE_R2_RELEASES_BUCKET }}
RELEASE_STORAGE_ENDPOINT: ${{ secrets.CLOUDFLARE_R2_RELEASES_URL }}
RELEASE_STORAGE_REGION: auto
RELEASE_STORAGE_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_R2_RELEASES_SK }}
run: pnpm exec tools-release check-storage
- name: Prepare exact release metadata
id: exact
env:
GITHUB_SHA: ${{ env.BUILT_SHA }}
GITHUB_REF_NAME: ${{ inputs.ref != '' && inputs.ref || github.ref_name }}
RELEASE_CHANNEL: ${{ inputs.exact_name }}
run: pnpm exec tools-release prepare "${{ inputs.exact_name }}"
- name: Reserve immutable exact version
id: reservation
env:
BASE_VERSION: ${{ steps.exact.outputs.base_version }}
GITHUB_SHA: ${{ env.BUILT_SHA }}
RELEASE_CHANNEL: ${{ inputs.exact_name }}
RELEASE_LANE: exact
RELEASE_METADATA_DIR: ${{ runner.temp }}/release-metadata
RELEASE_COMMIT: ${{ env.BUILT_SHA }}
RELEASE_PUBLIC_ORIGIN: ${{ env.RELEASE_PUBLIC_ORIGIN }}
RELEASE_STORAGE_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_R2_RELEASES_AK }}
RELEASE_STORAGE_BUCKET: ${{ secrets.CLOUDFLARE_R2_RELEASES_BUCKET }}
RELEASE_STORAGE_ENDPOINT: ${{ secrets.CLOUDFLARE_R2_RELEASES_URL }}
RELEASE_STORAGE_REGION: auto
RELEASE_STORAGE_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_R2_RELEASES_SK }}
RELEASE_VERSION_CANDIDATE: ${{ steps.exact.outputs.release_version }}
RELEASE_VERSION_MANUAL_OVERRIDE: "false"
run: pnpm exec tools-release reserve-version "${{ inputs.exact_name }}"
- name: Prepare immutable candidate identity
id: candidate
if: ${{ !inputs.promote }}
env:
RELEASE_CANDIDATE_SPEC_PATH: ${{ runner.temp }}/release-candidate/spec.json
run: |
set -euo pipefail
mkdir -p "$(dirname "$RELEASE_CANDIDATE_SPEC_PATH")"
jq -n \
--arg amrProfile "${{ inputs.amr_profile || 'test' }}" \
--arg channel "${{ inputs.exact_name }}" \
--arg closureMinShellVersion "${{ steps.exact.outputs.closure_min_shell_version }}" \
--arg commit "$BUILT_SHA" \
--arg releaseVersion "${{ steps.reservation.outputs.release_version }}" \
'{
amrProfile: $amrProfile,
channel: $channel,
closureMinShellVersion: $closureMinShellVersion,
commit: $commit,
macArm64SignMode: "notarized",
macX64SignMode: "notarized",
releaseVersion: $releaseVersion,
schemaVersion: 1,
targets: ["mac_arm64", "mac_x64", "win_x64"],
winX64SignMode: "unsigned"
}' > "$RELEASE_CANDIDATE_SPEC_PATH"
pnpm exec tools-release prepare-candidate
- name: Upload immutable candidate spec
if: ${{ !inputs.promote }}
uses: actions/upload-artifact@v7
with:
name: open-design-${{ inputs.exact_name }}-candidate-spec
path: ${{ runner.temp }}/release-candidate/spec.json
if-no-files-found: error
retention-days: 30
- name: Resolve release target identities
id: execution
uses: actions/github-script@v8
env:
APPLE_SIGNING_CERTIFICATE_BASE64: ${{ secrets.APPLE_SIGNING_CERTIFICATE_BASE64 }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
OD_UPDATE_METADATA_URL: ${{ inputs.release_public_origin != '' && inputs.release_public_origin || vars.CLOUDFLARE_R2_RELEASES_PUBLIC_ORIGIN }}/${{ inputs.exact_name }}/latest/metadata.json
RELEASE_ACTIVATE: ${{ inputs.promote && 'true' || 'false' }}
RELEASE_CHANNEL: ${{ inputs.exact_name }}
RELEASE_COMMIT: ${{ env.BUILT_SHA }}
RELEASE_MIN_SHELL_VERSION: ${{ steps.exact.outputs.closure_min_shell_version }}
RELEASE_PROFILE: ${{ inputs.amr_profile || 'test' }}
RELEASE_PUBLIC_ORIGIN: ${{ env.RELEASE_PUBLIC_ORIGIN }}
RELEASE_PUBLISH: ${{ inputs.publish && 'true' || 'false' }}
RELEASE_STORAGE_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_R2_RELEASES_AK }}
RELEASE_STORAGE_BUCKET: ${{ secrets.CLOUDFLARE_R2_RELEASES_BUCKET }}
RELEASE_STORAGE_ENDPOINT: ${{ secrets.CLOUDFLARE_R2_RELEASES_URL }}
RELEASE_STORAGE_REGION: auto
RELEASE_STORAGE_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_R2_RELEASES_SK }}
RELEASE_VERSION: ${{ steps.reservation.outputs.release_version || steps.exact.outputs.release_version }}
with:
script: |
const fs = require("fs");
const path = require("path");
const identityRoot = path.join(process.env.RUNNER_TEMP, "release-identities");
const profileRoot = path.join(process.env.RUNNER_TEMP, "release-identity-profiles");
fs.mkdirSync(identityRoot, { recursive: true });
fs.mkdirSync(profileRoot, { recursive: true });
const shellVersion = process.env.RELEASE_VERSION;
const channel = process.env.RELEASE_CHANNEL;
async function compileProfile(name, platform, target, args) {
const result = await exec.getExecOutput("pnpm", [
"exec", "tools-pack", platform, "identity",
"--dir", path.join(profileRoot, name),
"--cache-dir", path.join(profileRoot, `cache-${name}`),
...args,
"--portable",
"--release-version", shellVersion,
"--shell-version", shellVersion,
"--json",
]);
const profile = JSON.parse(result.stdout);
profile.target = target;
fs.writeFileSync(path.join(profileRoot, `${name}.json`), JSON.stringify(profile, null, 2));
return profile.profileDigest;
}
const macArm64Digest = await compileProfile("mac-arm64", "mac", "darwin-arm64", [
"--namespace", `release-${channel}`,
"--mac-compression", "normal", "--to", "dmg", "--sign-mode", "notarized",
]);
const macX64Digest = await compileProfile("mac-x64", "mac", "darwin-x64", [
"--namespace", `release-${channel}-x64`,
"--mac-compression", "normal", "--to", "all", "--sign-mode", "notarized",
]);
const winX64Digest = await compileProfile("win-x64", "win", "win32-x64", [
"--namespace", `release-${channel}-win`, "--to", "all", "--sign-mode", "unsigned",
]);
const identityInputPath = path.join(identityRoot, "input.json");
const identityPath = path.join(identityRoot, "resolved.json");
fs.writeFileSync(identityInputPath, JSON.stringify({
minShellVersion: process.env.RELEASE_MIN_SHELL_VERSION,
shellProfiles: { mac_arm64: macArm64Digest, mac_x64: macX64Digest, win_x64: winX64Digest },
}));
await exec.exec("pnpm", ["exec", "tools-release", "resolve-release-identities", "--input", identityInputPath, "--output", identityPath]);
const identities = JSON.parse(fs.readFileSync(identityPath, "utf8"));
core.setOutput("shared_closure_digest", identities.sharedClosureIdentityDigest);
for (const identity of identities.buildMatrix.include) {
core.setOutput(`${identity.target}_closure_digest`, identity.closure_identity_digest);
core.setOutput(`${identity.target}_shell_digest`, identity.shell_identity_digest);
}
- name: Probe mac_arm64 content proof
id: mac_arm64_proof
uses: ./.github/actions/hash-skip
env:
HASH_SKIP_EXTRA_CLOSURE: ${{ steps.execution.outputs.mac_arm64_closure_digest }}
HASH_SKIP_EXTRA_SHELL: ${{ steps.execution.outputs.mac_arm64_shell_digest }}
with:
hash_skip: release.smoke.mac
- name: Probe mac_x64 content proof
id: mac_x64_proof
uses: ./.github/actions/hash-skip
env:
HASH_SKIP_EXTRA_CLOSURE: ${{ steps.execution.outputs.mac_x64_closure_digest }}
HASH_SKIP_EXTRA_SHELL: ${{ steps.execution.outputs.mac_x64_shell_digest }}
with:
hash_skip: release.smoke.mac
- name: Probe win_x64 content proof
id: win_x64_proof
uses: ./.github/actions/hash-skip
env:
HASH_SKIP_EXTRA_CLOSURE: ${{ steps.execution.outputs.win_x64_closure_digest }}
HASH_SKIP_EXTRA_SHELL: ${{ steps.execution.outputs.win_x64_shell_digest }}
with:
hash_skip: release.smoke.win
- name: Select content execution topology
id: selection
uses: actions/github-script@v8
env:
MAC_ARM64_REUSE: ${{ steps.mac_arm64_proof.outputs.skip }}
MAC_X64_REUSE: ${{ steps.mac_x64_proof.outputs.skip }}
PROMOTE: ${{ inputs.promote }}
WIN_X64_REUSE: ${{ steps.win_x64_proof.outputs.skip }}
with:
script: |
const fs = require("fs");
const path = require("path");
const identities = JSON.parse(fs.readFileSync(path.join(process.env.RUNNER_TEMP, "release-identities", "resolved.json"), "utf8"));
const reusable = {
mac_arm64: process.env.PROMOTE === "true" && process.env.MAC_ARM64_REUSE === "true",
mac_x64: process.env.PROMOTE === "true" && process.env.MAC_X64_REUSE === "true",
win_x64: process.env.PROMOTE === "true" && process.env.WIN_X64_REUSE === "true",
};
const include = identities.buildMatrix.include.filter(({ target }) => !reusable[target]);
core.setOutput("build_matrix", JSON.stringify({ include }));
core.setOutput("build_count", String(include.length));
await core.summary.addHeading("Release content topology", 3).addTable([
[{ data: "Target", header: true }, { data: "Content", header: true }, { data: "Version projection", header: true }],
...identities.buildMatrix.include.map(({ target }) => [
target,
reusable[target] ? "reuse verified registry content" : "runner execution",
reusable[target] ? "metadata job" : "platform job",
]),
]).write();
- name: Materialize canonical shared Standalone Closure
uses: ./.github/actions/release/closure/shared
env:
RELEASE_COMMIT: ${{ env.BUILT_SHA }}
RELEASE_PUBLIC_ORIGIN: ${{ inputs.release_public_origin != '' && inputs.release_public_origin || vars.CLOUDFLARE_R2_RELEASES_PUBLIC_ORIGIN }}
RELEASE_STORAGE_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_R2_RELEASES_AK }}
RELEASE_STORAGE_BUCKET: ${{ secrets.CLOUDFLARE_R2_RELEASES_BUCKET }}
RELEASE_STORAGE_ENDPOINT: ${{ secrets.CLOUDFLARE_R2_RELEASES_URL }}
RELEASE_STORAGE_REGION: auto
RELEASE_STORAGE_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_R2_RELEASES_SK }}
RELEASE_VERSION_LOCK_KEY: ${{ steps.reservation.outputs.version_lock_key }}
RELEASE_VERSION_LOCK_REQUIRED: "true"
with:
channel: ${{ inputs.exact_name }}
handoff-enabled: ${{ steps.selection.outputs.build_count != '0' || !inputs.publish || !inputs.promote }}
identity-digest: ${{ steps.execution.outputs.shared_closure_digest }}
min-shell-version: ${{ steps.exact.outputs.closure_min_shell_version }}
publish-enabled: "true"
release-version: ${{ steps.reservation.outputs.release_version || steps.exact.outputs.release_version }}
- name: Project verified mac_arm64 content
if: ${{ steps.mac_arm64_proof.outputs.skip == 'true' && inputs.promote }}
uses: ./.github/actions/release/platform/project
env: &projection_env
GITHUB_SHA: ${{ env.BUILT_SHA }}
RELEASE_COMMIT: ${{ env.BUILT_SHA }}
RELEASE_STORAGE_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_R2_RELEASES_AK }}
RELEASE_STORAGE_BUCKET: ${{ secrets.CLOUDFLARE_R2_RELEASES_BUCKET }}
RELEASE_STORAGE_ENDPOINT: ${{ secrets.CLOUDFLARE_R2_RELEASES_URL }}
RELEASE_STORAGE_REGION: auto
RELEASE_STORAGE_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_R2_RELEASES_SK }}
RELEASE_VERSION_LOCK_KEY: ${{ steps.reservation.outputs.version_lock_key }}
RELEASE_VERSION_LOCK_REQUIRED: "true"
with:
artifact-id: mac-arm64
channel: ${{ inputs.exact_name }}
handoff-enabled: ${{ steps.selection.outputs.build_count != '0' || !inputs.publish || !inputs.promote }}
closure-identity-digest: ${{ steps.execution.outputs.mac_arm64_closure_digest }}
platform: darwin-arm64
public-origin: ${{ env.RELEASE_PUBLIC_ORIGIN }}
release-version: ${{ steps.reservation.outputs.release_version || steps.exact.outputs.release_version }}
shell-identity-digest: ${{ steps.execution.outputs.mac_arm64_shell_digest }}
shell-plan-path: ${{ runner.temp }}/release-identity-profiles/mac-arm64.json
target: mac_arm64
- name: Project verified mac_x64 content
if: ${{ steps.mac_x64_proof.outputs.skip == 'true' && inputs.promote }}
uses: ./.github/actions/release/platform/project
env: *projection_env
with:
artifact-id: mac-x64
channel: ${{ inputs.exact_name }}
handoff-enabled: ${{ steps.selection.outputs.build_count != '0' || !inputs.publish || !inputs.promote }}
closure-identity-digest: ${{ steps.execution.outputs.mac_x64_closure_digest }}
platform: darwin-x64
public-origin: ${{ env.RELEASE_PUBLIC_ORIGIN }}
release-version: ${{ steps.reservation.outputs.release_version || steps.exact.outputs.release_version }}
shell-identity-digest: ${{ steps.execution.outputs.mac_x64_shell_digest }}
shell-plan-path: ${{ runner.temp }}/release-identity-profiles/mac-x64.json
target: mac_x64
- name: Project verified win_x64 content
if: ${{ steps.win_x64_proof.outputs.skip == 'true' && inputs.promote }}
uses: ./.github/actions/release/platform/project
env: *projection_env
with:
artifact-id: win-x64
channel: ${{ inputs.exact_name }}
handoff-enabled: ${{ steps.selection.outputs.build_count != '0' || !inputs.publish || !inputs.promote }}
closure-identity-digest: ${{ steps.execution.outputs.win_x64_closure_digest }}
platform: win32-x64
public-origin: ${{ env.RELEASE_PUBLIC_ORIGIN }}
release-version: ${{ steps.reservation.outputs.release_version || steps.exact.outputs.release_version }}
shell-identity-digest: ${{ steps.execution.outputs.win_x64_shell_digest }}
shell-plan-path: ${{ runner.temp }}/release-identity-profiles/win-x64.json
target: win_x64
- name: Stage fully reused exact content
id: hot_stage
if: ${{ steps.selection.outputs.build_count == '0' && inputs.publish && inputs.promote }}
uses: ./.github/actions/release/exact/stage
env:
BASE_VERSION: ${{ steps.exact.outputs.base_version }}
RELEASE_ACTIVATE_LATEST: "false"
RELEASE_CHANNEL: ${{ inputs.exact_name }}
RELEASE_CLOSURE_DISTRIBUTION_MANIFEST_PATH: ${{ runner.temp }}/release-closure/closure-distribution.json
RELEASE_CLOSURE_DISTRIBUTION_REQUIRED: "true"
RELEASE_INSTALLATION_VERSION_MIN_BETA: ${{ vars.RELEASE_LAUNCHER_VERSION_MIN_BETA }}
RELEASE_INSTALLATION_VERSION_MIN_STABLE: ${{ vars.RELEASE_LAUNCHER_VERSION_MIN_STABLE }}
RELEASE_INSTALLATION_VERSION_MIN_URL_BETA: ${{ vars.RELEASE_LAUNCHER_VERSION_MIN_URL_BETA }}
RELEASE_INSTALLATION_VERSION_MIN_URL_STABLE: ${{ vars.RELEASE_LAUNCHER_VERSION_MIN_URL_STABLE }}
RELEASE_LATEST_CAS_REQUIRED: "true"
RELEASE_LEGACY_INSTALLATION_MIGRATION_REQUIRED: "true"
RELEASE_MANIFEST_DIR: ${{ runner.temp }}/release-platform-manifests
RELEASE_PUBLIC_ORIGIN: ${{ env.RELEASE_PUBLIC_ORIGIN }}
RELEASE_SHELL_REQUIRED: "true"
RELEASE_STORAGE_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_R2_RELEASES_AK }}
RELEASE_STORAGE_BUCKET: ${{ secrets.CLOUDFLARE_R2_RELEASES_BUCKET }}
RELEASE_STORAGE_ENDPOINT: ${{ secrets.CLOUDFLARE_R2_RELEASES_URL }}
RELEASE_STORAGE_REGION: auto
RELEASE_STORAGE_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_R2_RELEASES_SK }}
RELEASE_VERSION: ${{ steps.reservation.outputs.release_version || steps.exact.outputs.release_version }}
RELEASE_VERSION_LOCK_KEY: ${{ steps.reservation.outputs.version_lock_key }}
RELEASE_VERSION_LOCK_REQUIRED: "true"
STATE_SOURCE: ${{ steps.reservation.outputs.state_source || steps.exact.outputs.state_source }}
with:
exact-name: ${{ inputs.exact_name }}
release-version: ${{ steps.reservation.outputs.release_version || steps.exact.outputs.release_version }}
- name: Plan fully reused public acceptance
id: hot_acceptance
if: ${{ steps.hot_stage.outcome == 'success' }}
uses: ./.github/actions/release/public-acceptance/plan
with:
acceptance-matrix: '{"include":[{"artifact_dir":"dmg","os":"mac","runner":"macos-14","target":"mac_arm64"},{"artifact_dir":"dmg","os":"mac","runner":"macos-15-intel","target":"mac_x64"},{"artifact_dir":"builder","os":"win","runner":"windows-latest","target":"win_x64"}]}'
content-identities: ${{ format('{{"mac_arm64":{{"closure":"{0}","shell":"{1}"}},"mac_x64":{{"closure":"{2}","shell":"{3}"}},"win_x64":{{"closure":"{4}","shell":"{5}"}}}}', steps.execution.outputs.mac_arm64_closure_digest, steps.execution.outputs.mac_arm64_shell_digest, steps.execution.outputs.mac_x64_closure_digest, steps.execution.outputs.mac_x64_shell_digest, steps.execution.outputs.win_x64_closure_digest, steps.execution.outputs.win_x64_shell_digest) }}
- name: Activate fully reused exact release
id: hot_activate
if: ${{ steps.hot_acceptance.outputs.acceptance_count == '0' }}
uses: ./.github/actions/release/public-acceptance/activate
env:
RELEASE_STORAGE_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_R2_RELEASES_AK }}
RELEASE_STORAGE_BUCKET: ${{ secrets.CLOUDFLARE_R2_RELEASES_BUCKET }}
RELEASE_STORAGE_ENDPOINT: ${{ secrets.CLOUDFLARE_R2_RELEASES_URL }}
RELEASE_STORAGE_REGION: auto
RELEASE_STORAGE_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_R2_RELEASES_SK }}
with:
channel: ${{ inputs.exact_name }}
commit: ${{ env.BUILT_SHA }}
metadata-url: ${{ steps.hot_stage.outputs.version_metadata_url }}
public-origin: ${{ env.RELEASE_PUBLIC_ORIGIN }}
release-version: ${{ steps.reservation.outputs.release_version || steps.exact.outputs.release_version }}
reuse-json: ${{ steps.hot_acceptance.outputs.reuse_json }}
build:
name: Build exact ${{ matrix.target }}
needs: metadata
if: ${{ needs.metadata.outputs.build_count != '0' }}
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.metadata.outputs.build_matrix) }}
runs-on: ${{ matrix.runner }}
env:
CLOUDFLARE_R2_RELEASES_AK: ${{ secrets.CLOUDFLARE_R2_RELEASES_AK }}
CLOUDFLARE_R2_RELEASES_BUCKET: ${{ secrets.CLOUDFLARE_R2_RELEASES_BUCKET }}
CLOUDFLARE_R2_RELEASES_PUBLIC_ORIGIN: ${{ vars.CLOUDFLARE_R2_RELEASES_PUBLIC_ORIGIN }}
CLOUDFLARE_R2_RELEASES_SK: ${{ secrets.CLOUDFLARE_R2_RELEASES_SK }}
CLOUDFLARE_R2_RELEASES_URL: ${{ secrets.CLOUDFLARE_R2_RELEASES_URL }}
CLOSURE_MIN_SHELL_VERSION: ${{ needs.metadata.outputs.closure_min_shell_version }}
OPEN_DESIGN_POSTINSTALL_LEVEL: release-smoke
OD_UPDATE_METADATA_URL: ${{ inputs.release_public_origin != '' && inputs.release_public_origin || vars.CLOUDFLARE_R2_RELEASES_PUBLIC_ORIGIN }}/${{ inputs.exact_name }}/latest/metadata.json
RELEASE_COMMIT: ${{ needs.metadata.outputs.commit }}
RELEASE_CANDIDATE_ID: ${{ needs.metadata.outputs.candidate_id }}
RELEASE_VERSION_LOCK_KEY: ${{ needs.metadata.outputs.version_lock_key }}
RELEASE_VERSION_LOCK_REQUIRED: "true"
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
with:
ref: ${{ needs.metadata.outputs.commit }}
- name: Resolve and materialize exact macOS ${{ matrix.target }}
id: mac_prepare
if: ${{ matrix.target != 'win_x64' }}
uses: ./.github/actions/release/platform/mac/exact
env:
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_SIGNING_CERTIFICATE_BASE64: ${{ secrets.APPLE_SIGNING_CERTIFICATE_BASE64 }}
APPLE_SIGNING_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_SIGNING_CERTIFICATE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
with:
channel: ${{ inputs.exact_name }}
amr-profile: ${{ inputs.amr_profile || 'test' }}
arch: ${{ matrix.target == 'mac_arm64' && 'arm64' || 'x64' }}
artifact-id: ${{ matrix.target == 'mac_arm64' && 'mac-arm64' || 'mac-x64' }}
build-target: ${{ matrix.target == 'mac_arm64' && 'dmg' || 'all' }}
candidate-id: ${{ needs.metadata.outputs.candidate_id }}
closure-identity-digest: ${{ matrix.closure_identity_digest }}
closure-shared-identity-digest: ${{ needs.metadata.outputs.closure_shared_identity_digest }}
closure-version: ${{ needs.metadata.outputs.closure_version }}
candidate-enabled: ${{ !inputs.promote }}
legacy-migration-enabled: ${{ matrix.target == 'mac_arm64' && inputs.exact_name == 'beta' && 'true' || 'false' }}
namespace: ${{ matrix.target == 'mac_arm64' && format('release-{0}', inputs.exact_name) || format('release-{0}-x64', inputs.exact_name) }}
platform: ${{ matrix.target == 'mac_arm64' && 'darwin-arm64' || 'darwin-x64' }}
phase: prepare
publish: "true"
release-public-origin: ${{ inputs.release_public_origin }}
release-version: ${{ needs.metadata.outputs.release_version }}
shell-smoke-matrix: ${{ matrix.target == 'mac_arm64' && 'mac-shell-v3' || 'mac-shell-v2' }}
shell-identity-digest: ${{ matrix.shell_identity_digest }}
shell-version: ${{ needs.metadata.outputs.shell_version }}
sign-mode: notarized
smoke-lanes-on-miss: ${{ matrix.target == 'mac_x64' && 'shell,standalone' || '' }}
smoke-mode: full
target: ${{ matrix.target }}
update-metadata-url: ""
update-target-version: ""
- name: Smoke and publish exact macOS ${{ matrix.target }}
id: mac_finish
if: ${{ matrix.target != 'win_x64' }}
uses: ./.github/actions/release/platform/mac/exact
with:
channel: ${{ inputs.exact_name }}
amr-profile: ${{ inputs.amr_profile || 'test' }}
arch: ${{ matrix.target == 'mac_arm64' && 'arm64' || 'x64' }}
artifact-id: ${{ matrix.target == 'mac_arm64' && 'mac-arm64' || 'mac-x64' }}
build-target: ${{ matrix.target == 'mac_arm64' && 'dmg' || 'all' }}
candidate-id: ${{ needs.metadata.outputs.candidate_id }}
closure-identity-digest: ${{ matrix.closure_identity_digest }}
closure-shared-identity-digest: ${{ needs.metadata.outputs.closure_shared_identity_digest }}
closure-version: ${{ needs.metadata.outputs.closure_version }}
candidate-enabled: ${{ !inputs.promote }}
legacy-migration-enabled: ${{ matrix.target == 'mac_arm64' && inputs.exact_name == 'beta' && 'true' || 'false' }}
namespace: ${{ matrix.target == 'mac_arm64' && format('release-{0}', inputs.exact_name) || format('release-{0}-x64', inputs.exact_name) }}
phase: finish
platform: ${{ matrix.target == 'mac_arm64' && 'darwin-arm64' || 'darwin-x64' }}
publish: "true"
release-public-origin: ${{ inputs.release_public_origin }}
release-version: ${{ needs.metadata.outputs.release_version }}
shell-smoke-matrix: ${{ matrix.target == 'mac_arm64' && 'mac-shell-v3' || 'mac-shell-v2' }}
shell-identity-digest: ${{ matrix.shell_identity_digest }}
shell-version: ${{ needs.metadata.outputs.shell_version }}
sign-mode: notarized
smoke-lanes-on-miss: ${{ matrix.target == 'mac_x64' && 'shell,standalone' || '' }}
smoke-mode: full
target: ${{ matrix.target }}
update-metadata-url: ""
update-target-version: ""
- name: Resolve and materialize exact win_x64
id: win_prepare
if: ${{ matrix.target == 'win_x64' }}
uses: ./.github/actions/release/platform/win/exact
with:
channel: ${{ inputs.exact_name }}
candidate-id: ${{ needs.metadata.outputs.candidate_id }}
candidate-enabled: ${{ !inputs.promote }}
closure-identity-digest: ${{ matrix.closure_identity_digest }}
closure-shared-identity-digest: ${{ needs.metadata.outputs.closure_shared_identity_digest }}
release-version: ${{ needs.metadata.outputs.release_version }}
closure-version: ${{ needs.metadata.outputs.closure_version }}
shell-version: ${{ needs.metadata.outputs.shell_version }}
shell-identity-digest: ${{ matrix.shell_identity_digest }}
amr-profile: ${{ inputs.amr_profile || 'test' }}
phase: prepare
publish: "true"
release-public-origin: ${{ inputs.release_public_origin }}
sign-mode: unsigned
smoke-mode: full
build-target: all
update-metadata-url: ""
update-target-version: ""
- name: Smoke and publish exact win_x64
id: win_finish
if: ${{ matrix.target == 'win_x64' }}
uses: ./.github/actions/release/platform/win/exact
with:
channel: ${{ inputs.exact_name }}
candidate-id: ${{ needs.metadata.outputs.candidate_id }}
candidate-enabled: ${{ !inputs.promote }}
closure-identity-digest: ${{ matrix.closure_identity_digest }}
closure-shared-identity-digest: ${{ needs.metadata.outputs.closure_shared_identity_digest }}
release-version: ${{ needs.metadata.outputs.release_version }}
closure-version: ${{ needs.metadata.outputs.closure_version }}
shell-version: ${{ needs.metadata.outputs.shell_version }}
shell-identity-digest: ${{ matrix.shell_identity_digest }}
amr-profile: ${{ inputs.amr_profile || 'test' }}
phase: finish
publish: "true"
release-public-origin: ${{ inputs.release_public_origin }}
sign-mode: unsigned
smoke-mode: full
build-target: all
update-metadata-url: ""
update-target-version: ""
- name: Write ${{ matrix.target }} build result
if: ${{ always() }}
uses: actions/github-script@v8
env:
RESULT_SMOKE: ${{ steps.mac_finish.outputs.smoke_result || steps.win_finish.outputs.smoke_result }}
RESULT_TARGET: ${{ matrix.target }}
RESULT_URL: ${{ steps.mac_finish.outputs.dmg_url || steps.win_finish.outputs.win_x64_url || steps.mac_prepare.outputs.candidate_primary_url || steps.win_prepare.outputs.candidate_primary_url }}
RESULT_CANDIDATE_MANIFEST_URL: ${{ steps.mac_prepare.outputs.candidate_manifest_url || steps.win_prepare.outputs.candidate_manifest_url }}
with:
script: |
const fs = require("fs");
const path = require("path");
const root = path.join(process.env.RUNNER_TEMP, "release-platform-result");
fs.mkdirSync(root, { recursive: true });
fs.writeFileSync(path.join(root, `${process.env.RESULT_TARGET}.json`), JSON.stringify({
candidateManifestUrl: process.env.RESULT_CANDIDATE_MANIFEST_URL || "",
smokeResult: process.env.RESULT_SMOKE || "",
target: process.env.RESULT_TARGET,
url: process.env.RESULT_URL || "",
}));
- name: Upload ${{ matrix.target }} build result
if: ${{ always() }}
uses: actions/upload-artifact@v7
with:
name: open-design-${{ inputs.exact_name }}-${{ matrix.target }}-build-result
path: ${{ runner.temp }}/release-platform-result/${{ matrix.target }}.json
if-no-files-found: error
candidate:
name: Seal exact candidate
needs: [metadata, build, stage]
if: ${{ !cancelled() && !inputs.promote && needs.metadata.result == 'success' && (needs.build.result == 'success' || needs.build.result == 'skipped') && needs.stage.result == 'success' }}
runs-on: ubuntu-latest
outputs:
manifest_url: ${{ steps.finalize.outputs.candidate_manifest_url }}
mac_arm64_url: ${{ steps.links.outputs.mac_arm64_url }}
mac_x64_url: ${{ steps.links.outputs.mac_x64_url }}
win_x64_url: ${{ steps.links.outputs.win_x64_url }}
steps:
- name: Checkout candidate finalizer
uses: actions/checkout@v6.0.2
with:
ref: ${{ needs.metadata.outputs.commit }}
- name: Setup release toolchain
uses: ./.github/actions/release/platform/setup
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Download candidate spec
uses: actions/download-artifact@v8
with:
name: open-design-${{ inputs.exact_name }}-candidate-spec
path: ${{ runner.temp }}/release-candidate
- name: Download platform results
uses: actions/download-artifact@v8
with:
pattern: open-design-${{ inputs.exact_name }}-*-build-result
path: ${{ runner.temp }}/release-platform-results
merge-multiple: true
- name: Prepare candidate target URLs and links
id: links
run: |
set -euo pipefail
jq -s '[.[].candidateManifestUrl | select(length > 0)]' "$RUNNER_TEMP"/release-platform-results/*.json > "$RUNNER_TEMP/release-candidate/target-urls.json"
for target in mac_arm64 mac_x64 win_x64; do
url="$(jq -r --arg target "$target" '. | select(.target == $target) | .url' "$RUNNER_TEMP"/release-platform-results/*.json)"
echo "${target}_url=$url" >> "$GITHUB_OUTPUT"
done
- name: Seal candidate manifest
id: finalize
env:
RELEASE_CANDIDATE_SPEC_PATH: ${{ runner.temp }}/release-candidate/spec.json
RELEASE_CANDIDATE_TARGET_URLS_PATH: ${{ runner.temp }}/release-candidate/target-urls.json
RELEASE_PUBLIC_ORIGIN: ${{ inputs.release_public_origin != '' && inputs.release_public_origin || vars.CLOUDFLARE_R2_RELEASES_PUBLIC_ORIGIN }}
RELEASE_STORAGE_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_R2_RELEASES_AK }}
RELEASE_STORAGE_BUCKET: ${{ secrets.CLOUDFLARE_R2_RELEASES_BUCKET }}
RELEASE_STORAGE_ENDPOINT: ${{ secrets.CLOUDFLARE_R2_RELEASES_URL }}
RELEASE_STORAGE_REGION: auto
RELEASE_STORAGE_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_R2_RELEASES_SK }}
RELEASE_VERSION_METADATA_URL: ${{ needs.stage.outputs.version_metadata_url }}
run: pnpm exec tools-release finalize-candidate
stage:
name: Stage exact metadata
needs:
- metadata
- build
if: >-
${{
always() &&
!cancelled() &&
needs.metadata.result == 'success' &&
needs.metadata.outputs.hot_staged != 'true' &&
(needs.build.result == 'success' || needs.build.result == 'skipped')
}}
runs-on: ubuntu-latest
outputs:
acceptance_latest_url: ${{ steps.staged.outputs.acceptance_latest_url }}
release_state: ${{ steps.staged.outputs.release_state }}
version_metadata_url: ${{ steps.staged.outputs.version_metadata_url }}
mac_arm64_url: ${{ steps.staged.outputs.mac_arm64_url }}
win_x64_url: ${{ steps.staged.outputs.win_x64_url }}
mac_x64_url: ${{ steps.staged.outputs.mac_x64_url }}
mac_arm64_smoke_result: ${{ steps.platform_results.outputs.mac_arm64_smoke_result }}
mac_x64_smoke_result: ${{ steps.platform_results.outputs.mac_x64_smoke_result }}
win_x64_smoke_result: ${{ steps.platform_results.outputs.win_x64_smoke_result }}
env:
GITHUB_SHA: ${{ needs.metadata.outputs.commit }}
OPEN_DESIGN_POSTINSTALL_LEVEL: release-prepare
RELEASE_COMMIT: ${{ needs.metadata.outputs.commit }}
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
with:
ref: ${{ needs.metadata.outputs.commit }}
- name: Download mac_arm64 publish manifest
uses: actions/download-artifact@v8
with:
name: open-design-${{ inputs.exact_name }}-mac-arm64-publish-manifest
path: ${{ runner.temp }}/release-platform-manifests
- name: Download mac_x64 publish manifest
uses: actions/download-artifact@v8
with:
name: open-design-${{ inputs.exact_name }}-mac-x64-publish-manifest
path: ${{ runner.temp }}/release-platform-manifests
- name: Download win_x64 publish manifest
uses: actions/download-artifact@v8
with:
name: open-design-${{ inputs.exact_name }}-win-x64-publish-manifest
path: ${{ runner.temp }}/release-platform-manifests
- name: Download shared Standalone contract
uses: actions/download-artifact@v8
with:
name: open-design-${{ inputs.exact_name }}-closure-shared
path: ${{ runner.temp }}/release-closure/shared
- name: Download mac_arm64 Standalone contribution
uses: actions/download-artifact@v8
with:
name: open-design-${{ inputs.exact_name }}-closure-darwin-arm64
path: ${{ runner.temp }}/release-closure/targets/darwin-arm64
- name: Download mac_x64 Standalone contribution
uses: actions/download-artifact@v8
with:
name: open-design-${{ inputs.exact_name }}-closure-darwin-x64
path: ${{ runner.temp }}/release-closure/targets/darwin-x64
- name: Download win_x64 Standalone contribution
uses: actions/download-artifact@v8
with:
name: open-design-${{ inputs.exact_name }}-closure-win32-x64
path: ${{ runner.temp }}/release-closure/targets/win32-x64
- name: Setup pnpm
uses: pnpm/action-setup@v5
with:
version: 10.33.2
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 24.18.0
cache: pnpm
cache-dependency-path: pnpm-lock.yaml
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Download platform build results
uses: actions/download-artifact@v8
with:
pattern: open-design-${{ inputs.exact_name }}-*-build-result
merge-multiple: true
path: ${{ runner.temp }}/release-platform-results
- name: Read platform build results
id: platform_results
uses: actions/github-script@v8
with:
script: |
const fs = require("fs");
const path = require("path");
const root = path.join(process.env.RUNNER_TEMP, "release-platform-results");
for (const target of ["mac_arm64", "mac_x64", "win_x64"]) {
const file = path.join(root, `${target}.json`);
if (!fs.existsSync(file)) continue;
const result = JSON.parse(fs.readFileSync(file, "utf8"));
core.setOutput(`${target}_smoke_result`, String(result.smokeResult || ""));
}
- name: Stage exact release content
id: staged
uses: ./.github/actions/release/exact/stage
with:
exact-name: ${{ inputs.exact_name }}
release-version: ${{ needs.metadata.outputs.release_version }}
env:
BASE_VERSION: ${{ needs.metadata.outputs.base_version }}
RELEASE_ACTIVATE_LATEST: "false"
RELEASE_CHANNEL: ${{ inputs.exact_name }}
RELEASE_CLOSURE_DISTRIBUTION_MANIFEST_PATH: ${{ runner.temp }}/release-closure/closure-distribution.json
RELEASE_CLOSURE_DISTRIBUTION_REQUIRED: "true"
RELEASE_SHELL_REQUIRED: "true"
RELEASE_INSTALLATION_VERSION_MIN_BETA: ${{ vars.RELEASE_LAUNCHER_VERSION_MIN_BETA }}
RELEASE_INSTALLATION_VERSION_MIN_STABLE: ${{ vars.RELEASE_LAUNCHER_VERSION_MIN_STABLE }}
RELEASE_INSTALLATION_VERSION_MIN_URL_BETA: ${{ vars.RELEASE_LAUNCHER_VERSION_MIN_URL_BETA }}
RELEASE_INSTALLATION_VERSION_MIN_URL_STABLE: ${{ vars.RELEASE_LAUNCHER_VERSION_MIN_URL_STABLE }}
RELEASE_LEGACY_INSTALLATION_MIGRATION_REQUIRED: ${{ inputs.exact_name == 'beta' && 'true' || 'false' }}
RELEASE_LATEST_CAS_REQUIRED: "true"
RELEASE_VERSION_LOCK_KEY: ${{ needs.metadata.outputs.version_lock_key }}
RELEASE_VERSION_LOCK_REQUIRED: "true"
RELEASE_MANIFEST_DIR: ${{ runner.temp }}/release-platform-manifests
RELEASE_PUBLIC_ORIGIN: ${{ inputs.release_public_origin != '' && inputs.release_public_origin || vars.CLOUDFLARE_R2_RELEASES_PUBLIC_ORIGIN }}
RELEASE_STORAGE_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_R2_RELEASES_AK }}
RELEASE_STORAGE_BUCKET: ${{ secrets.CLOUDFLARE_R2_RELEASES_BUCKET }}
RELEASE_STORAGE_ENDPOINT: ${{ secrets.CLOUDFLARE_R2_RELEASES_URL }}
RELEASE_STORAGE_REGION: auto
RELEASE_STORAGE_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_R2_RELEASES_SK }}
RELEASE_VERSION: ${{ needs.metadata.outputs.release_version }}
STATE_SOURCE: ${{ needs.metadata.outputs.state_source }}
accept:
name: Accept and activate exact release
needs:
- metadata
- stage
if: >-
${{
!cancelled() &&
inputs.publish &&
inputs.promote &&
needs.metadata.result == 'success' &&
needs.metadata.outputs.hot_activated != 'true' &&
(needs.stage.result == 'success' || needs.metadata.outputs.hot_staged == 'true') &&
(needs.stage.outputs.release_state == 'complete' || needs.metadata.outputs.hot_release_state == 'complete')
}}
uses: ./.github/workflows/distribution-exact-accept.yml
secrets: inherit
with:
commit: ${{ needs.metadata.outputs.commit }}
content_identities: ${{ format('{{"mac_arm64":{{"closure":"{0}","shell":"{1}"}},"mac_x64":{{"closure":"{2}","shell":"{3}"}},"win_x64":{{"closure":"{4}","shell":"{5}"}}}}', needs.metadata.outputs.mac_arm64_closure_identity_digest, needs.metadata.outputs.mac_arm64_shell_identity_digest, needs.metadata.outputs.mac_x64_closure_identity_digest, needs.metadata.outputs.mac_x64_shell_identity_digest, needs.metadata.outputs.win_x64_closure_identity_digest, needs.metadata.outputs.win_x64_shell_identity_digest) }}
exact_name: ${{ inputs.exact_name }}
metadata_url: ${{ needs.metadata.outputs.hot_version_metadata_url || needs.stage.outputs.version_metadata_url }}
mutable_metadata_url: ${{ needs.metadata.outputs.hot_acceptance_latest_url || needs.stage.outputs.acceptance_latest_url }}
public_origin: ${{ inputs.release_public_origin != '' && inputs.release_public_origin || vars.CLOUDFLARE_R2_RELEASES_PUBLIC_ORIGIN }}
release_version: ${{ needs.metadata.outputs.release_version }}
notify:
name: Report exact release
needs:
- metadata
- build
- candidate
- stage
- accept
if: ${{ always() && !cancelled() }}
uses: ./.github/workflows/release-notify.yml
with:
activation_completed_at: ${{ needs.metadata.outputs.hot_activation_completed_at || needs.accept.outputs.activation_completed_at }}
actor: ${{ github.actor }}
branch: ${{ needs.metadata.outputs.branch }}
channel: ${{ inputs.exact_name }}
commit: ${{ needs.metadata.outputs.commit }}
event_name: ${{ github.event_name }}
mac_arm64_smoke: ${{ needs.metadata.outputs.hot_staged == 'true' && 'success' || needs.stage.outputs.mac_arm64_smoke_result }}
mac_arm64_url: ${{ !inputs.publish && needs.candidate.outputs.mac_arm64_url || needs.metadata.outputs.hot_mac_arm64_url || needs.stage.outputs.mac_arm64_url }}
mac_x64_smoke: ${{ needs.metadata.outputs.hot_staged == 'true' && 'success' || needs.stage.outputs.mac_x64_smoke_result }}
mac_x64_url: ${{ !inputs.publish && needs.candidate.outputs.mac_x64_url || needs.metadata.outputs.hot_mac_x64_url || needs.stage.outputs.mac_x64_url }}
metadata_url: ${{ !inputs.publish && needs.candidate.outputs.manifest_url || needs.metadata.outputs.hot_version_metadata_url || needs.stage.outputs.version_metadata_url }}
notification_stream: ${{ inputs.notification_stream || 'release' }}
previous_commit: ${{ needs.metadata.outputs.previous_commit }}
release_mode: ${{ inputs.publish && (inputs.promote && 'promote' || 'publish') || 'candidate' }}
release_result: ${{ needs.metadata.outputs.hot_activated == 'true' && 'success' || needs.accept.result != 'skipped' && needs.accept.result || needs.stage.result != 'skipped' && needs.stage.result || needs.build.result }}
release_state: ${{ needs.metadata.outputs.hot_activated == 'true' && 'complete' || needs.accept.result == 'success' && 'complete' || needs.metadata.outputs.hot_release_state || needs.stage.outputs.release_state || (needs.build.result == 'success' && 'partial' || 'failed') }}
run_attempt: ${{ github.run_attempt }}
run_number: ${{ github.run_number }}
triggering_actor: ${{ github.triggering_actor }}
version: ${{ needs.metadata.outputs.release_version }}
win_x64_smoke: ${{ needs.metadata.outputs.hot_staged == 'true' && 'success' || needs.stage.outputs.win_x64_smoke_result }}
win_x64_url: ${{ !inputs.publish && needs.candidate.outputs.win_x64_url || needs.metadata.outputs.hot_win_x64_url || needs.stage.outputs.win_x64_url }}
workflow_name: ${{ github.workflow }}
secrets:
bot: ${{ secrets.RELEASE_FEISHU_BETA }}