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
43 changes: 24 additions & 19 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -801,7 +801,7 @@ jobs:
if: github.event_name == 'workflow_dispatch' && inputs.promote_nightly_next_sha != '' && github.ref == 'refs/heads/main' && github.repository == 'manaflow-ai/cmux'
# Holds only the release App credentials (CMUX_RELEASE_APP_*).
environment: nightly-next-promote
runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || 'blacksmith-32vcpu-ubuntu-2404' }}
runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || vars.LINUX_RUNNER == 'blacksmith-16vcpu-ubuntu-2404' && 'blacksmith-16vcpu-ubuntu-2404' || 'blacksmith-32vcpu-ubuntu-2404' }}
timeout-minutes: 10
permissions:
contents: read
Expand Down Expand Up @@ -931,7 +931,7 @@ jobs:
# Holds only the release App credentials (CMUX_RELEASE_APP_*). The approval
# was given to release-surface-ack on this run; guard.approvedBy() reads it.
environment: nightly-next-promote
runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || 'blacksmith-32vcpu-ubuntu-2404' }}
runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || vars.LINUX_RUNNER == 'blacksmith-16vcpu-ubuntu-2404' && 'blacksmith-16vcpu-ubuntu-2404' || 'blacksmith-32vcpu-ubuntu-2404' }}
timeout-minutes: 10
permissions:
contents: read
Expand Down Expand Up @@ -1745,7 +1745,7 @@ jobs:
needs: [decide, resolve-nightly-cmux-tui-client]
if: ${{ !cancelled() && needs.resolve-nightly-cmux-tui-client.result == 'success' }}
continue-on-error: true
runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || 'blacksmith-32vcpu-ubuntu-2404' }}
runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || vars.LINUX_RUNNER == 'blacksmith-16vcpu-ubuntu-2404' && 'blacksmith-16vcpu-ubuntu-2404' || 'blacksmith-32vcpu-ubuntu-2404' }}
# A dispatch mints the routing token too, so the key is read in the routing environment, as
# the other dispatchable pickers do (iroh-release-gate.yml, test-ios.yml).
environment: ci-routing-agent
Expand Down Expand Up @@ -3283,9 +3283,14 @@ jobs:
BUILD_SHA: ${{ steps.build.outputs.build_sha }}
ARCHS: ${{ needs.decide.outputs.compile_archs }}
run: bash scripts/ci/nightly-steps/chief-input-key.sh
# Off unless NIGHTLY_NEXT_SUPERSEDE_SKIP is 1. NEXT runs are run-scoped (no shared slot), and the
# rulebook's Nightly freshness has a newer tip build alongside a running one; publish-nightly
# still publishes only a build newer than the feed. With this on, every run from 04:28Z to
# 05:23Z on 2026-10-31 skipped its build: nightly-next moved (about every 6 minutes) before a
# run reached this step (12+ minutes on a slow Linux queue), so no run built at all.
- name: Leave the build slot to a newer nightly-next push
id: superseded
if: github.event_name == 'push' && github.ref == 'refs/heads/nightly-next'
if: github.event_name == 'push' && github.ref == 'refs/heads/nightly-next' && vars.NIGHTLY_NEXT_SUPERSEDE_SKIP == '1'
env:
GH_TOKEN: ${{ github.token }}
TIP_SHA: ${{ needs.decide.outputs.head_sha }}
Expand Down Expand Up @@ -3939,7 +3944,7 @@ jobs:
# A failed pick reads as success to the signing jobs, whose runs-on then falls back to
# vars.MACOS_RUNNER_26: a pick never blocks signing. It holds a key: Blacksmith Linux 32.
continue-on-error: true
runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || 'blacksmith-32vcpu-ubuntu-2404' }}
runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || vars.LINUX_RUNNER == 'blacksmith-16vcpu-ubuntu-2404' && 'blacksmith-16vcpu-ubuntu-2404' || 'blacksmith-32vcpu-ubuntu-2404' }}
# The owned pick mints the routing token (GLAEDA_ROUTE_APP_KEY), so the key is read in the
# routing environment, as pick-nightly-build-runner does (tests/test_supplychain_security.py).
environment: ci-routing-agent
Expand Down Expand Up @@ -5270,7 +5275,7 @@ jobs:
environment: ${{ needs.decide.outputs.environment }}
needs: [decide, build-nightly-app, resolve-nightly-cmux-tui-client, prepare-nightly-variant, signer-build]
if: needs.resolve-nightly-cmux-tui-client.outputs.already_published != 'true' && needs.resolve-nightly-cmux-tui-client.outputs.superseded != 'true'
runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || 'blacksmith-32vcpu-ubuntu-2404' }}
runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || vars.LINUX_RUNNER == 'blacksmith-16vcpu-ubuntu-2404' && 'blacksmith-16vcpu-ubuntu-2404' || 'blacksmith-32vcpu-ubuntu-2404' }}
timeout-minutes: 75
permissions:
contents: read
Expand Down Expand Up @@ -5359,7 +5364,7 @@ jobs:
signer-app-notary:
environment: ${{ needs.decide.outputs.environment }}
needs: [decide, resolve-nightly-cmux-tui-client, signer-build, signer-app]
runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || 'blacksmith-32vcpu-ubuntu-2404' }}
runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || vars.LINUX_RUNNER == 'blacksmith-16vcpu-ubuntu-2404' && 'blacksmith-16vcpu-ubuntu-2404' || 'blacksmith-32vcpu-ubuntu-2404' }}
timeout-minutes: 40
permissions:
contents: read
Expand Down Expand Up @@ -5476,7 +5481,7 @@ jobs:
signer-dmg:
environment: ${{ needs.decide.outputs.environment }}
needs: [decide, build-nightly-app, resolve-nightly-cmux-tui-client, signer-build, signer-app, signer-app-notary, signer-dmg-image]
runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || 'blacksmith-32vcpu-ubuntu-2404' }}
runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || vars.LINUX_RUNNER == 'blacksmith-16vcpu-ubuntu-2404' && 'blacksmith-16vcpu-ubuntu-2404' || 'blacksmith-32vcpu-ubuntu-2404' }}
timeout-minutes: 40
permissions:
contents: read
Expand Down Expand Up @@ -5787,7 +5792,7 @@ jobs:
# with no idle runner; only `enforce` with a failing verdict holds this run's build.
route-visual-gate-nightly:
needs: [decide, resolve-nightly-cmux-tui-client]
if: github.repository == 'manaflow-ai/cmux' && needs.decide.outputs.track == 'nightly-next' && needs.decide.outputs.should_build == 'true' && needs.decide.outputs.fast_build != 'true' && needs.decide.outputs.build_only != 'true' && needs.decide.outputs.continue_only != 'true' && needs.decide.outputs.no_publish != 'true' && needs.resolve-nightly-cmux-tui-client.outputs.already_published != 'true' && needs.resolve-nightly-cmux-tui-client.outputs.superseded != 'true' && vars.NIGHTLY_NEXT_VISUAL_GATE != 'off'
if: github.repository == 'manaflow-ai/cmux' && needs.decide.outputs.track == 'nightly-next' && needs.decide.outputs.should_build == 'true' && needs.decide.outputs.fast_build != 'true' && needs.decide.outputs.build_only != 'true' && needs.decide.outputs.continue_only != 'true' && needs.decide.outputs.no_publish != 'true' && needs.resolve-nightly-cmux-tui-client.outputs.already_published != 'true' && needs.resolve-nightly-cmux-tui-client.outputs.superseded != 'true' && vars.NIGHTLY_NEXT_VISUAL_GATE != '' && vars.NIGHTLY_NEXT_VISUAL_GATE != 'off'
runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || vars.LINUX_RUNNER == 'blacksmith-16vcpu-ubuntu-2404' && 'blacksmith-16vcpu-ubuntu-2404' || 'blacksmith-32vcpu-ubuntu-2404' }}
timeout-minutes: 5
# GLAEDA_ROUTE_APP_KEY lives in the routing environment, like every other route job's.
Expand Down Expand Up @@ -6100,7 +6105,7 @@ jobs:
# failing verdict, or mode `invalid`. A recovered build was judged when it was continued.
if: "always() && !cancelled() && needs.decide.result == 'success' && needs.decide.outputs.should_build == 'true' && needs.decide.outputs.fast_build != 'true' && needs.decide.outputs.build_only != 'true' && (github.event_name != 'schedule' || github.event.schedule == '47 8 * * *') && needs.resolve-nightly-cmux-tui-client.outputs.already_published != 'true' && needs.decide.outputs.no_publish != 'true' && (((needs.build-sign-notarize-nightly.result == 'success' || needs.signer-dmg.result == 'success') && needs.build-sign-notarize-nightly.outputs.notary_pending != 'true' && (needs.signer-dmg.result == 'success' || needs.verify-signed-nightly.result == 'success') && needs.verify-final-dmg-nightly.result == 'success' && needs.visual-gate-nightly-verdict.outputs.blocks != 'true' && needs.route-visual-gate-nightly.outputs.mode != 'invalid') || (needs.recover-nightly-next-notarization.outputs.accepted == 'true' && needs.verify-recovered-nightly-next.result == 'success'))"
# The release App key remains scoped to this protected environment.
runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || 'blacksmith-32vcpu-ubuntu-2404' }}
runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || vars.LINUX_RUNNER == 'blacksmith-16vcpu-ubuntu-2404' && 'blacksmith-16vcpu-ubuntu-2404' || 'blacksmith-32vcpu-ubuntu-2404' }}
timeout-minutes: 120
env:
NIGHTLY_LEGACY_VARIANT: ${{ needs.decide.outputs.legacy_variant }}
Expand Down Expand Up @@ -6586,7 +6591,7 @@ jobs:
environment: ${{ needs.decide.outputs.environment }}
permissions:
contents: write
runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || 'blacksmith-32vcpu-ubuntu-2404' }}
runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || vars.LINUX_RUNNER == 'blacksmith-16vcpu-ubuntu-2404' && 'blacksmith-16vcpu-ubuntu-2404' || 'blacksmith-32vcpu-ubuntu-2404' }}
timeout-minutes: 150
outputs:
release_id: ${{ steps.publish_assets.outputs.release_id }}
Expand Down Expand Up @@ -6708,7 +6713,7 @@ jobs:
cancel-in-progress: false
permissions:
contents: write
runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || 'blacksmith-32vcpu-ubuntu-2404' }}
runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || vars.LINUX_RUNNER == 'blacksmith-16vcpu-ubuntu-2404' && 'blacksmith-16vcpu-ubuntu-2404' || 'blacksmith-32vcpu-ubuntu-2404' }}
timeout-minutes: 90
env:
NIGHTLY_FEEDS: ${{ needs.decide.outputs.feeds }}
Expand Down Expand Up @@ -6810,7 +6815,7 @@ jobs:
permissions:
contents: read
actions: read
runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || 'blacksmith-32vcpu-ubuntu-2404' }}
runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || vars.LINUX_RUNNER == 'blacksmith-16vcpu-ubuntu-2404' && 'blacksmith-16vcpu-ubuntu-2404' || 'blacksmith-32vcpu-ubuntu-2404' }}
timeout-minutes: 75
env:
BUILD: ${{ needs.publish-nightly.outputs.build }}
Expand Down Expand Up @@ -6863,7 +6868,7 @@ jobs:
# A feed read error must not turn a run that published cmux NEXT red; the
# macOS job then does not start (it needs this job's success and a build).
continue-on-error: true
runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || 'blacksmith-32vcpu-ubuntu-2404' }}
runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || vars.LINUX_RUNNER == 'blacksmith-16vcpu-ubuntu-2404' && 'blacksmith-16vcpu-ubuntu-2404' || 'blacksmith-32vcpu-ubuntu-2404' }}
timeout-minutes: 65
permissions:
contents: read
Expand Down Expand Up @@ -7337,7 +7342,7 @@ jobs:
# failure) for the whole sign, verify and publish chain. Runs no bundle code
# and needs no Apple tool: Blacksmith Linux 32 vCPU (trusted with release
# secrets), never vars.LINUX_RUNNER, which may name an owned host.
runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || 'blacksmith-32vcpu-ubuntu-2404' }}
runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || vars.LINUX_RUNNER == 'blacksmith-16vcpu-ubuntu-2404' && 'blacksmith-16vcpu-ubuntu-2404' || 'blacksmith-32vcpu-ubuntu-2404' }}
timeout-minutes: 90
env:
NIGHTLY_BUILD: ${{ needs.legacy-identity-target.outputs.build }}
Expand Down Expand Up @@ -7720,7 +7725,7 @@ jobs:
concurrency:
group: release-${{ needs.decide.outputs.track == 'nightly-next' && 'deltas' || 'publish' }}-${{ needs.decide.outputs.release_tag }}
cancel-in-progress: false
runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || 'blacksmith-32vcpu-ubuntu-2404' }}
runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || vars.LINUX_RUNNER == 'blacksmith-16vcpu-ubuntu-2404' && 'blacksmith-16vcpu-ubuntu-2404' || 'blacksmith-32vcpu-ubuntu-2404' }}
# It holds the publish group, so a hang must not hold it for the default 6 h. nightly-next
# makes no gh call here (deltas and feed go to R2) and took at most 1.3 minutes in 56 runs;
# other tracks keep the default, as their release uploads may wait out the rate limit.
Expand Down Expand Up @@ -7944,7 +7949,7 @@ jobs:
if: "!cancelled() && needs.decide.outputs.should_publish == 'true' && needs.decide.outputs.track == 'nightly-next' && needs.decide.outputs.no_publish != 'true' && needs.publish-nightly.result == 'success' && needs.publish-nightly.outputs.already_published != 'true' && needs.publish-nightly.outputs.refused != 'true' && needs.publish-nightly.outputs.source != 'recovered'"
environment: content-signing
# The content-signing key remains scoped to this protected environment.
runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || 'blacksmith-32vcpu-ubuntu-2404' }}
runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || vars.LINUX_RUNNER == 'blacksmith-16vcpu-ubuntu-2404' && 'blacksmith-16vcpu-ubuntu-2404' || 'blacksmith-32vcpu-ubuntu-2404' }}
timeout-minutes: 70
permissions:
contents: read
Expand Down Expand Up @@ -8079,7 +8084,7 @@ jobs:
cancel-in-progress: false
if: "!cancelled() && needs.decide.outputs.track == 'nightly-next' && needs.publish-nightly.result == 'success' && needs.publish-nightly.outputs.already_published != 'true' && needs.publish-nightly.outputs.refused != 'true' && needs.sign-release-notes.result == 'success'"
environment: ${{ needs.decide.outputs.environment }}
runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || 'blacksmith-32vcpu-ubuntu-2404' }}
runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || vars.LINUX_RUNNER == 'blacksmith-16vcpu-ubuntu-2404' && 'blacksmith-16vcpu-ubuntu-2404' || 'blacksmith-32vcpu-ubuntu-2404' }}
timeout-minutes: 70
permissions:
contents: read
Expand Down Expand Up @@ -8128,7 +8133,7 @@ jobs:
needs: [decide, build-nightly-app, resolve-nightly-cmux-tui-client, publish-release-notes]
if: "!cancelled() && github.repository_owner == 'manaflow-ai' && needs.decide.outputs.track == 'nightly-next' && needs.publish-release-notes.result == 'success'"
continue-on-error: true
runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || 'blacksmith-32vcpu-ubuntu-2404' }}
runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || vars.LINUX_RUNNER == 'blacksmith-16vcpu-ubuntu-2404' && 'blacksmith-16vcpu-ubuntu-2404' || 'blacksmith-32vcpu-ubuntu-2404' }}
timeout-minutes: 90
# The private-terms secret lives only in this environment, which admits only
# the protected branches (main, feat-cmux-next, nightly-next).
Expand Down
26 changes: 25 additions & 1 deletion tests/test_ci_signing_keychain_is_deleted_before_bundle_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,16 @@
# `vars.CI_CLOUD_OVERFLOW_SAVED && '<blacksmith label>' ||`: the overflow failover,
# a boolean record choosing between two literal Blacksmith Linux labels.
OVERFLOW_FAILOVER = re.compile(r"vars\.CI_CLOUD_OVERFLOW_SAVED\s*&&\s*'blacksmith-\d+vcpu-ubuntu-2404'\s*\|\|\s*")
# `vars.NAME == '<label>' && '<label>' ||`: a variable that only chooses between literal labels.
# Its value is compared, never used as the label, so the job still runs on the literal it names.
LITERAL_SELECTOR = re.compile(
r"vars\.([A-Za-z0-9_]+)\s*==\s*'([^']*)'\s*&&\s*'([^']*)'\s*\|\|\s*", re.IGNORECASE)


