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
216 changes: 216 additions & 0 deletions .github/ci-areas.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
# Declarative CI ownership table. Area include rules win over area neutral rules.
# macOS defaults to run so every new or unknown path receives native coverage.
# force_all is the exception: explicit neutral control-plane helpers override
# the broad scripts/ci/** rule so their dedicated lanes keep current behavior.
version: 1

force_all:
default: skip
include:
- ".github/workflows/ci.yml"
- ".github/ci-areas.yml"
- "scripts/ci/**"
- "tests/test_ci_change_areas.py"
neutral:
- "scripts/ci/persistent_mac_route.py"
- "scripts/ci/web_validation.py"
- "scripts/ci/app_host_test_products.py"
- "scripts/ci/compile-app-host-test-product.sh"
- "scripts/ci/product_input_identity.py"
- "scripts/ci/restore-app-host-test-product.sh"
- "scripts/ci/reuse_app_host_products.py"
- "scripts/ci/sanitize-xcode-source-packages-cache.py"

macos:
default: run
include:
- ".github/workflows/ci-macos.yml"
- "docs/cli-contract.md"
- "webviews/src/agent-session/**"
- "package.json"
- "bun.lock"
- "biome.json"
- "Resources/agent-session-react/**"
- "Resources/agent-session-solid/**"
- "skills/cmux-cua/**"
- "scripts/ci/app_host_test_products.py"
- "scripts/ci/compile-app-host-test-product.sh"
- "scripts/ci/product_input_identity.py"
- "scripts/ci/restore-app-host-test-product.sh"
- "scripts/ci/reuse_app_host_products.py"
- "scripts/ci/sanitize-xcode-source-packages-cache.py"
neutral:
- ".github/workflows/**"
- ".github/actionlint.yaml"
- "docs/**"
- "design/**"
- "plans/**"
- "ios/**"
- "web/**"
- "webviews/**"
- "cmux-tui/**"
- "README.md"
- "README.*.md"
- "**/CLAUDE.md"
- "**/AGENTS.md"
- "skills/**/*.md"
- "scripts/ci/persistent_mac_route.py"
- "scripts/ci/web_validation.py"
- "tests/test_app_bundle_license_compliance.sh"
- "tests/test_app_host_test_products.py"
- "tests/test_bash_integration_no_done_notifications.py"
- "tests/test_build_app_bundled_resources.sh"
- "tests/test_check_package_resolved_policy.py"
- "tests/test_ci_app_host_failure_census.py"
- "tests/test_ci_app_host_home_cleanup.sh"
- "tests/test_ci_app_host_home_isolation.py"
- "tests/test_ci_app_host_identity.sh"
- "tests/test_ci_app_host_pipe_capture.py"
- "tests/test_ci_app_host_processes.sh"
- "tests/test_ci_app_host_test_output.py"
- "tests/test_ci_app_host_xcodebuild_attempts.sh"
- "tests/test_ci_app_host_xcodebuild_retry.sh"
- "tests/test_ci_auxiliary_window_close_shortcuts.sh"
- "tests/test_ci_change_areas.py"
- "tests/test_ci_checkout_network_diagnostics.py"
- "tests/test_ci_cmux_unit_test_shard.py"
- "tests/test_ci_create_dmg_pinned.sh"
- "tests/test_ci_fetch_complexity_base.py"
- "tests/test_ci_ghosttykit_checksum_present.sh"
- "tests/test_ci_ghosttykit_checksum_verification.sh"
- "tests/test_ci_ghosttykit_download_stall.sh"
- "tests/test_ci_ghosttykit_release_check.sh"
- "tests/test_ci_linux_guard_routing.py"
- "tests/test_ci_merge_queue_required_checks.py"
- "tests/test_ci_nightly_prune_python_compat.sh"
- "tests/test_ci_nightly_tag_push_auth.sh"
- "tests/test_ci_nightly_xcode_selection.sh"
- "tests/test_ci_notification_semantics.py"
- "tests/test_ci_pbxproj_test_wiring.sh"
- "tests/test_ci_persistent_mac_compile.py"
- "tests/test_ci_product_publication.py"
- "tests/test_ci_prune_xcode_compilation_cache.py"
- "tests/test_ci_pull_request_caches_are_read_only.py"
- "tests/test_ci_r2_cache.sh"
- "tests/test_ci_r2_upload_python.sh"
- "tests/test_ci_release_build_archs.sh"
- "tests/test_ci_release_build_timeout.sh"
- "tests/test_ci_release_ios_screenshots_decoupled.sh"
- "tests/test_ci_release_sdk_lane.sh"
- "tests/test_ci_release_tunnel_identifiers.sh"
- "tests/test_ci_resolve_cmux_tui_client_commit.sh"
- "tests/test_ci_reusable_workflow_permissions.py"
- "tests/test_ci_run_and_capture.sh"
- "tests/test_ci_sanitize_xcode_source_packages_cache.py"
- "tests/test_ci_scheme_testaction_debug.sh"
- "tests/test_ci_select_package_tests.py"
- "tests/test_ci_self_hosted_guard.sh"
- "tests/test_ci_sidebar_lazy_layout_guard.py"
- "tests/test_ci_swift_warning_budget.sh"
- "tests/test_ci_test_compilation_cache_seed.sh"
- "tests/test_ci_testbox_broker_guard.py"
- "tests/test_ci_unit_test_spm_retry.sh"
- "tests/test_ci_virtual_display_lock.sh"
- "tests/test_ci_workload_profiles.py"
- "tests/test_ci_xcode_selection_fast_path.sh"
- "tests/test_ci_xcodebuild_noninteractive_helper.py"
- "tests/test_ci_xcodebuild_with_diagnostics.sh"
- "tests/test_cleanup_stale_runs.py"
- "tests/test_cloud_vm_skill_coverage.py"
- "tests/test_compress_markdown_viewer_assets.sh"
- "tests/test_dock_shortcut_routing_guard.py"
- "tests/test_docs_deploy_auth_guard.py"
- "tests/test_fetch_previous_nightly_dmgs.sh"
- "tests/test_finalize_sparkle_deltas.sh"
- "tests/test_ghostty_cli_helper_cache.sh"
- "tests/test_ghostty_zig_version_sync.sh"
- "tests/test_github_release_publication.py"
- "tests/test_install_zig_ci_download_resume.sh"
- "tests/test_install_zig_ci_no_sudo.sh"
- "tests/test_ios_appstore_lane_identity.py"
- "tests/test_ios_selected_test_execution.py"
- "tests/test_ios_testflight_external_distribution.py"
- "tests/test_ios_testflight_main_push_filter.py"
- "tests/test_ios_testflight_notes.py"
- "tests/test_ios_testflight_pro_distribution.py"
- "tests/test_issue_5164_starship_prompt_composition.py"
- "tests/test_lint_stored_dispatch_work_items.py"
- "tests/test_lint_xcstrings.py"
- "tests/test_local_build_cache_policy.py"
- "tests/test_local_build_cache_preflight.py"
- "tests/test_localizable_xcstrings_structure.py"
- "tests/test_localization_catalog.py"
- "tests/test_localize_changes.py"
- "tests/test_nightly_universal_build.sh"
- "tests/test_normalize_pbxproj.py"
- "tests/test_notarize_computer_use_helper.sh"
- "tests/test_notarize_nightly_dmg.sh"
- "tests/test_package_resolved_policy_remote_inputs.py"
- "tests/test_perf_activation_scrollback_sizing.py"
- "tests/test_preflight_trust.py"
- "tests/test_reload_local_backend_mode.sh"
- "tests/test_reload_shared_derived_data.sh"
- "tests/test_reuse_app_host_products.py"
- "tests/test_run_e2e.py"
- "tests/test_sparkle_build_monotonic_modes.sh"
- "tests/test_sparkle_generate_appcast_no_deltas.sh"
- "tests/test_start_cmux_profiling.sh"
- "tests/test_strip_release_bundle.sh"
- "tests/test_swift_testing_suite_timeout.py"
- "tests/test_tui_npm_package_artifact.py"
- "tests/test_tui_package_contract.py"
- "tests/test_web_complexity_trusted_workflow.py"
- "tests/test_write_sidebar_extension_point.py"

