From 0f30917d004009a9378b718de4ff44ac05e24a28 Mon Sep 17 00:00:00 2001 From: "omegent-app[bot]" <306514130+omegent-app[bot]@users.noreply.github.com> Date: Thu, 6 Aug 2026 05:17:26 +0000 Subject: [PATCH] ci(fork): run Fork CI for fork/dev PRs and merges MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fork/dev had no CI path at all: fork-ci.yml listed only the rebased stack layers and the registered overlays as pull_request bases, and had no push trigger anywhere. That blocks both halves of the cutover — PRs into fork/dev could not satisfy a required check, and no run would ever exist for a merge SHA. Add fork/dev as a pull_request base, and add a push trigger for it. The push run matters because fork/dev is never rebased, so its merge commits are the release candidates: deployment promotes an exact green SHA and keys on a successful run of this workflow for that SHA. A green PR tip is not enough when the merge SHA differs. Push runs are keyed by SHA and exempt from cancel-in-progress. Cancelling one as superseded would leave that merge SHA permanently unapprovable for deployment, which is not a state the poller can recover from on its own. Extend the release-tip jobs the same way. deployment_scope and dispatch_mobile_releases were gated on workflow_dispatch + fork/integration, so after the cutover mobile releases would have stopped silently — nothing errors, EAS simply never gets dispatched again. Both now accept a fork/dev push as well. The previous-successful-CI lookup that feeds classification hardcoded fork/integration + workflow_dispatch. Left alone it would diff every fork/dev push against an unrelated tip and classify every component as changed, so it now follows the running branch and event. The mobile dispatch likewise targets the branch being validated instead of a fixed fork/integration ref, so the mobile workflows come from the same tip that passed. Both fork/integration paths are retained unchanged; the two tips coexist until fork/integration is retired. Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com> --- .github/workflows/fork-ci.yml | 53 ++++++++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/.github/workflows/fork-ci.yml b/.github/workflows/fork-ci.yml index 3f203892342..2e50722418f 100644 --- a/.github/workflows/fork-ci.yml +++ b/.github/workflows/fork-ci.yml @@ -36,15 +36,28 @@ on: - fork/tim - fork/candidates - fork/changes + # Stable contributor and release branch (docs/stable-dev-release-branch-handover.md). + - fork/dev # Registered integration overlays (same names as .github/pr-stack.json) - t3-discord/f7d37879-desktop-deeplinks - fork/discord - fork/vscode - fork/identity + # fork/dev is never rebased, so its merge commits are the release candidates. + # Deployment promotes an exact green SHA and keys on a successful run of this + # workflow for that SHA, so every merge must produce one — a green PR tip is + # not enough when the merge SHA differs. + push: + branches: + - fork/dev concurrency: - group: ci-${{ github.event.pull_request.number || inputs.checkout_ref || github.ref }} - cancel-in-progress: true + # Push runs are keyed by SHA and never cancelled: the conclusion of a push run + # is what approves that exact commit for deployment, so cancelling one as + # superseded would leave that merge SHA permanently unapprovable. + group: ci-${{ github.event.pull_request.number || inputs.checkout_ref || + (github.event_name == 'push' && github.sha) || github.ref }} + cancel-in-progress: ${{ github.event_name != 'push' }} jobs: check: @@ -181,7 +194,12 @@ jobs: deployment_scope: name: Classify Deployment Scope - if: github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/fork/integration' + # Two release tips during the cutover: the composed fork/integration tip + # (dispatched by the stack workflow) and fork/dev merge commits. Keep both + # until fork/integration is retired. + if: >- + (github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/fork/integration') || + (github.event_name == 'push' && github.ref == 'refs/heads/fork/dev') runs-on: ubuntu-24.04 timeout-minutes: 5 permissions: @@ -200,16 +218,22 @@ jobs: with: fetch-depth: 0 - - name: Find previous successful integration CI + - name: Find previous successful release-tip CI id: previous env: GH_TOKEN: ${{ github.token }} + # Compare against this branch's own history. Hardcoding + # fork/integration + workflow_dispatch here would make every fork/dev + # push diff against an unrelated tip and classify every component as + # changed. + SCOPE_BRANCH: ${{ github.ref_name }} + SCOPE_EVENT: ${{ github.event_name }} run: | previous_sha="$( gh api --method GET \ "repos/${GITHUB_REPOSITORY}/actions/workflows/fork-ci.yml/runs" \ - -f branch=fork/integration \ - -f event=workflow_dispatch \ + -f "branch=${SCOPE_BRANCH}" \ + -f "event=${SCOPE_EVENT}" \ -f status=success \ -f per_page=20 \ --jq ".workflow_runs | map(select(.head_sha != \"${GITHUB_SHA}\")) | first | .head_sha // \"\"" @@ -258,9 +282,9 @@ jobs: dispatch_mobile_releases: name: Dispatch Mobile Releases needs: [check, test, mobile_native_static_analysis, release_smoke, deployment_scope] - if: | - github.event_name == 'workflow_dispatch' && - github.ref == 'refs/heads/fork/integration' && + if: >- + ((github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/fork/integration') || + (github.event_name == 'push' && github.ref == 'refs/heads/fork/dev')) && needs.deployment_scope.outputs.mobile == 'true' runs-on: ubuntu-24.04 timeout-minutes: 5 @@ -268,22 +292,25 @@ jobs: actions: write contents: read steps: - - name: Dispatch exact integration SHA + - name: Dispatch exact release SHA env: GH_TOKEN: ${{ github.token }} + # Dispatch against the branch this run is validating so the mobile + # workflows come from the same tip, not a stale fork/integration copy. + RELEASE_REF: ${{ github.ref_name }} run: | # mode=auto, not update: an OTA alone never reaches a phone when the # native runtime changed, so the fingerprint decides between an update # and a TestFlight build. iOS only, because Android has no keystore. gh workflow run mobile-eas-production.yml \ --repo "$GITHUB_REPOSITORY" \ - --ref fork/integration \ + --ref "$RELEASE_REF" \ -f mode=auto \ -f platform=ios \ -f sha="$GITHUB_SHA" \ - -f message="Integration ${GITHUB_SHA}" + -f message="${RELEASE_REF} ${GITHUB_SHA}" gh workflow run mobile-eas-development.yml \ --repo "$GITHUB_REPOSITORY" \ - --ref fork/integration \ + --ref "$RELEASE_REF" \ -f platform=ios \ -f sha="$GITHUB_SHA"