mirror of
https://github.com/nexu-io/open-design.git
synced 2026-06-01 03:14:35 +07:00
* chore: bump vela cli to 0.0.4-test.0 * chore: refresh lockfile for vela cli 0.0.4-test.0 * chore(nix): refresh pnpm deps hash * fix: materialize electron before mac release checks * fix: rebuild electron when mac framework links are invalid * revert: drop release workflow experiments * chore(nix): refresh pnpm deps hash * fix: stop blocking beta mac release on electron symlink preflight * fix: stop using custom electron dist for beta mac packaging * fix: guard oversized chat images and opencode overflow * chore: bump vela cli to 0.0.4 * chore(nix): refresh pnpm deps hash * fix(daemon): surface prompt-image stat failures instead of dropping them resolveSafePromptImagePaths only swallowed unresolvable path input; once a path was confirmed inside UPLOAD_DIR and existed, a statSync failure (EACCES/EPERM, a file vanishing mid-run) silently dropped the image and let the run continue without that prompt context. Since this helper is now also the 1 MB enforcement point, that turned an infra/validation failure into a 'successful' run with missing required context. Collect those into a new failedImages bucket and fail the run with INTERNAL_ERROR at the call site, mirroring the oversized-image guard. Add a unit test covering statSync throwing. --------- Co-authored-by: open-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com> Co-authored-by: lefarcen <935902669@qq.com>
859 lines
35 KiB
YAML
859 lines
35 KiB
YAML
name: release-beta
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
enable_mac:
|
|
description: "Build and publish mac arm64 beta artifacts."
|
|
required: true
|
|
type: boolean
|
|
default: true
|
|
enable_win:
|
|
description: "Build and publish Windows x64 beta artifacts."
|
|
required: true
|
|
type: boolean
|
|
default: true
|
|
enable_mac_intel:
|
|
description: "Build and publish macOS Intel x64 (unsigned) beta artifacts."
|
|
required: true
|
|
type: boolean
|
|
default: false
|
|
enable_linux:
|
|
description: "Build and publish Linux x64 AppImage/checksum to R2 only; no updater feed is published yet."
|
|
required: true
|
|
type: boolean
|
|
default: false
|
|
|
|
permissions:
|
|
actions: write
|
|
contents: read
|
|
|
|
concurrency:
|
|
group: open-design-release-beta
|
|
cancel-in-progress: false
|
|
|
|
env:
|
|
OPEN_DESIGN_TELEMETRY_RELAY_URL: ${{ vars.OPEN_DESIGN_TELEMETRY_RELAY_URL }}
|
|
# PostHog product-analytics ingest. Both vars must be defined as
|
|
# repository/organization secrets/vars for official builds to ship with
|
|
# analytics enabled. PR builds and forks run without these — the daemon's
|
|
# /api/analytics/config short-circuits to enabled=false in that case and
|
|
# no events leave the user's machine.
|
|
POSTHOG_KEY: ${{ secrets.POSTHOG_KEY }}
|
|
POSTHOG_HOST: ${{ vars.POSTHOG_HOST }}
|
|
# PostHog Error tracking sourcemap upload. Personal API key (phx_...) and
|
|
# project ID let tools-pack's web-sourcemaps step ship browser sourcemaps
|
|
# to PostHog after `next build` and before the .map files are stripped
|
|
# from the packaged bundle. Missing in PR/fork builds → upload is skipped
|
|
# and the helper still strips .map to keep source out of the installer.
|
|
POSTHOG_CLI_API_KEY: ${{ secrets.POSTHOG_CLI_API_KEY }}
|
|
POSTHOG_CLI_PROJECT_ID: ${{ vars.POSTHOG_CLI_PROJECT_ID }}
|
|
|
|
jobs:
|
|
metadata:
|
|
name: Prepare beta metadata
|
|
if: github.repository == 'nexu-io/open-design'
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
OPEN_DESIGN_BETA_METADATA_URL: ${{ vars.CLOUDFLARE_R2_RELEASES_PUBLIC_ORIGIN }}/beta/latest/metadata.json
|
|
outputs:
|
|
asset_version_suffix: ${{ steps.beta.outputs.asset_version_suffix }}
|
|
base_version: ${{ steps.beta.outputs.base_version }}
|
|
beta_version: ${{ steps.beta.outputs.beta_version }}
|
|
branch: ${{ steps.beta.outputs.branch }}
|
|
commit: ${{ steps.beta.outputs.commit }}
|
|
release_name: ${{ steps.beta.outputs.release_name }}
|
|
state_source: ${{ steps.beta.outputs.state_source }}
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v6.0.2
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Setup Node.js
|
|
uses: actions/setup-node@v6
|
|
with:
|
|
node-version: 24
|
|
|
|
- name: Validate beta publish inputs
|
|
run: |
|
|
set -euo pipefail
|
|
if [ "${{ inputs.enable_mac }}" != "true" ] && [ "${{ inputs.enable_win }}" != "true" ] && [ "${{ inputs.enable_mac_intel }}" != "true" ] && [ "${{ inputs.enable_linux }}" != "true" ]; then
|
|
echo "release-beta requires at least one platform to be enabled" >&2
|
|
exit 1
|
|
fi
|
|
|
|
- name: Validate R2 release access
|
|
env:
|
|
AWS_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_R2_RELEASES_AK }}
|
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_R2_RELEASES_SK }}
|
|
AWS_DEFAULT_REGION: auto
|
|
AWS_EC2_METADATA_DISABLED: "true"
|
|
CLOUDFLARE_R2_RELEASES_BUCKET: ${{ secrets.CLOUDFLARE_R2_RELEASES_BUCKET }}
|
|
CLOUDFLARE_R2_RELEASES_PUBLIC_ORIGIN: ${{ vars.CLOUDFLARE_R2_RELEASES_PUBLIC_ORIGIN }}
|
|
CLOUDFLARE_R2_RELEASES_URL: ${{ secrets.CLOUDFLARE_R2_RELEASES_URL }}
|
|
R2_ACCESS_PROBE_NAME: release-beta
|
|
RELEASE_CHANNEL: beta
|
|
run: bash .github/scripts/release/r2/check.sh
|
|
|
|
- name: Prepare beta release metadata
|
|
id: beta
|
|
run: node --experimental-strip-types ./scripts/release-beta.ts
|
|
|
|
build_mac:
|
|
name: Build beta mac arm64
|
|
needs: metadata
|
|
if: ${{ inputs.enable_mac }}
|
|
runs-on: macos-14
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v6.0.2
|
|
|
|
- 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
|
|
|
|
- name: Install dependencies
|
|
run: pnpm install --frozen-lockfile
|
|
|
|
- name: Inspect mac Electron framework symlinks
|
|
run: |
|
|
set -euo pipefail
|
|
electron_dist="$(node -e 'const path = require("node:path"); const { createRequire } = require("node:module"); const requireFromDesktop = createRequire(path.join(process.cwd(), "apps/desktop/package.json")); const electron = requireFromDesktop.resolve("electron"); process.stdout.write(path.join(path.dirname(electron), "dist"));')"
|
|
framework="$electron_dist/Electron.app/Contents/Frameworks/Electron Framework.framework"
|
|
missing_links=0
|
|
for link in \
|
|
"$framework/Electron Framework" \
|
|
"$framework/Helpers" \
|
|
"$framework/Libraries" \
|
|
"$framework/Resources" \
|
|
"$framework/Versions/Current"; do
|
|
if [ ! -L "$link" ]; then
|
|
echo "::warning::Expected Electron framework symlink, got non-symlink: $link"
|
|
missing_links=1
|
|
fi
|
|
done
|
|
if [ "$missing_links" -ne 0 ]; then
|
|
ls -la "$framework" >&2 || true
|
|
ls -la "$framework/Versions" >&2 || true
|
|
echo "Continuing into tools-pack because electron-builder is the source of truth for whether packaging actually works."
|
|
fi
|
|
|
|
- name: Prepare Apple signing certificate
|
|
env:
|
|
APPLE_SIGNING_CERTIFICATE_BASE64: ${{ secrets.APPLE_SIGNING_CERTIFICATE_BASE64 }}
|
|
APPLE_SIGNING_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_SIGNING_CERTIFICATE_PASSWORD }}
|
|
run: |
|
|
set -euo pipefail
|
|
cert_path="$RUNNER_TEMP/open-design-signing.p12"
|
|
if ! printf '%s' "$APPLE_SIGNING_CERTIFICATE_BASE64" | base64 --decode > "$cert_path" 2>/dev/null; then
|
|
printf '%s' "$APPLE_SIGNING_CERTIFICATE_BASE64" | base64 -D > "$cert_path"
|
|
fi
|
|
{
|
|
echo "CSC_LINK=$cert_path"
|
|
echo "CSC_KEY_PASSWORD=$APPLE_SIGNING_CERTIFICATE_PASSWORD"
|
|
} >> "$GITHUB_ENV"
|
|
|
|
- name: Build beta mac artifacts
|
|
env:
|
|
APPLE_ID: ${{ secrets.APPLE_ID }}
|
|
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
|
|
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
|
run: |
|
|
set -euo pipefail
|
|
tools_pack_dir="$RUNNER_TEMP/tools-pack"
|
|
build_json_path="$RUNNER_TEMP/mac-tools-pack-build.json"
|
|
build_log_path="$RUNNER_TEMP/mac-tools-pack-build.log"
|
|
rm -rf "$tools_pack_dir"
|
|
: > "$build_log_path"
|
|
build_args=(
|
|
exec tools-pack mac build
|
|
--dir "$tools_pack_dir"
|
|
--namespace release-beta
|
|
--portable
|
|
--app-version "${{ needs.metadata.outputs.beta_version }}"
|
|
--mac-compression normal
|
|
--to dmg
|
|
--json
|
|
--require-vela-cli
|
|
--signed
|
|
)
|
|
if build_output="$(pnpm "${build_args[@]}" 2> >(tee -a "$build_log_path" >&2))"; then
|
|
printf '%s\n' "$build_output" | tee "$build_json_path"
|
|
else
|
|
build_status=$?
|
|
printf '%s\n' "$build_output"
|
|
exit "$build_status"
|
|
fi
|
|
|
|
- name: Capture mac framework diagnostics
|
|
if: ${{ failure() }}
|
|
continue-on-error: true
|
|
run: |
|
|
set -euo pipefail
|
|
output="$RUNNER_TEMP/mac-framework-diagnostics.txt"
|
|
source_resolve_log="$RUNNER_TEMP/mac-framework-source-resolve.err"
|
|
source_framework="$(node -e 'const path = require("node:path"); const { createRequire } = require("node:module"); const requireFromDesktop = createRequire(path.join(process.cwd(), "apps/desktop/package.json")); const electron = requireFromDesktop.resolve("electron"); process.stdout.write(path.join(path.dirname(electron), "dist", "Electron.app", "Contents", "Frameworks", "Electron Framework.framework"));' 2>"$source_resolve_log" || true)"
|
|
built_framework="$RUNNER_TEMP/tools-pack/out/mac/namespaces/release-beta/builder/mac-arm64/Open Design Beta.app/Contents/Frameworks/Electron Framework.framework"
|
|
|
|
dump_framework() {
|
|
local label="$1"
|
|
local framework="$2"
|
|
echo "## $label"
|
|
echo "path=$framework"
|
|
if [ ! -e "$framework" ] && [ ! -L "$framework" ]; then
|
|
echo "missing"
|
|
return 0
|
|
fi
|
|
echo "### top-level"
|
|
ls -la "$framework" || true
|
|
echo "### symlinks"
|
|
find "$framework" -maxdepth 4 -type l -print0 | while IFS= read -r -d '' link; do
|
|
printf '%s -> %s\n' "$link" "$(readlink "$link")"
|
|
done || true
|
|
echo "### selected stat"
|
|
for path in \
|
|
"$framework" \
|
|
"$framework/Electron Framework" \
|
|
"$framework/Versions" \
|
|
"$framework/Versions/Current" \
|
|
"$framework/Versions/Current/Electron Framework" \
|
|
"$framework/Versions/A" \
|
|
"$framework/Versions/A/Electron Framework" \
|
|
"$framework/Resources" \
|
|
"$framework/Versions/A/Resources/Info.plist"; do
|
|
if [ -e "$path" ] || [ -L "$path" ]; then
|
|
stat -f '%Sp %HT %N' "$path" || true
|
|
else
|
|
echo "missing: $path"
|
|
fi
|
|
done
|
|
echo "### plist"
|
|
plutil -p "$framework/Versions/A/Resources/Info.plist" 2>&1 || true
|
|
echo "### codesign display"
|
|
codesign --display --verbose=4 "$framework/Electron Framework" 2>&1 || true
|
|
codesign --display --verbose=4 "$framework/Versions/Current/Electron Framework" 2>&1 || true
|
|
codesign --display --verbose=4 "$framework/Versions/A/Electron Framework" 2>&1 || true
|
|
codesign --display --verbose=4 "$framework" 2>&1 || true
|
|
}
|
|
|
|
{
|
|
date -u
|
|
if [ -n "$source_framework" ]; then
|
|
dump_framework "source Electron Framework" "$source_framework"
|
|
else
|
|
echo "## source Electron Framework"
|
|
echo "resolve failed"
|
|
cat "$source_resolve_log" || true
|
|
fi
|
|
dump_framework "built Electron Framework" "$built_framework"
|
|
} > "$output"
|
|
cat "$output"
|
|
|
|
- name: Upload mac build diagnostics
|
|
if: ${{ always() }}
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: open-design-beta-mac-build-diagnostics
|
|
path: |
|
|
${{ runner.temp }}/mac-tools-pack-build.log
|
|
${{ runner.temp }}/mac-tools-pack-build.json
|
|
${{ runner.temp }}/mac-framework-diagnostics.txt
|
|
if-no-files-found: warn
|
|
|
|
- name: Smoke beta mac packaged runtime
|
|
working-directory: e2e
|
|
env:
|
|
OD_PACKAGED_E2E_BUILD_JSON_PATH: ${{ runner.temp }}/mac-tools-pack-build.json
|
|
OD_PACKAGED_E2E_BUILD_LOG_PATH: ${{ runner.temp }}/mac-tools-pack-build.log
|
|
OD_PACKAGED_E2E_MAC: "1"
|
|
OD_PACKAGED_E2E_NAMESPACE: release-beta
|
|
OD_PACKAGED_E2E_RELEASE_CHANNEL: beta
|
|
OD_PACKAGED_E2E_RELEASE_VERSION: ${{ needs.metadata.outputs.beta_version }}
|
|
OD_PACKAGED_E2E_REPORT_DIR: ${{ runner.temp }}/release-report/mac
|
|
OD_PACKAGED_E2E_TOOLS_PACK_DIR: ${{ runner.temp }}/tools-pack
|
|
run: |
|
|
set -euo pipefail
|
|
pnpm exec tsx scripts/release-smoke.ts mac specs/mac.spec.ts
|
|
|
|
- name: Upload mac e2e spec report
|
|
if: ${{ always() }}
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: open-design-beta-mac-e2e-report
|
|
path: ${{ runner.temp }}/release-report/mac
|
|
if-no-files-found: warn
|
|
|
|
- name: Prepare beta assets
|
|
id: assets
|
|
env:
|
|
ASSET_VERSION_SUFFIX: ${{ needs.metadata.outputs.asset_version_suffix }}
|
|
CLOUDFLARE_R2_RELEASES_PUBLIC_ORIGIN: ${{ vars.CLOUDFLARE_R2_RELEASES_PUBLIC_ORIGIN }}
|
|
MAC_ARTIFACT_MODE: dmg-only
|
|
RELEASE_CHANNEL: beta
|
|
RELEASE_NOTES: Open Design beta ${{ needs.metadata.outputs.beta_version }}${{ needs.metadata.outputs.asset_version_suffix }}
|
|
RELEASE_VERSION: ${{ needs.metadata.outputs.beta_version }}
|
|
TOOLS_PACK_NAMESPACE: release-beta
|
|
run: bash .github/scripts/release/assets/mac.sh
|
|
|
|
- name: Publish beta mac assets to R2
|
|
env:
|
|
AWS_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_R2_RELEASES_AK }}
|
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_R2_RELEASES_SK }}
|
|
AWS_DEFAULT_REGION: auto
|
|
AWS_EC2_METADATA_DISABLED: "true"
|
|
CLOUDFLARE_R2_RELEASES_BUCKET: ${{ secrets.CLOUDFLARE_R2_RELEASES_BUCKET }}
|
|
CLOUDFLARE_R2_RELEASES_PUBLIC_ORIGIN: ${{ vars.CLOUDFLARE_R2_RELEASES_PUBLIC_ORIGIN }}
|
|
CLOUDFLARE_R2_RELEASES_URL: ${{ secrets.CLOUDFLARE_R2_RELEASES_URL }}
|
|
ASSET_VERSION_SUFFIX: ${{ needs.metadata.outputs.asset_version_suffix }}
|
|
MAC_ARTIFACT_MODE: dmg-only
|
|
RELEASE_CHANNEL: beta
|
|
RELEASE_PLATFORM: mac
|
|
RELEASE_SIGNED: "true"
|
|
RELEASE_VERSION: ${{ needs.metadata.outputs.beta_version }}
|
|
run: node --experimental-strip-types .github/scripts/release/r2/publish-platform.ts
|
|
|
|
- name: Upload mac publish manifest
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: open-design-beta-mac-publish-manifest
|
|
path: ${{ runner.temp }}/release-platform-manifests/mac.json
|
|
|
|
build_mac_intel:
|
|
name: Build beta mac x64
|
|
needs: metadata
|
|
if: ${{ inputs.enable_mac_intel }}
|
|
runs-on: macos-15-intel
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v6.0.2
|
|
|
|
- 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
|
|
|
|
- name: Install dependencies
|
|
run: pnpm install --frozen-lockfile
|
|
|
|
- name: Apply beta package version
|
|
run: npm pkg set "version=${{ needs.metadata.outputs.beta_version }}" --prefix apps/packaged
|
|
|
|
- name: Build beta mac intel artifacts
|
|
run: |
|
|
set -euo pipefail
|
|
pnpm exec tools-pack mac build \
|
|
--dir "$RUNNER_TEMP/tools-pack" \
|
|
--namespace release-beta-intel \
|
|
--portable \
|
|
--mac-compression maximum \
|
|
--to all \
|
|
--json
|
|
|
|
- name: Prepare beta mac intel assets
|
|
id: assets
|
|
env:
|
|
ASSET_VERSION_SUFFIX: .unsigned
|
|
CLOUDFLARE_R2_RELEASES_PUBLIC_ORIGIN: ${{ vars.CLOUDFLARE_R2_RELEASES_PUBLIC_ORIGIN }}
|
|
RELEASE_CHANNEL: beta
|
|
RELEASE_VERSION: ${{ needs.metadata.outputs.beta_version }}
|
|
TOOLS_PACK_NAMESPACE: release-beta-intel
|
|
run: bash .github/scripts/release/assets/mac-intel.sh
|
|
|
|
- name: Publish beta mac intel assets to R2
|
|
env:
|
|
AWS_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_R2_RELEASES_AK }}
|
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_R2_RELEASES_SK }}
|
|
AWS_DEFAULT_REGION: auto
|
|
AWS_EC2_METADATA_DISABLED: "true"
|
|
CLOUDFLARE_R2_RELEASES_BUCKET: ${{ secrets.CLOUDFLARE_R2_RELEASES_BUCKET }}
|
|
CLOUDFLARE_R2_RELEASES_PUBLIC_ORIGIN: ${{ vars.CLOUDFLARE_R2_RELEASES_PUBLIC_ORIGIN }}
|
|
CLOUDFLARE_R2_RELEASES_URL: ${{ secrets.CLOUDFLARE_R2_RELEASES_URL }}
|
|
ASSET_VERSION_SUFFIX: ${{ needs.metadata.outputs.asset_version_suffix }}
|
|
MAC_INTEL_ASSET_SUFFIX: .unsigned
|
|
RELEASE_CHANNEL: beta
|
|
RELEASE_PLATFORM: mac-intel
|
|
RELEASE_VERSION: ${{ needs.metadata.outputs.beta_version }}
|
|
run: node --experimental-strip-types .github/scripts/release/r2/publish-platform.ts
|
|
|
|
- name: Upload mac intel publish manifest
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: open-design-beta-mac-intel-publish-manifest
|
|
path: ${{ runner.temp }}/release-platform-manifests/macIntel.json
|
|
|
|
build_win:
|
|
name: Build beta win x64
|
|
needs: metadata
|
|
if: ${{ inputs.enable_win }}
|
|
runs-on: windows-latest
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v6.0.2
|
|
|
|
- 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
|
|
cache: pnpm
|
|
cache-dependency-path: pnpm-lock.yaml
|
|
|
|
- name: Compute Windows tools-pack cache key
|
|
id: win_tools_pack_cache_key
|
|
shell: pwsh
|
|
env:
|
|
WIN_TOOLS_PACK_ORIGIN_KEY: ${{ hashFiles('package.json', 'pnpm-lock.yaml', 'pnpm-workspace.yaml', 'apps/daemon/**', 'apps/web/**', 'apps/desktop/**', 'apps/packaged/**', 'packages/agui-adapter/**', 'packages/contracts/**', 'packages/plugin-runtime/**', 'packages/sidecar-proto/**', 'packages/sidecar/**', 'packages/platform/**', 'tools/pack/bin/**', 'tools/pack/package.json', 'tools/pack/resources/**', 'tools/pack/src/**', 'tools/pack/tsconfig.json', 'assets/community-pets/**', 'assets/frames/**', 'craft/**', 'design-systems/**', 'design-templates/**', 'plugins/_official/**', 'plugins/registry/**', 'prompt-templates/**', 'skills/**', '.github/workflows/release-beta.yml', '.github/scripts/release/cache/win.ps1') }}
|
|
run: |
|
|
if ([string]::IsNullOrWhiteSpace($env:WIN_TOOLS_PACK_ORIGIN_KEY)) {
|
|
throw "Windows tools-pack cache origin key is empty"
|
|
}
|
|
$prefix = "tools-pack-win-v7-beta-$env:RUNNER_OS-"
|
|
"origin=$env:WIN_TOOLS_PACK_ORIGIN_KEY" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
|
|
"prefix=$prefix" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
|
|
"key=$prefix$env:WIN_TOOLS_PACK_ORIGIN_KEY" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
|
|
|
|
- name: Restore Windows tools-pack cache
|
|
id: win_tools_pack_cache_restore
|
|
uses: actions/cache/restore@v5
|
|
continue-on-error: true
|
|
with:
|
|
path: ${{ runner.temp }}/tools-pack-cache
|
|
key: ${{ steps.win_tools_pack_cache_key.outputs.key }}
|
|
restore-keys: |
|
|
${{ steps.win_tools_pack_cache_key.outputs.prefix }}
|
|
|
|
- name: Install dependencies
|
|
run: pnpm install --frozen-lockfile
|
|
|
|
- name: Setup NSIS
|
|
shell: pwsh
|
|
run: |
|
|
if ((Get-Command makensis.exe -ErrorAction SilentlyContinue) -or (Test-Path "C:\Program Files (x86)\NSIS\makensis.exe")) {
|
|
exit 0
|
|
}
|
|
choco install nsis -y --no-progress
|
|
|
|
- name: Build beta windows artifacts
|
|
id: win_tools_pack_build
|
|
shell: pwsh
|
|
run: |
|
|
$ErrorActionPreference = "Stop"
|
|
$toolsPackDir = "${{ runner.temp }}/tools-pack"
|
|
$cacheDir = "${{ runner.temp }}/tools-pack-cache"
|
|
$buildJsonPath = Join-Path $env:RUNNER_TEMP "windows-tools-pack-build.json"
|
|
$buildArgs = @(
|
|
"exec", "tools-pack", "win", "build",
|
|
"--dir", $toolsPackDir,
|
|
"--cache-dir", $cacheDir,
|
|
"--namespace", "release-beta-win",
|
|
"--portable",
|
|
"--app-version", "${{ needs.metadata.outputs.beta_version }}",
|
|
"--to", "all",
|
|
"--json"
|
|
)
|
|
"cache_failed=false" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
|
|
try {
|
|
$buildOutput = pnpm @buildArgs
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "Windows tools-pack cached build exited with code $LASTEXITCODE"
|
|
}
|
|
} catch {
|
|
Write-Warning "Windows tools-pack cached build failed; removing restored cache and retrying without cache."
|
|
"cache_failed=true" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
|
|
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue $cacheDir
|
|
$buildOutput = pnpm exec tools-pack win build `
|
|
--dir $toolsPackDir `
|
|
--namespace release-beta-win `
|
|
--portable `
|
|
--app-version "${{ needs.metadata.outputs.beta_version }}" `
|
|
--to all `
|
|
--json
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "Windows tools-pack uncached fallback build exited with code $LASTEXITCODE"
|
|
}
|
|
}
|
|
$buildOutput | Set-Content -Path $buildJsonPath
|
|
$buildOutput
|
|
|
|
- name: Delete failed Windows tools-pack cache
|
|
if: ${{ steps.win_tools_pack_build.outputs.cache_failed == 'true' && steps.win_tools_pack_cache_restore.outputs.cache-matched-key != '' }}
|
|
shell: pwsh
|
|
continue-on-error: true
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
$matchedKey = "${{ steps.win_tools_pack_cache_restore.outputs.cache-matched-key }}"
|
|
$caches = @(gh cache list --key $matchedKey --limit 100 --json id,key,ref | ConvertFrom-Json | Where-Object { $_.key -eq $matchedKey })
|
|
foreach ($cache in $caches) {
|
|
gh cache delete $cache.id
|
|
}
|
|
"deletedFailedCacheKey=$matchedKey count=$($caches.Count)"
|
|
|
|
- name: Smoke beta windows packaged runtime
|
|
working-directory: e2e
|
|
env:
|
|
OD_PACKAGED_E2E_BUILD_JSON_PATH: ${{ runner.temp }}/windows-tools-pack-build.json
|
|
OD_PACKAGED_E2E_WIN: "1"
|
|
OD_PACKAGED_E2E_WIN_VERIFY_REINSTALL: "0"
|
|
OD_PACKAGED_E2E_NAMESPACE: release-beta-win
|
|
OD_PACKAGED_E2E_RELEASE_CHANNEL: beta
|
|
OD_PACKAGED_E2E_RELEASE_VERSION: ${{ needs.metadata.outputs.beta_version }}
|
|
OD_PACKAGED_E2E_REPORT_DIR: ${{ runner.temp }}/release-report/win
|
|
OD_PACKAGED_E2E_TOOLS_PACK_DIR: ${{ runner.temp }}/tools-pack
|
|
run: |
|
|
$ErrorActionPreference = "Stop"
|
|
pnpm exec tsx scripts/release-smoke.ts win specs/win.spec.ts
|
|
if ($LASTEXITCODE -ne 0) {
|
|
exit $LASTEXITCODE
|
|
}
|
|
|
|
- name: Upload windows e2e spec report
|
|
if: ${{ always() }}
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: open-design-beta-win-e2e-report
|
|
path: ${{ runner.temp }}/release-report/win
|
|
if-no-files-found: warn
|
|
|
|
- name: Prepare windows beta assets
|
|
shell: pwsh
|
|
env:
|
|
ASSET_VERSION_SUFFIX: ${{ needs.metadata.outputs.asset_version_suffix }}
|
|
CLOUDFLARE_R2_RELEASES_PUBLIC_ORIGIN: ${{ vars.CLOUDFLARE_R2_RELEASES_PUBLIC_ORIGIN }}
|
|
RELEASE_CHANNEL: beta
|
|
RELEASE_NOTES: Open Design beta ${{ needs.metadata.outputs.beta_version }}.unsigned
|
|
RELEASE_VERSION: ${{ needs.metadata.outputs.beta_version }}
|
|
TOOLS_PACK_NAMESPACE: release-beta-win
|
|
WINDOWS_ASSET_SUFFIX: .unsigned
|
|
run: ./.github/scripts/release/assets/win.ps1
|
|
|
|
- name: Publish beta windows assets to R2
|
|
env:
|
|
AWS_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_R2_RELEASES_AK }}
|
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_R2_RELEASES_SK }}
|
|
AWS_DEFAULT_REGION: auto
|
|
AWS_EC2_METADATA_DISABLED: "true"
|
|
CLOUDFLARE_R2_RELEASES_BUCKET: ${{ secrets.CLOUDFLARE_R2_RELEASES_BUCKET }}
|
|
CLOUDFLARE_R2_RELEASES_PUBLIC_ORIGIN: ${{ vars.CLOUDFLARE_R2_RELEASES_PUBLIC_ORIGIN }}
|
|
CLOUDFLARE_R2_RELEASES_URL: ${{ secrets.CLOUDFLARE_R2_RELEASES_URL }}
|
|
ASSET_VERSION_SUFFIX: ${{ needs.metadata.outputs.asset_version_suffix }}
|
|
RELEASE_CHANNEL: beta
|
|
RELEASE_PLATFORM: win
|
|
RELEASE_VERSION: ${{ needs.metadata.outputs.beta_version }}
|
|
WIN_ASSET_SUFFIX: .unsigned
|
|
run: node --experimental-strip-types .github/scripts/release/r2/publish-platform.ts
|
|
|
|
- name: Upload windows publish manifest
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: open-design-beta-win-publish-manifest
|
|
path: ${{ runner.temp }}/release-platform-manifests/win.json
|
|
|
|
build_linux:
|
|
name: Build beta linux x64
|
|
needs: metadata
|
|
if: ${{ inputs.enable_linux }}
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v6.0.2
|
|
|
|
- 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
|
|
cache: pnpm
|
|
cache-dependency-path: pnpm-lock.yaml
|
|
|
|
- name: Install dependencies
|
|
run: pnpm install --frozen-lockfile
|
|
|
|
# `--containerized` builds the AppImage inside the electronuserland/builder
|
|
# Docker image (glibc 2.27 baseline) so the resulting binary runs on older
|
|
# distros than ubuntu-latest's glibc 2.39. Docker is preinstalled on the
|
|
# GitHub-hosted ubuntu-latest runner, so no extra setup is required.
|
|
- name: Build beta linux artifacts
|
|
run: |
|
|
set -euo pipefail
|
|
tools_pack_dir="$RUNNER_TEMP/tools-pack"
|
|
report_dir="$RUNNER_TEMP/release-report/linux"
|
|
build_json_path="$report_dir/tools-pack.json"
|
|
build_log_path="$report_dir/tools-pack.log"
|
|
rm -rf "$tools_pack_dir"
|
|
mkdir -p "$report_dir"
|
|
: > "$build_log_path"
|
|
build_args=(
|
|
exec tools-pack linux build
|
|
--dir "$tools_pack_dir"
|
|
--namespace release-beta-linux
|
|
--portable
|
|
--app-version "${{ needs.metadata.outputs.beta_version }}"
|
|
--to appimage
|
|
--containerized
|
|
--json
|
|
)
|
|
if build_output="$(pnpm "${build_args[@]}" 2> >(tee -a "$build_log_path" >&2))"; then
|
|
printf '%s\n' "$build_output" | tee "$build_json_path"
|
|
node -e 'const fs = require("node:fs"); JSON.parse(fs.readFileSync(process.argv[1], "utf8"));' "$build_json_path"
|
|
else
|
|
build_status=$?
|
|
printf '%s\n' "$build_output" | tee "$build_json_path"
|
|
exit "$build_status"
|
|
fi
|
|
|
|
- name: Smoke beta linux AppImage runtime
|
|
working-directory: e2e
|
|
env:
|
|
OD_PACKAGED_E2E_LINUX_APPIMAGE: "1"
|
|
OD_PACKAGED_E2E_NAMESPACE: release-beta-linux
|
|
OD_PACKAGED_E2E_SCREENSHOT_PATH: ${{ runner.temp }}/release-report/linux/screenshots/open-design-linux-smoke.png
|
|
OD_PACKAGED_E2E_TOOLS_PACK_DIR: ${{ runner.temp }}/tools-pack
|
|
run: |
|
|
set -euo pipefail
|
|
report_dir="$RUNNER_TEMP/release-report/linux"
|
|
mkdir -p "$report_dir/screenshots"
|
|
cat > "$report_dir/manifest.json" <<EOF
|
|
{
|
|
"platform": "linux",
|
|
"spec": "specs/linux.spec.ts",
|
|
"namespace": "release-beta-linux",
|
|
"screenshot": "screenshots/open-design-linux-smoke.png",
|
|
"githubRunId": "$GITHUB_RUN_ID",
|
|
"githubRunAttempt": "$GITHUB_RUN_ATTEMPT",
|
|
"commit": "$GITHUB_SHA"
|
|
}
|
|
EOF
|
|
sudo apt-get update 2>&1 | tee "$report_dir/apt-get-update.log"
|
|
sudo apt-get install -y xvfb 2>&1 | tee "$report_dir/apt-get-install-xvfb.log"
|
|
xvfb-run -a pnpm test specs/linux.spec.ts 2>&1 | tee "$report_dir/vitest.log"
|
|
|
|
- name: Upload linux e2e spec report
|
|
if: ${{ always() }}
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: open-design-beta-linux-e2e-report
|
|
path: ${{ runner.temp }}/release-report/linux
|
|
if-no-files-found: warn
|
|
|
|
- name: Prepare linux beta assets
|
|
env:
|
|
LINUX_ASSET_SUFFIX: .unsigned
|
|
RELEASE_VERSION: ${{ needs.metadata.outputs.beta_version }}
|
|
TOOLS_PACK_NAMESPACE: release-beta-linux
|
|
run: bash .github/scripts/release/assets/linux.sh
|
|
|
|
- name: Publish beta linux assets to R2
|
|
env:
|
|
AWS_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_R2_RELEASES_AK }}
|
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_R2_RELEASES_SK }}
|
|
AWS_DEFAULT_REGION: auto
|
|
AWS_EC2_METADATA_DISABLED: "true"
|
|
CLOUDFLARE_R2_RELEASES_BUCKET: ${{ secrets.CLOUDFLARE_R2_RELEASES_BUCKET }}
|
|
CLOUDFLARE_R2_RELEASES_PUBLIC_ORIGIN: ${{ vars.CLOUDFLARE_R2_RELEASES_PUBLIC_ORIGIN }}
|
|
CLOUDFLARE_R2_RELEASES_URL: ${{ secrets.CLOUDFLARE_R2_RELEASES_URL }}
|
|
ASSET_VERSION_SUFFIX: ${{ needs.metadata.outputs.asset_version_suffix }}
|
|
LINUX_ASSET_SUFFIX: .unsigned
|
|
RELEASE_CHANNEL: beta
|
|
RELEASE_PLATFORM: linux
|
|
RELEASE_VERSION: ${{ needs.metadata.outputs.beta_version }}
|
|
run: node --experimental-strip-types .github/scripts/release/r2/publish-platform.ts
|
|
|
|
- name: Upload linux publish manifest
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: open-design-beta-linux-publish-manifest
|
|
path: ${{ runner.temp }}/release-platform-manifests/linux.json
|
|
|
|
publish:
|
|
name: Publish beta metadata to R2
|
|
needs:
|
|
- metadata
|
|
- build_mac
|
|
- build_mac_intel
|
|
- build_win
|
|
- build_linux
|
|
if: >-
|
|
${{
|
|
always() &&
|
|
!cancelled() &&
|
|
needs.metadata.result == 'success' &&
|
|
(inputs.enable_mac || inputs.enable_win || inputs.enable_mac_intel || inputs.enable_linux)
|
|
}}
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
AWS_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_R2_RELEASES_AK }}
|
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_R2_RELEASES_SK }}
|
|
AWS_DEFAULT_REGION: auto
|
|
AWS_EC2_METADATA_DISABLED: "true"
|
|
CLOUDFLARE_R2_RELEASES_BUCKET: ${{ secrets.CLOUDFLARE_R2_RELEASES_BUCKET }}
|
|
CLOUDFLARE_R2_RELEASES_PUBLIC_ORIGIN: ${{ vars.CLOUDFLARE_R2_RELEASES_PUBLIC_ORIGIN }}
|
|
CLOUDFLARE_R2_RELEASES_URL: ${{ secrets.CLOUDFLARE_R2_RELEASES_URL }}
|
|
ASSET_VERSION_SUFFIX: ${{ needs.metadata.outputs.asset_version_suffix }}
|
|
BASE_VERSION: ${{ needs.metadata.outputs.base_version }}
|
|
BETA_VERSION: ${{ needs.metadata.outputs.beta_version }}
|
|
BRANCH_NAME: ${{ needs.metadata.outputs.branch }}
|
|
ENABLE_LINUX: ${{ inputs.enable_linux }}
|
|
ENABLE_MAC: ${{ inputs.enable_mac }}
|
|
ENABLE_MAC_INTEL: ${{ inputs.enable_mac_intel }}
|
|
ENABLE_WIN: ${{ inputs.enable_win }}
|
|
LINUX_RESULT: ${{ needs.build_linux.result }}
|
|
MAC_INTEL_RESULT: ${{ needs.build_mac_intel.result }}
|
|
MAC_RESULT: ${{ needs.build_mac.result }}
|
|
RELEASE_CHANNEL: beta
|
|
RELEASE_VERSION: ${{ needs.metadata.outputs.beta_version }}
|
|
RELEASE_SIGNED: "true"
|
|
STATE_SOURCE: ${{ needs.metadata.outputs.state_source }}
|
|
WIN_RESULT: ${{ needs.build_win.result }}
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v6.0.2
|
|
|
|
- name: Download mac publish manifest
|
|
if: ${{ inputs.enable_mac && needs.build_mac.result == 'success' }}
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
name: open-design-beta-mac-publish-manifest
|
|
path: ${{ runner.temp }}/release-platform-manifests
|
|
|
|
- name: Download mac intel publish manifest
|
|
if: ${{ inputs.enable_mac_intel && needs.build_mac_intel.result == 'success' }}
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
name: open-design-beta-mac-intel-publish-manifest
|
|
path: ${{ runner.temp }}/release-platform-manifests
|
|
|
|
- name: Download windows publish manifest
|
|
if: ${{ inputs.enable_win && needs.build_win.result == 'success' }}
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
name: open-design-beta-win-publish-manifest
|
|
path: ${{ runner.temp }}/release-platform-manifests
|
|
|
|
- name: Download linux publish manifest
|
|
if: ${{ inputs.enable_linux && needs.build_linux.result == 'success' }}
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
name: open-design-beta-linux-publish-manifest
|
|
path: ${{ runner.temp }}/release-platform-manifests
|
|
|
|
- name: Setup Node.js
|
|
uses: actions/setup-node@v6
|
|
with:
|
|
node-version: 24
|
|
|
|
- name: Publish beta metadata to R2
|
|
id: r2
|
|
run: node --experimental-strip-types .github/scripts/release/r2/publish-beta-metadata.ts
|
|
|
|
- name: Verify R2 beta metadata
|
|
env:
|
|
R2_METADATA_URL: ${{ steps.r2.outputs.version_metadata_url }}
|
|
run: node --experimental-strip-types .github/scripts/release/r2/verify-beta-metadata.ts
|
|
|
|
- name: Publish summary
|
|
env:
|
|
R2_METADATA_URL: ${{ steps.r2.outputs.version_metadata_url }}
|
|
run: node --experimental-strip-types .github/scripts/release/r2/summary-beta.ts >> "$GITHUB_STEP_SUMMARY"
|
|
|
|
- name: Cleanup workflow artifacts
|
|
if: ${{ success() && steps.r2.outputs.release_state == 'complete' }}
|
|
run: bash .github/scripts/release/github/cleanup-artifacts.sh
|
|
|
|
runtime_trace:
|
|
name: Runtime trace
|
|
needs:
|
|
- metadata
|
|
- build_mac
|
|
- build_mac_intel
|
|
- build_win
|
|
- build_linux
|
|
- publish
|
|
if: ${{ always() }}
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
|
|
steps:
|
|
- name: Summarize workflow runtime
|
|
continue-on-error: true
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
RUN_ID: ${{ github.run_id }}
|
|
run: |
|
|
set -euo pipefail
|
|
run_json="$RUNNER_TEMP/run.json"
|
|
gh run view "$RUN_ID" --repo "$GITHUB_REPOSITORY" --json conclusion,createdAt,databaseId,displayTitle,event,headBranch,jobs,updatedAt,url > "$run_json"
|
|
jq -r '
|
|
def parse_ts: sub("\\.[0-9]+Z$"; "Z") | fromdateiso8601;
|
|
def seconds($start; $end):
|
|
if ($start and $end) then (($end | parse_ts) - ($start | parse_ts)) else null end;
|
|
def fmt($seconds):
|
|
if $seconds == null then "n/a"
|
|
elif $seconds >= 60 then "\(((($seconds / 60) * 10 | round) / 10))m"
|
|
else "\(($seconds | round))s"
|
|
end;
|
|
def row($cells): "| \($cells | join(" | ")) |";
|
|
|
|
.jobs as $jobs |
|
|
[
|
|
"## Runtime trace",
|
|
"",
|
|
"Run: [\(.displayTitle)](\(.url))",
|
|
"Event: `\(.event)`",
|
|
"Branch: `\(.headBranch)`",
|
|
"Elapsed: \(fmt(seconds(.createdAt; .updatedAt)))",
|
|
"",
|
|
"### Jobs",
|
|
"| Job | Result | Duration | Slowest step |",
|
|
"| --- | --- | ---: | --- |",
|
|
(
|
|
$jobs
|
|
| sort_by(seconds(.startedAt; .completedAt) // 0)
|
|
| reverse
|
|
| .[]
|
|
| select(.conclusion != "skipped")
|
|
| (
|
|
[(.steps // [])[] | select(.startedAt and .completedAt and .conclusion != "skipped") | {name, duration: seconds(.startedAt; .completedAt)}]
|
|
| max_by(.duration // 0)
|
|
) as $slow
|
|
| row([.name, (.conclusion // .status), fmt(seconds(.startedAt; .completedAt)), "\($slow.name // "n/a") (\(fmt($slow.duration)))"])
|
|
),
|
|
"",
|
|
"### Slowest steps",
|
|
"| Step | Job | Duration |",
|
|
"| --- | --- | ---: |",
|
|
(
|
|
[
|
|
$jobs[] as $job
|
|
| ($job.steps // [])[]
|
|
| select(.startedAt and .completedAt and .conclusion != "skipped")
|
|
| {job: $job.name, name, duration: seconds(.startedAt; .completedAt)}
|
|
]
|
|
| sort_by(.duration // 0)
|
|
| reverse
|
|
| .[0:20][]
|
|
| row([.name, .job, fmt(.duration)])
|
|
)
|
|
][]
|
|
' "$run_json" >> "$GITHUB_STEP_SUMMARY"
|