@@ -569,6 +569,9 @@ jobs:
569569 needs.promote.result == 'success' &&
570570 needs.trigger-upload.result == 'success' &&
571571 needs.promote.outputs.promoted == 'true'
572+ outputs :
573+ # The new app serves traffic; indexnow keys off this, not the later Trigger.dev promotion.
574+ cutover : ${{ steps.cutover.outcome }}
572575 # Mostly waiting on the ECS cutover after a dependency install: the smallest runner is enough.
573576 runs-on : *runner-2vcpu
574577 # Leave setup/promotion headroom above the 70-minute cutover poll.
@@ -596,6 +599,7 @@ jobs:
596599 # An unchanged tag may belong to a failed or still-running earlier deploy.
597600 # Verify its latest cutover rather than treating tag equality as success.
598601 - name : Wait for ECS traffic cutover
602+ id : cutover
599603 env :
600604 OVERALL_TIMEOUT : 4200
601605 APP_IMAGE_CHANGED : ${{ needs.promote.outputs.app_image_changed }}
@@ -636,6 +640,64 @@ jobs:
636640 fi
637641 bunx trigger.dev@4.5.16 promote "$VERSION" "${TARGET_ARGS[@]}"
638642
643+ # Production only, once the new app serves traffic: submit the library and comparison URLs this
644+ # push changed to IndexNow. Best effort: nothing depends on it, and a failure never fails the run.
645+ indexnow :
646+ name : indexnow
647+ needs : [promote, trigger-promote]
648+ if : >-
649+ !cancelled() &&
650+ github.event_name == 'push' &&
651+ github.ref == 'refs/heads/main' &&
652+ needs.promote.outputs.promoted == 'true' &&
653+ needs.trigger-promote.outputs.cutover == 'success'
654+ runs-on : *runner-2vcpu
655+ timeout-minutes : 5
656+ continue-on-error : true
657+ permissions :
658+ contents : read
659+ actions : read
660+ checks : read
661+ steps :
662+ - name : Checkout code
663+ uses : actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6
664+
665+ - name : Setup Bun
666+ uses : oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
667+ with :
668+ bun-version : 1.4.4
669+
670+ # Diff from the last earlier main commit whose `indexnow` check succeeded, not the previous
671+ # push: a push that never deployed, or whose submission failed, would otherwise drop its
672+ # changed URLs for good. The check-runs lookup asks for this one check per commit, so it
673+ # stays small and counts every attempt of a re-run; a lookup that errors counts as no
674+ # success. With no success in view, the base is the oldest run in view, so URLs that no
675+ # submission ever covered are sent now. The diff needs only the two trees, so that one
676+ # commit is fetched alone; an all-zero `before` (the branch's first push) has no base,
677+ # which the script skips.
678+ - name : Submit changed URLs to IndexNow
679+ env :
680+ GH_TOKEN : ${{ github.token }}
681+ PUSH_BEFORE : ${{ github.event.before }}
682+ run : |
683+ BASE_SHA=""
684+ OLDEST_SHA=""
685+ while read -r head_sha; do
686+ OLDEST_SHA="$head_sha"
687+ if succeeded="$(gh api "repos/$GITHUB_REPOSITORY/commits/$head_sha/check-runs?check_name=indexnow&filter=all" \
688+ --jq '[.check_runs[] | select(.conclusion == "success")] | length')" && [[ "$succeeded" -gt 0 ]]; then
689+ BASE_SHA="$head_sha"
690+ break
691+ fi
692+ done < <(gh api "repos/$GITHUB_REPOSITORY/actions/workflows/ci.yml/runs?branch=main&event=push&per_page=30" \
693+ --jq ".workflow_runs[] | select(.id < $GITHUB_RUN_ID) | .head_sha")
694+ BASE_SHA="${BASE_SHA:-${OLDEST_SHA:-$PUSH_BEFORE}}"
695+ echo "Submitting URLs changed in $BASE_SHA..$GITHUB_SHA"
696+ if [[ ! "$BASE_SHA" =~ ^0+$ ]]; then
697+ git fetch --no-tags --depth=1 origin "$BASE_SHA"
698+ fi
699+ bun scripts/ping-indexnow.ts "$BASE_SHA" "$GITHUB_SHA"
700+
639701 # Build ARM64 images for GHCR (main branch only, runs in parallel with
640702 # tests). Pushes only the immutable sha tag — latest-arm64/version-arm64
641703 # are applied by ghcr after the gate, so a failing run
0 commit comments