Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,12 @@ jobs:
uses: ./.github/actions/setup

# STT is the bundled whisper-stt-server (whisper.cpp with native DTW token
# timestamps); no VAD model is fetched here. The binary is built by
# build-whisper-stt.yml and staged below — without that step the installer
# ships without speech-to-text. See
# technical-documentation/architecture/transcription-and-captions.md.
# timestamps); no model is fetched here. The binary is built by
# build-whisper-stt.yml and staged below, from the run built from this
# commit's helper sources — without that step the installer ships without
# speech-to-text. See
# technical-documentation/architecture/transcription-and-captions.md, and
# technical-documentation/engineering/release-and-secrets.md for the run.
- name: Stage whisper-stt binaries
shell: bash
env:
Expand Down
48 changes: 42 additions & 6 deletions scripts/stage-whisper-stt.sh
Original file line number Diff line number Diff line change
Expand Up @@ -79,20 +79,56 @@ fi
TMP="$(mktemp -d)"
trap 'rm -rf "${TMP}"' EXIT

echo "Fetching ${ARTIFACT} from the latest successful build-whisper-stt run..."
# No run id: gh resolves the most recent run that published this artifact.
# Artifacts expire (retention-days in build-whisper-stt.yml), so a stale branch
# Which run: the most recent successful build of THIS commit's helper sources.
# Not simply the most recent artifact, which is whatever branch last pushed a
# helper change: on 2026-09-30 that was a PR branch built from main without the
# fix 2.0.0-rc.2 was cut for, and rc.1 had shipped whatever main last built.
# Sources are compared by git object id, so the release branch's cherry-pick of
# a change matches the run built from main.
SOURCES=(electron/native/whisper-stt scripts/build-whisper-stt.sh .github/workflows/build-whisper-stt.yml)
object_at() { # <commit> <path>: the path's git object id at that commit, from the API
gh api "repos/${REPO}/contents/$(dirname "$2")?ref=$1" --jq ".[] | select(.path == \"$2\") | .sha"
}
same_sources() { # <commit>: were the helper's sources there the ones checked out here?
local path
for path in "${SOURCES[@]}"; do
[ "$(object_at "$1" "${path}")" = "$(git rev-parse "HEAD:${path}")" ] || return 1
done
}
RUN_ID=""
while read -r id sha; do
if same_sources "${sha}"; then RUN_ID="${id}"; break; fi
done < <(gh run list --repo "${REPO}" --workflow build-whisper-stt.yml --status success \
--limit 50 --json databaseId,headSha --jq '.[] | "\(.databaseId) \(.headSha)"')
if [ -z "${RUN_ID}" ]; then
cat >&2 <<EOF

FATAL: no successful build-whisper-stt run was built from this commit's helper
sources (${SOURCES[*]}).

Run it on this branch or tag, wait for it, then re-run this build:

gh workflow run build-whisper-stt.yml --repo ${REPO} --ref <branch or tag>

Refusing to package a helper built from other sources than the ones this
release ships.
EOF
exit 1
fi

echo "Fetching ${ARTIFACT} from build-whisper-stt run ${RUN_ID} (same helper sources)..."
# Artifacts expire (retention-days in build-whisper-stt.yml), so an old commit
# can legitimately find nothing — say so in terms someone can act on.
if ! gh run download --repo "${REPO}" --name "${ARTIFACT}" --dir "${TMP}" 2>"${TMP}/err"; then
if ! gh run download "${RUN_ID}" --repo "${REPO}" --name "${ARTIFACT}" --dir "${TMP}" 2>"${TMP}/err"; then
cat "${TMP}/err" >&2
cat >&2 <<EOF

FATAL: could not fetch ${ARTIFACT}.

The binaries come from the "Build whisper-stt binaries" workflow, and its
artifacts expire. Re-run it against this branch, then re-run this build:
artifacts expire. Re-run it against this branch or tag, then re-run this build:

gh workflow run build-whisper-stt.yml --repo ${REPO}
gh workflow run build-whisper-stt.yml --repo ${REPO} --ref <branch or tag>

Refusing to package: the installer would ship with speech-to-text silently
dead (no transcription, no captions).
Expand Down
2 changes: 2 additions & 0 deletions technical-documentation/engineering/release-and-secrets.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ The workflow computes `X.Y.Z-rc.N`, migrates items from `Next Release` to the `v

The two workflows push their tags with different credentials, which is deliberate: `promote.yml` uses `GITHUB_TOKEN` (a tag is a ref, not a file change), while `prerelease.yml` pushes the RC tag with `OPENSCREEN_RELEASE_TOKEN`. A `GITHUB_TOKEN` tag push is answered with `remote: Internal Server Error` — a 500, not a 403 — by a tag ruleset that rejects the Actions token, and that failure took down the whole `v1.8.0-rc.1` cut, skipping the build trigger and the Discord announce with it.

**Which whisper helper a build ships.** `build.yml` does not compile `whisper-stt-server`: `scripts/stage-whisper-stt.sh` downloads it from a `build-whisper-stt.yml` run, which runs on the pushes that touch the helper. It takes the most recent successful run built from the **same helper sources** as the commit being packaged (the `electron/native/whisper-stt` tree, `scripts/build-whisper-stt.sh` and the workflow, compared by git object id), so the release branch's cherry-pick of a helper change matches the run built from `main`. No such run fails the build, with the command that makes one. Until 2026-09-30 it took the most recent artifact of any branch: `v2.0.0-rc.1` shipped whatever `main` had last built, and a PR branch pushed an hour later would have decided what rc.2 shipped. So after cherry-picking a helper change, let a `build-whisper-stt` run of those sources finish before dispatching `prerelease.yml`.

RC tags are signed and notarized exactly like stable ones. That keeps testers out of `xattr -rd com.apple.quarantine`, and exercises the whole credential path on every candidate instead of first proving it on the promotion build.

### Promote to stable
Expand Down
Loading