def strip_literal_selectors(runs_on: str) -> str:
"""Drop `vars.X == 'L' && 'L' ||` choices whose label L is ephemeral; anything else stays for the checks."""
return LITERAL_SELECTOR.sub(
lambda m: "" if m.group(2) == m.group(3) and EPHEMERAL_LABEL.match(m.group(3)) else m.group(0), runs_on)


def commands(run: str) -> list[str]:
Expand Down Expand Up @@ -259,7 +269,7 @@ def check_job(name: str, job: dict, failures: list[str]) -> None:
"set CMUX_DEFER_LAUNCH_SMOKES: \"true\" and smoke in a job without secrets"
)
if SIGNING_SECRET_REFERENCE.search(json.dumps(job)):
runs_on = str(job.get("runs-on", ""))
runs_on = strip_literal_selectors(str(job.get("runs-on", "")))
if len(ANY_VARS.findall(runs_on)) != len(RUNNER_VARIABLE.findall(runs_on)):
failures.append(
f"{name}: holds a signing secret and reads a repository variable in runs-on in a form this "
Expand Down Expand Up @@ -441,8 +451,22 @@ def check_live_runner_variables(failures: list[str]) -> None:
)


def check_literal_selectors(failures: list[str]) -> None:
"""A selector is dropped only when it picks the literal it compares against, and that literal is ephemeral."""
cases = {
"${{ vars.LINUX_RUNNER == 'blacksmith-16vcpu-ubuntu-2404' && 'blacksmith-16vcpu-ubuntu-2404' || 'blacksmith-32vcpu-ubuntu-2404' }}":
"${{ 'blacksmith-32vcpu-ubuntu-2404' }}",
"${{ vars.LINUX_RUNNER == 'blacksmith-16vcpu-ubuntu-2404' && 'glaeda-std-xcode-26.6' || 'blacksmith-32vcpu-ubuntu-2404' }}": None,
"${{ vars.X == 'glaeda-std-xcode-26.6' && 'glaeda-std-xcode-26.6' || 'blacksmith-32vcpu-ubuntu-2404' }}": None,
}
for runs_on, expected in cases.items():
if strip_literal_selectors(runs_on) != (runs_on if expected is None else expected):
failures.append(f"strip_literal_selectors mishandles {runs_on}")