web:
default: skip
include:
- ".github/workflows/ci-web.yml"
- "web/**"
- "webviews/**"
- "Resources/agent-session-react/**"
- "Resources/agent-session-solid/**"
- "Resources/markdown-viewer/**"
- "config/**"
- "workers/**"
- "CHANGELOG.md"
- "package.json"
- "bun.lock"
- "biome.json"
- ".vercelignore"
- "vercel.json"
- "bunfig.toml"
- ".npmrc"
- ".github/workflows/web-validation.yml"
- "scripts/ci/web_validation.py"
- "tests/test_web_validation.py"
- "scripts/build-agent-session-web.sh"
- "scripts/build-webviews-app.sh"
- "scripts/check-webviews-react-compiler.mjs"

agent_session_web:
default: skip
include:
- ".github/workflows/ci-web.yml"
- "webviews/src/agent-session/**"
- "Resources/agent-session-react/**"
- "Resources/agent-session-solid/**"
- "package.json"
- "bun.lock"
- "webviews/package.json"
- "webviews/bun.lock"
- "scripts/build-agent-session-web.sh"
- "Resources/markdown-viewer/marked.min.js"

release_build:
default: run
neutral:
- "scripts/ci/app_host_test_products.py"
- "scripts/ci/compile-app-host-test-product.sh"
- "scripts/ci/product_input_identity.py"
- "scripts/ci/restore-app-host-test-product.sh"
- "scripts/ci/reuse_app_host_products.py"
- "scripts/ci/sanitize-xcode-source-packages-cache.py"
- "cmuxTests/**"
- "cmuxUITests/**"
- "Packages/*/*/Tests/**"
71 changes: 13 additions & 58 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,17 +110,10 @@ jobs:
}

