mirror of
https://github.com/stablyai/orca.git
synced 2026-11-13 09:27:29 +00:00
* chore(endpoints): call orca.dev for feedback, plugin lists and update notes The site moved to orca.dev. Feedback, connection diagnostics, plugin install stats, the plugin kill list, update nudges and the changelog feed now use orca.dev, as do the diagnostics token URL baked into builds and the release download mirror (downloads.orca.dev). The old hosts serve the same paths, so released builds keep working. Skill share links on share.orca.dev are accepted, and the mobile scanner recognizes the orca.dev Android download code. The old hosts stay accepted. * chore(ci): leave the release JavaScript benchmark workflow unchanged Editing it triggers the benchmark, whose parity check currently fails on arm64 because max-quality brotli output for the mobile page differs by architecture. The benchmark only needs some diagnostics URL, and the old host still serves it.
481 lines
23 KiB
YAML
481 lines
23 KiB
YAML
name: Daily macOS Dev Build
|
||
|
||
# Publish daily main builds to orca-daily, separate from hourly and stable/RC feeds. Sign/notarize for update-preserved macOS TCC grants; PR/release pipelines own blocking validation, with E2E detached after publish. Cron 18:15 UTC means 10:15 PST/11:15 PDT, offset from hourly. Use the shared hourly App with write access to orca-daily (setup-hourly-release-token.sh/setup-daily-release-repo.sh). Mint tokens again after build and upload because 2×45min retry can exceed their 1h life. Keep releases draft until manifests verify; drafts retain build numbers.
|
||
|
||
on:
|
||
schedule:
|
||
# Once a day, late morning Pacific. Single cron — no DST twin, no clock gate.
|
||
- cron: '15 18 * * *'
|
||
workflow_dispatch:
|
||
inputs:
|
||
force:
|
||
description: Build even if main has not moved since the last daily
|
||
required: false
|
||
default: false
|
||
type: boolean
|
||
|
||
permissions:
|
||
contents: read
|
||
|
||
concurrency:
|
||
group: daily-mac-build
|
||
cancel-in-progress: false
|
||
|
||
env:
|
||
DAILY_REPO: stablyai/orca-daily
|
||
# Keep ~30 days so a regression can be bisected without holding a full year.
|
||
DAILY_RETAIN_COUNT: 30
|
||
|
||
jobs:
|
||
# Why: the app and the orcad template it ships must come from one commit, so resolve main once.
|
||
resolve-main:
|
||
if: github.repository == 'stablyai/orca'
|
||
runs-on: ubuntu-latest
|
||
timeout-minutes: 5
|
||
outputs:
|
||
head_sha: ${{ steps.resolve.outputs.head_sha }}
|
||
steps:
|
||
- name: Resolve main
|
||
id: resolve
|
||
shell: bash
|
||
env:
|
||
GH_TOKEN: ${{ github.token }}
|
||
run: |
|
||
set -euo pipefail
|
||
head_sha="$(gh api "repos/$GITHUB_REPOSITORY/commits/main" --jq .sha)"
|
||
[[ "$head_sha" =~ ^[0-9a-f]{40}$ ]] || { echo "::error::Could not resolve main"; exit 1; }
|
||
echo "head_sha=$head_sha" >>"$GITHUB_OUTPUT"
|
||
|
||
# As release-cut does: every SSH host runs orcad, deployed from the template this build ships.
|
||
orcad-template:
|
||
needs: resolve-main
|
||
permissions:
|
||
contents: read
|
||
actions: read
|
||
uses: ./.github/workflows/node-server-tests.yml
|
||
with:
|
||
ref: ${{ needs.resolve-main.outputs.head_sha }}
|
||
build_template: true
|
||
fast_runners: false
|
||
|
||
build-daily-mac:
|
||
needs:
|
||
- resolve-main
|
||
- orcad-template
|
||
if: github.repository == 'stablyai/orca'
|
||
outputs:
|
||
tag: ${{ steps.release.outputs.tag }}
|
||
version: ${{ steps.daily.outputs.version }}
|
||
head_sha: ${{ steps.freshness.outputs.head_sha }}
|
||
published: ${{ steps.publish_live.outcome == 'success' && 'true' || 'false' }}
|
||
# Hosted macos-15 cannot boot the packaged x64 terminal host that afterPack verifies.
|
||
runs-on: blacksmith-6vcpu-macos-15
|
||
# Allow 150min for install 3×10 + publish 2×45 + ~25 setup/build/verify, so outer timeout cannot strand cleanup mid-retry.
|
||
timeout-minutes: 150
|
||
env:
|
||
NODE_OPTIONS: --max-old-space-size=4096
|
||
steps:
|
||
- name: Checkout
|
||
uses: actions/checkout@v6
|
||
with:
|
||
ref: ${{ needs.resolve-main.outputs.head_sha }}
|
||
# Version helpers only read HEAD; published versions come from git tags.
|
||
fetch-depth: 1
|
||
# Read this repo without persisted credentials; write orca-daily through env-passed App token (zizmor: artipacked).
|
||
persist-credentials: false
|
||
|
||
- name: Mint daily repo token
|
||
id: app_token
|
||
uses: actions/create-github-app-token@v2
|
||
with:
|
||
app-id: ${{ secrets.HOURLY_RELEASE_APP_ID }}
|
||
private-key: ${{ secrets.HOURLY_RELEASE_APP_PRIVATE_KEY }}
|
||
owner: stablyai
|
||
repositories: orca-daily
|
||
|
||
# Skip unchanged main to avoid duplicate builds/tags and runner cost.
|
||
- name: Check whether main moved since the last daily
|
||
id: freshness
|
||
shell: bash
|
||
env:
|
||
GH_TOKEN: ${{ steps.app_token.outputs.token }}
|
||
FORCED: ${{ github.event_name == 'workflow_dispatch' && inputs.force }}
|
||
run: |
|
||
set -euo pipefail
|
||
head_sha="$(git rev-parse HEAD)"
|
||
echo "head_sha=$head_sha" >>"$GITHUB_OUTPUT"
|
||
if [[ "$FORCED" == "true" ]]; then
|
||
echo "should_build=true" >>"$GITHUB_OUTPUT"
|
||
echo "Forced dispatch; building $head_sha."
|
||
exit 0
|
||
fi
|
||
# Drafts never shipped; exclude them when comparing the previous daily’s source commit.
|
||
last_body="$(gh release list --repo "$DAILY_REPO" --limit 20 --json tagName,isDraft \
|
||
--jq 'map(select(.isDraft | not)) | .[0].tagName // empty' 2>/dev/null || true)"
|
||
if [[ -z "$last_body" ]]; then
|
||
echo "should_build=true" >>"$GITHUB_OUTPUT"
|
||
echo "No prior daily release found; building $head_sha."
|
||
exit 0
|
||
fi
|
||
last_sha="$(gh release view "$last_body" --repo "$DAILY_REPO" --json body \
|
||
--jq '.body | capture("commit `(?<sha>[0-9a-f]{7,40})`") | .sha' 2>/dev/null || true)"
|
||
if [[ -n "$last_sha" && "$head_sha" == "$last_sha"* ]]; then
|
||
echo "should_build=false" >>"$GITHUB_OUTPUT"
|
||
echo "main is unchanged since $last_body ($last_sha); skipping."
|
||
else
|
||
echo "should_build=true" >>"$GITHUB_OUTPUT"
|
||
echo "main moved to $head_sha (last daily built $last_sha); building."
|
||
fi
|
||
|
||
- name: Setup pnpm
|
||
if: steps.freshness.outputs.should_build == 'true'
|
||
uses: pnpm/setup@v2
|
||
with:
|
||
install: false
|
||
|
||
- name: Setup Node.js
|
||
if: steps.freshness.outputs.should_build == 'true'
|
||
uses: actions/setup-node@v6
|
||
with:
|
||
node-version-file: package.json
|
||
check-latest: true
|
||
cache: pnpm
|
||
cache-dependency-path: |
|
||
pnpm-lock.yaml
|
||
mobile/pnpm-lock.yaml
|
||
|
||
# Cache bundled host Node by exact pin without restore keys.
|
||
- name: Locate the pinned Node runtime cache
|
||
if: steps.freshness.outputs.should_build == 'true'
|
||
shell: bash
|
||
run: echo "ORCA_NODE_RUNTIME_CACHE_DIR=$HOME/Library/Caches/orca-node-runtime" >>"$GITHUB_ENV"
|
||
|
||
- name: Cache the pinned Node runtime
|
||
if: steps.freshness.outputs.should_build == 'true'
|
||
uses: actions/cache@v5
|
||
with:
|
||
path: ~/Library/Caches/orca-node-runtime
|
||
key: orca-node-runtime-mac-${{ hashFiles('src/shared/node-runtime-pin.ts') }}
|
||
|
||
- name: Cache electron-builder downloads
|
||
if: steps.freshness.outputs.should_build == 'true'
|
||
uses: actions/cache@v5
|
||
with:
|
||
path: |
|
||
~/Library/Caches/electron
|
||
~/Library/Caches/electron-builder
|
||
key: electron-builder-mac-${{ hashFiles('pnpm-lock.yaml') }}
|
||
restore-keys: |
|
||
electron-builder-mac-
|
||
|
||
# Install x64 and arm64 optional natives for both packaged macOS targets.
|
||
- name: Install dependencies
|
||
if: steps.freshness.outputs.should_build == 'true'
|
||
uses: nick-fields/retry@v4
|
||
with:
|
||
timeout_minutes: 10
|
||
max_attempts: 3
|
||
retry_wait_seconds: 30
|
||
command: pnpm install --frozen-lockfile --cpu=current,x64,arm64
|
||
|
||
# Build required out/mobile-web using mobile’s React Native/Expo install.
|
||
- uses: ./.github/actions/install-mobile-dependencies
|
||
if: steps.freshness.outputs.should_build == 'true'
|
||
|
||
# Why: signing is what makes a daily installable over an existing Orca, so
|
||
# a missing cert must fail here rather than after a 20-minute build.
|
||
- name: Verify macOS signing environment
|
||
if: steps.freshness.outputs.should_build == 'true'
|
||
run: node config/scripts/desktop-distribution/macos/verify-macos-release-env.mjs
|
||
env:
|
||
CSC_LINK: ${{ secrets.MAC_CERTS }}
|
||
CSC_KEY_PASSWORD: ${{ secrets.MAC_CERTS_PASSWORD }}
|
||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
|
||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||
|
||
- name: Compute daily version
|
||
id: daily
|
||
if: steps.freshness.outputs.should_build == 'true'
|
||
shell: bash
|
||
env:
|
||
GH_TOKEN: ${{ steps.app_token.outputs.token }}
|
||
MAIN_REPO_TOKEN: ${{ github.token }}
|
||
run: |
|
||
set -euo pipefail
|
||
# The script continues numbering for its resolved base version; stranded drafts still reserve numbers,
|
||
# although freshness checks exclude them because they never shipped.
|
||
names="$(gh release list --repo "$DAILY_REPO" --limit 200 --json name \
|
||
--jq '.[].name // empty')"
|
||
# Tags survive release deletion, preventing updater version regression. Use a separate token because
|
||
# GH_TOKEN’s App grant is scoped to the channel repo; lookup failure falls back to package.json.
|
||
main_tags="$(GH_TOKEN="$MAIN_REPO_TOKEN" gh api \
|
||
"repos/$GITHUB_REPOSITORY/git/matching-refs/tags/v" \
|
||
--jq '.[].ref | sub("^refs/tags/"; "")' || true)"
|
||
# Already-shipped channel tags are a second floor so unpublishing a
|
||
# buggy main release cannot drag this series below a daily already out.
|
||
channel_tags="$(gh release list --repo "$DAILY_REPO" --limit 200 --json tagName \
|
||
--jq '.[].tagName' || true)"
|
||
published="$main_tags"$'\n'"$channel_tags"
|
||
echo "Published version sources: $(grep -c . <<<"$main_tags" || true) main tags, $(grep -c . <<<"$channel_tags" || true) channel tags"
|
||
ORCA_PUBLISHED_VERSIONS="$published" ORCA_DAILY_RELEASE_NAMES="$names" \
|
||
node "$(if [ -f config/scripts/release/versioning/daily-build-version.mjs ]; then printf %s config/scripts/release/versioning/daily-build-version.mjs; else printf %s config/scripts/daily-build-version.mjs; fi)" \
|
||
>"$RUNNER_TEMP/daily-identity.txt"
|
||
grep -E '^(version|build_number)=' "$RUNNER_TEMP/daily-identity.txt"
|
||
# Dispatch YAML can be newer than the pinned source’s version script; reject missing output before publishing an untitled release.
|
||
if ! grep -q '^name=' "$RUNNER_TEMP/daily-identity.txt"; then
|
||
echo "::error::daily-build-version.mjs emitted no release name; this workflow and main's copy of the script are out of sync."
|
||
exit 1
|
||
fi
|
||
cat "$RUNNER_TEMP/daily-identity.txt" >>"$GITHUB_OUTPUT"
|
||
|
||
- name: Build app
|
||
if: steps.freshness.outputs.should_build == 'true'
|
||
run: pnpm build:release
|
||
env:
|
||
NODE_OPTIONS: --max-old-space-size=4096
|
||
# Leave dev identity unset so official-only stable/rc telemetry stays silent.
|
||
ORCA_DIAGNOSTICS_TOKEN_URL: https://orca.dev/diagnostics/token
|
||
|
||
# After the app build so nothing that cleans out/ can drop it; electron-builder ships it.
|
||
- name: Download the orcad deployment template
|
||
if: steps.freshness.outputs.should_build == 'true'
|
||
uses: actions/download-artifact@v8
|
||
with:
|
||
name: orcad-template
|
||
path: out/orcad-template
|
||
|
||
# Refresh the one-hour token after read-only install/build, before long publish/notary work.
|
||
- name: Re-mint daily repo token for publish
|
||
id: app_token_publish
|
||
if: steps.freshness.outputs.should_build == 'true'
|
||
uses: actions/create-github-app-token@v2
|
||
with:
|
||
app-id: ${{ secrets.HOURLY_RELEASE_APP_ID }}
|
||
private-key: ${{ secrets.HOURLY_RELEASE_APP_PRIVATE_KEY }}
|
||
owner: stablyai
|
||
repositories: orca-daily
|
||
|
||
- name: Create daily release
|
||
id: release
|
||
if: steps.freshness.outputs.should_build == 'true'
|
||
shell: bash
|
||
env:
|
||
GH_TOKEN: ${{ steps.app_token_publish.outputs.token }}
|
||
TAG: v${{ steps.daily.outputs.version }}
|
||
NAME: ${{ steps.daily.outputs.name }}
|
||
SHA: ${{ steps.freshness.outputs.head_sha }}
|
||
run: |
|
||
set -euo pipefail
|
||
# Freshness parses the 12-character source SHA from the body even though titles show seven.
|
||
short_sha="${SHA:0:12}"
|
||
# Write one body file for creation and reassertion; freshness parsing requires exact agreement.
|
||
notes_file="$RUNNER_TEMP/daily-release-notes.md"
|
||
cat >"$notes_file" <<EOF
|
||
Automated daily macOS and Windows dev build from commit \`$short_sha\`.
|
||
|
||
Built from [\`stablyai/orca@$short_sha\`](https://github.com/stablyai/orca/commit/$SHA).
|
||
|
||
**Unvetted.** No tests ran. The macOS build is signed and notarized like a
|
||
release, so it installs through Orca's in-app updater and opens from a manual
|
||
download without a Gatekeeper prompt — but nothing here has been reviewed.
|
||
|
||
**Windows builds are unsigned.** They install and update normally once you
|
||
are on one, but a signed Stable or RC build cannot install one through the
|
||
in-app updater — download \`orca-windows-setup.exe\` below and run it once
|
||
(SmartScreen will warn about an unknown publisher). Every later switch,
|
||
including back to Stable, works in-app from there.
|
||
EOF
|
||
# Precreate a known tag as draft so builder cannot infer it from package.json.
|
||
# Keep incomplete assets invisible until manifest verification, even if timeout kills the job without cleanup.
|
||
gh release create "$TAG" \
|
||
--repo "$DAILY_REPO" \
|
||
--title "$NAME" \
|
||
--draft \
|
||
--notes-file "$notes_file"
|
||
echo "tag=$TAG" >>"$GITHUB_OUTPUT"
|
||
echo "notes_file=$notes_file" >>"$GITHUB_OUTPUT"
|
||
|
||
- name: Publish daily macOS artifacts
|
||
if: steps.freshness.outputs.should_build == 'true'
|
||
uses: nick-fields/retry@v4
|
||
with:
|
||
# Allow two 45min pack/notarize/upload attempts; a third costs more runner time than a missed daily.
|
||
timeout_minutes: 45
|
||
max_attempts: 2
|
||
retry_wait_seconds: 30
|
||
command: node --disable-warning=MODULE_TYPELESS_PACKAGE_JSON "$(if [ -f config/scripts/orcad-distribution/prebuilds/pinned-node-downloads.mjs ]; then printf %s config/scripts/orcad-distribution/prebuilds/pinned-node-downloads.mjs; else printf %s config/scripts/pinned-node-downloads.mjs; fi)" darwin-arm64 darwin-x64 && node "$(if [ -f config/scripts/native-build/node-addons/ensure-native-runtime.mjs ]; then printf %s config/scripts/native-build/node-addons/ensure-native-runtime.mjs; else printf %s config/scripts/ensure-native-runtime.mjs; fi)" --runtime=electron && if { [ -f config/scripts/desktop-distribution/macos/build-mac-notarization-overlap.mjs ] || [ -f config/scripts/build-mac-notarization-overlap.mjs ]; }; then ORCA_MAC_DAILY=1 node "$(if [ -f config/scripts/desktop-distribution/macos/build-mac-notarization-overlap.mjs ]; then printf %s config/scripts/desktop-distribution/macos/build-mac-notarization-overlap.mjs; else printf %s config/scripts/build-mac-notarization-overlap.mjs; fi)" --publish always; else ORCA_MAC_DAILY=1 pnpm exec electron-builder --config config/electron-builder.config.cjs --mac --publish always; fi
|
||
env:
|
||
# Give the publisher App write access to its configured orca-daily target.
|
||
GH_TOKEN: ${{ steps.app_token_publish.outputs.token }}
|
||
ORCA_DAILY_BUILD_VERSION: ${{ steps.daily.outputs.version }}
|
||
ORCA_BUILD_COMMIT: ${{ steps.daily.outputs.commit }}
|
||
# beforePack and afterPack fail the package when the template is absent.
|
||
ORCA_REQUIRE_ORCAD_TEMPLATE: '1'
|
||
CSC_LINK: ${{ secrets.MAC_CERTS }}
|
||
CSC_KEY_PASSWORD: ${{ secrets.MAC_CERTS_PASSWORD }}
|
||
# Require Apple id/app-specific password/team for notary authentication, separate from signing cert.
|
||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
|
||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||
|
||
# Always refresh after upload: 2×45min can outlive the earlier token, including failed/cancelled uploads needing draft cleanup.
|
||
- name: Re-mint daily repo token for verify and cleanup
|
||
id: app_token_final
|
||
if: always() && steps.freshness.outputs.should_build == 'true'
|
||
uses: actions/create-github-app-token@v2
|
||
with:
|
||
app-id: ${{ secrets.HOURLY_RELEASE_APP_ID }}
|
||
private-key: ${{ secrets.HOURLY_RELEASE_APP_PRIVATE_KEY }}
|
||
owner: stablyai
|
||
repositories: orca-daily
|
||
|
||
# Builder cannot write this field, so older macOS updaters would install a build that won't launch.
|
||
- name: Gate the update manifest on the minimum macOS
|
||
if: steps.freshness.outputs.should_build == 'true'
|
||
shell: bash
|
||
env:
|
||
GH_TOKEN: ${{ steps.app_token_final.outputs.token }}
|
||
TAG: ${{ steps.release.outputs.tag }}
|
||
run: |
|
||
set -euo pipefail
|
||
node config/scripts/desktop-distribution/macos/gate-mac-update-manifests.mjs dist
|
||
gh release upload "$TAG" dist/*-mac.yml --repo "$DAILY_REPO" --clobber
|
||
|
||
# Verify latest-mac.yml before exposing its release to prevent picker downloads returning 404.
|
||
- name: Verify update manifest published
|
||
if: steps.freshness.outputs.should_build == 'true'
|
||
shell: bash
|
||
env:
|
||
GH_TOKEN: ${{ steps.app_token_final.outputs.token }}
|
||
TAG: ${{ steps.release.outputs.tag }}
|
||
run: |
|
||
set -euo pipefail
|
||
assets="$(gh release view "$TAG" --repo "$DAILY_REPO" --json assets --jq '.assets[].name')"
|
||
echo "Published assets:"
|
||
echo "$assets"
|
||
# Drafts are already invisible; let failure cleanup delete them so set -e cannot mask this explicit failure.
|
||
for required in latest-mac.yml; do
|
||
if ! grep -qx "$required" <<<"$assets"; then
|
||
echo "::error::Daily draft $TAG is missing $required; the updater could not install it."
|
||
exit 1
|
||
fi
|
||
done
|
||
if ! grep -q '\.zip$' <<<"$assets"; then
|
||
echo "::error::Daily draft $TAG has no ZIP artifact for the updater to download."
|
||
exit 1
|
||
fi
|
||
|
||
# Publish only after manifest validation so the picker cannot offer incomplete assets.
|
||
- name: Publish the verified release
|
||
id: publish_live
|
||
if: steps.freshness.outputs.should_build == 'true'
|
||
shell: bash
|
||
env:
|
||
GH_TOKEN: ${{ steps.app_token_final.outputs.token }}
|
||
TAG: ${{ steps.release.outputs.tag }}
|
||
NAME: ${{ steps.daily.outputs.name }}
|
||
NOTES_FILE: ${{ steps.release.outputs.notes_file }}
|
||
run: |
|
||
set -euo pipefail
|
||
# Builder may rewrite title/body on upload; reassert both so the picker name and next run’s source-SHA freshness check stay correct.
|
||
gh release edit "$TAG" --repo "$DAILY_REPO" --draft=false --prerelease \
|
||
--title "$NAME" --notes-file "$NOTES_FILE"
|
||
echo "Published $TAG as \"$NAME\""
|
||
|
||
# Remove failed/cancelled stranded drafts, never releases already published by publish_live, including after prune failure. Outer timeout skips cleanup, so retain draft status until verified.
|
||
- name: Discard the draft release on failure
|
||
if: >-
|
||
(failure() || cancelled()) && steps.release.outputs.tag != '' &&
|
||
steps.publish_live.outcome != 'success'
|
||
shell: bash
|
||
env:
|
||
# If token refresh fails, let cleanup try the older token while it may still be valid.
|
||
GH_TOKEN: ${{ steps.app_token_final.outputs.token || steps.app_token_publish.outputs.token }}
|
||
TAG: ${{ steps.release.outputs.tag }}
|
||
run: |
|
||
set -uo pipefail
|
||
# No --cleanup-tag: an unpublished draft never created a git tag.
|
||
echo "Run failed before publish; discarding draft $TAG"
|
||
gh release delete "$TAG" --repo "$DAILY_REPO" --yes ||
|
||
echo "::warning::Could not discard draft $TAG; remove it manually."
|
||
|
||
- name: Prune old daily releases
|
||
# Prune only after publication; failed runs leave draft disposal to cleanup.
|
||
if: steps.publish_live.outcome == 'success'
|
||
shell: bash
|
||
env:
|
||
GH_TOKEN: ${{ steps.app_token_final.outputs.token }}
|
||
# Never prune this run’s just-published tag at the retention cap.
|
||
TAG: ${{ steps.release.outputs.tag }}
|
||
run: |
|
||
set -euo pipefail
|
||
# Delete pruned tags too; exclude drafts owned by failure cleanup. Rank by publishedAt/tagName:
|
||
# createdAt ties (bulk import/recreation) can evict the just-published release. Partition $TAG to
|
||
# the front before slicing so it owns exactly one retained seat without under-pruning or keeping retain+1.
|
||
jq_filter='map(select(.isDraft | not)) | sort_by(.publishedAt // "", .tagName) | reverse'
|
||
if [[ -n "${TAG:-}" ]]; then
|
||
jq_filter+=" | (map(select(.tagName == \"${TAG//\"/\\\"}\")) + map(select(.tagName != \"${TAG//\"/\\\"}\")))"
|
||
fi
|
||
jq_filter+=" | .[${DAILY_RETAIN_COUNT}:] | .[].tagName"
|
||
stale="$(gh release list --repo "$DAILY_REPO" --limit 200 --json tagName,publishedAt,isDraft \
|
||
--jq "$jq_filter")"
|
||
if [[ -z "$stale" ]]; then
|
||
echo "Nothing to prune; at or under $DAILY_RETAIN_COUNT retained builds."
|
||
exit 0
|
||
fi
|
||
while read -r tag; do
|
||
[[ -n "$tag" ]] || continue
|
||
# Belt-and-suspenders: partition above should already exclude $TAG.
|
||
if [[ -n "${TAG:-}" && "$tag" == "$TAG" ]]; then
|
||
echo "::warning::Prune list still included just-published $tag after protect; skipping delete."
|
||
continue
|
||
fi
|
||
echo "Pruning $tag"
|
||
gh release delete "$tag" --repo "$DAILY_REPO" --yes --cleanup-tag || \
|
||
echo "::warning::Could not prune $tag"
|
||
done <<<"$stale"
|
||
|
||
# Dispatch E2E separately so main’s red full suite cannot fail an already-published daily. Use main’s workflow plus exact built SHA because daily tags live in another repo.
|
||
post-daily-e2e:
|
||
needs: build-daily-mac
|
||
if: ${{ needs.build-daily-mac.outputs.published == 'true' && needs.build-daily-mac.outputs.head_sha != '' }}
|
||
runs-on: ubuntu-latest
|
||
permissions:
|
||
actions: write
|
||
steps:
|
||
- name: Dispatch cut-scoped E2E
|
||
env:
|
||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||
SHA: ${{ needs.build-daily-mac.outputs.head_sha }}
|
||
run: |
|
||
for attempt in 1 2 3; do
|
||
if gh workflow run e2e.yml \
|
||
--repo "$GITHUB_REPOSITORY" \
|
||
--ref main \
|
||
--raw-field "ref=$SHA"; then
|
||
echo "Dispatched post-daily E2E for $SHA."
|
||
exit 0
|
||
fi
|
||
[[ "$attempt" -eq 3 ]] || sleep "$((attempt * 5))"
|
||
done
|
||
echo "::warning::Failed to dispatch post-daily E2E for $SHA after 3 attempts."
|
||
|
||
# Wait for mac release identity before Windows uploads; local ./ workflow follows the dispatch ref consistently.
|
||
build-daily-win:
|
||
needs: build-daily-mac
|
||
# Start Windows publication only after the mac release is live; its workflow refuses to create a missing release.
|
||
if: needs.build-daily-mac.outputs.published == 'true'
|
||
permissions:
|
||
contents: read
|
||
actions: read
|
||
uses: ./.github/workflows/dev-channel-win-build.yml
|
||
secrets: inherit
|
||
with:
|
||
channel: daily
|
||
tag: ${{ needs.build-daily-mac.outputs.tag }}
|
||
ref: ${{ needs.build-daily-mac.outputs.head_sha }}
|
||
version: ${{ needs.build-daily-mac.outputs.version }}
|
||
orcad_template: true
|
||
fast_release: false
|