def main() -> int:
failures: list[str] = []
check_literal_selectors(failures)
check_live_runner_variables(failures)
signing_jobs = 0
for path in sorted(WORKFLOWS.glob("*.y*ml")):
Expand Down
10 changes: 7 additions & 3 deletions tests/test_nightly_legacy_publish_linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,13 @@ class Workflow(unittest.TestCase):
def test_publish_runs_on_blacksmith_linux(self) -> None:
body = job("publish-nightly-identity-legacy")
runs_on = re.search(r"^ runs-on: (.+)$", body, re.MULTILINE).group(1)
self.assertEqual(runs_on, "${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || 'blacksmith-32vcpu-ubuntu-2404' }}")
# It holds the Sparkle and R2 keys: never an owned host, never a Mac.
self.assertNotIn("LINUX_RUNNER", runs_on)
self.assertEqual(runs_on, "${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || "
"vars.LINUX_RUNNER == 'blacksmith-16vcpu-ubuntu-2404' && 'blacksmith-16vcpu-ubuntu-2404' || "
"'blacksmith-32vcpu-ubuntu-2404' }}")
# It holds the Sparkle and R2 keys: never an owned host, never a Mac. LINUX_RUNNER only
# chooses between two literal Blacksmith labels; its value is never the label.
self.assertNotRegex(runs_on, r"vars\.LINUX_RUNNER \|\|")
self.assertNotIn("&& vars.LINUX_RUNNER", runs_on)

def test_publish_uses_no_apple_tool(self) -> None:
body = job("publish-nightly-identity-legacy")
Expand Down
Loading