if [ "$EVENT_NAME" = "pull_request" ] || [ "$EVENT_NAME" = "merge_group" ]; then
# Checkout depth 2 contains the synthetic merge commit and its
# base parent, even when base/head histories are shallow boundaries.
# The event's base SHA is where the pull request last synced. Once
# main moves on it is outside this checkout, and a diff from it
# would also count everything main gained since. The merge commit's
# first parent is the base it was actually built on.
if git rev-parse -q --verify "$MERGE_SHA^2" > /dev/null; then
BASE_SHA="$(git rev-parse "$MERGE_SHA^1")"
fi
# Keep both sides of renames visible to path-based guard routing.
# A guarded file moved into docs/ must still run its owning checks.
# Keep both sides of renames visible to path-based routing.
if ! git diff --no-renames --name-only "$BASE_SHA" "$MERGE_SHA" > /tmp/cmux-ci-changed-files.txt; then
echo "Could not compute PR diff; running all CI areas." >&2
emit_all_areas
Expand All @@ -133,41 +126,26 @@ jobs:
exit 0
fi

# A provenance-only PR gets the cheap Linux guard and avoids
# queuing the unrelated macOS/web/Go suites. Keep this exception
# narrow: an unrelated workflow edit or source/configuration file
# opts back into normal routing below. The workflow edit is allowed
# only when this PR also contains one of the explicit provenance
# inputs, so this guard's own wiring does not queue unrelated app
# suites.
ghosttykit_guard_only=true
has_ghosttykit_guard_file=false
ci_router_changed=false
ci_workflow_changed=false
while IFS= read -r changed_file; do
case "$changed_file" in
ghostty|scripts/download-prebuilt-ghosttykit.sh|scripts/validate-xcframework-archive.py|scripts/ghosttykit-checksums.txt|tests/test_ci_ghosttykit_release_check.sh)
has_ghosttykit_guard_file=true
;;
tests/test_ci_change_areas.py)
ci_router_changed=true
;;
scripts/ci/detect_ci_change_areas.py)
.github/workflows/ci.yml|.github/ci-areas.yml|tests/test_ci_change_areas.py|scripts/ci/detect_ci_change_areas.py)
ci_router_changed=true
ghosttykit_guard_only=false
;;
scripts/ci/app_host_test_products.py|scripts/ci/compile-app-host-test-product.sh|scripts/ci/product_input_identity.py|scripts/ci/restore-app-host-test-product.sh|scripts/ci/reuse_app_host_products.py|scripts/ci/sanitize-xcode-source-packages-cache.py|scripts/ci/persistent_mac_route.py|scripts/ci/web_validation.py)
# These have explicit owning lanes and names that cannot
# shadow Python stdlib imports used by the router.
# Known CI helpers have explicit ownership in the area table
# and names that cannot shadow Python stdlib imports.
ghosttykit_guard_only=false
;;
.github/workflows/ci.yml)
ci_workflow_changed=true
;;
scripts/ci/*)
# Unknown scripts/ci files fail open before invoking the
# PR-controlled Python detector. A new file here could
# otherwise shadow a stdlib import (for example subprocess.py).
# Unknown CI implementation files fail open before executing
# PR-controlled Python. This also prevents import shadowing.
ci_router_changed=true
ghosttykit_guard_only=false
;;
Expand All @@ -176,6 +154,7 @@ jobs:
;;
esac
done < /tmp/cmux-ci-changed-files.txt

if [ "$ghosttykit_guard_only" = true ] && [ "$has_ghosttykit_guard_file" = true ]; then
echo "GhosttyKit provenance-only PR; running the release guard."
{
Expand All @@ -187,45 +166,21 @@ jobs:
exit 0
fi

# This guard lives in workflow YAML, before the PR-editable detector
# runs. Router/workflow edits must fail open even if a PR breaks the
# detector's own self-change classification.
# This check runs from workflow YAML before PR-controlled routing
# code. Router/table/caller edits therefore fail open even when the
# candidate detector is malformed.
if [ "$ci_router_changed" = true ]; then
echo "CI router changed; running all CI areas."
emit_all_areas
exit 0
fi

# The detector is unchanged here, so it may judge a ci.yml edit: only
# an edit confined to Linux jobs skips macOS, and anything it cannot
# read runs every area.
workflow_base_args=()
if [ "$ci_workflow_changed" = true ]; then
if ! git show "$BASE_SHA:.github/workflows/ci.yml" > /tmp/cmux-ci-base-workflow.yml; then
echo "Could not read the base ci.yml; running all CI areas." >&2
emit_all_areas
exit 0
fi
workflow_base_args=(--ci-workflow-base /tmp/cmux-ci-base-workflow.yml)
fi

python3 scripts/ci/detect_ci_change_areas.py \
--event-name "$EVENT_NAME" \
--files-from /tmp/cmux-ci-changed-files.txt \
${workflow_base_args[@]+"${workflow_base_args[@]}"}
python3 scripts/ci/detect_ci_change_areas.py --event-name "$EVENT_NAME" --files-from /tmp/cmux-ci-changed-files.txt
exit 0
fi

python3 scripts/ci/detect_ci_change_areas.py \
--event-name "$EVENT_NAME" \
--base-sha "$BASE_SHA" \
--head-sha "$HEAD_SHA"
python3 scripts/ci/detect_ci_change_areas.py --event-name "$EVENT_NAME" --base-sha "$BASE_SHA" --head-sha "$HEAD_SHA"

# Compile admission runs on every routed push. The rest of the macOS suite
# runs on merge groups and dispatches always, and on pull requests unless
# the repository variable CI_PULL_REQUEST_SUITE is "compile-only". Under
# that policy the "full-ci" label opts one pull request back in. Labels are
# read live so a re-run sees a label added after the push.
- name: Choose the macOS suite for this run
id: suite
env:
Expand Down Expand Up @@ -254,7 +209,7 @@ jobs:
if [ -f /tmp/cmux-ci-changed-files.txt ]; then
while IFS= read -r path; do
case "$path" in
.github/workflows/ci.yml|scripts/ci/detect_ci_change_areas.py|scripts/ci/detect_linux_guard_changes.py|tests/test_ci_change_areas.py|tests/test_ci_linux_guard_routing.py)
.github/workflows/ci.yml|.github/ci-areas.yml|scripts/ci/detect_ci_change_areas.py|scripts/ci/detect_linux_guard_changes.py|tests/test_ci_change_areas.py|tests/test_ci_linux_guard_routing.py)
for route in linux_guard_tests linux_guard_history linux_guard_cli linux_guard_source ghosttykit_release; do
echo "$route=true" >> "$GITHUB_OUTPUT"
done
Expand Down
Loading
Loading