Repository navigation
Isolate app-host tests from runner user config #9716
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 11 commits
Commits
Show all changes
82 commits
Select commit
Hold shift + click to select a range
81c6d8c
test: require isolated app-host user state
lawrencecchen 56aa78d
ci: isolate app-host user configuration
lawrencecchen 5beeaa3
test(ci): scope app-host isolation guard to jobs
lawrencecchen 945f370
test(ci): reject malformed isolation workflow
lawrencecchen 2ef782f
test: require app-host launch home isolation
lawrencecchen 8b8856f
test: reject app-host config path leaks
lawrencecchen c42f766
fix: isolate launched app-host user state
lawrencecchen 047ad6c
test: require nonempty app-host XDG default
lawrencecchen 1ba2484
fix: default app-host XDG path safely
lawrencecchen 14d0359
test: keep app-host redirects off xcodebuild
lawrencecchen 468bf90
fix: scope redirects to launched app host
lawrencecchen 695659d
test: require XCTest runner home isolation
lawrencecchen 552ae7b
fix: isolate XCTest app host through runner environment
lawrencecchen 3f2d757
test: close app-host isolation validation gaps
lawrencecchen ba91848
fix: harden app-host isolation validation
lawrencecchen 8250217
test: cover optional app-host isolation validation
lawrencecchen 883f2b9
fix: keep app-host isolation assertions opt in
lawrencecchen 56ea1f4
test: reject app-host XDG and scheme overrides
lawrencecchen 2ae885e
fix: bind app-host XDG to isolated home
lawrencecchen fad91f4
test: cover non-isolated macOS Bash wrapper
lawrencecchen 600a406
fix: support optional isolation on macOS Bash
lawrencecchen 234e73b
test: cover app-host child isolation boundaries
lawrencecchen 592a76a
fix: isolate app-host child process state
lawrencecchen 5022f30
test: require fail-closed app-host cleanup
lawrencecchen b1491f4
fix: fail closed and clean app-host homes
lawrencecchen 0a9de06
test: require cleanup after XDG removal
lawrencecchen 42e5fd2
fix: clean homes after XDG removal
lawrencecchen a164928
Merge remote-tracking branch 'origin/main' into fix-app-host-user-con…
lawrencecchen 50beb70
style: restore process helper indentation
lawrencecchen bbb381a
test: require robust app-host teardown discovery
lawrencecchen 879598a
fix: make app-host teardown robust
lawrencecchen 33e9656
test: preserve long-path app-host fixture
lawrencecchen e095c75
test: keep app-host isolation out of driver environment
lawrencecchen 991d9ec
fix: isolate app-host redirects at launch boundary
lawrencecchen b17616b
Merge remote-tracking branch 'origin/main' into fix-app-host-user-con…
lawrencecchen 8718178
test: reject ambient Ghostty configuration reads
lawrencecchen 6a882f9
fix: validate every Ghostty config path
lawrencecchen 4dd956f
test: clean mutated app-host XDG entries
lawrencecchen 9684a02
fix: clean mutated app-host XDG entries
lawrencecchen c414754
Merge remote-tracking branch 'origin/main' into fix-app-host-user-con…
lawrencecchen 9999aa7
test: require durable app-host isolation evidence
lawrencecchen d6efb15
fix: require durable app-host isolation evidence
lawrencecchen b8ab901
test: enforce short symlink-safe app-host homes
lawrencecchen df068f0
fix: use short symlink-safe app-host homes
lawrencecchen 024c139
Merge remote-tracking branch 'origin/main' into fix-app-host-user-con…
lawrencecchen 6faea3b
test: exercise app-host home symlink guard
lawrencecchen 158a1b5
test: require app-host identity receipts
lawrencecchen 471ce8a
fix: bind app-host cleanup to run identity
lawrencecchen 82125af
Merge remote-tracking branch 'origin/main' into fix-app-host-user-con…
lawrencecchen 8e830f7
Merge remote-tracking branch 'origin/main' into fix-app-host-user-con…
lawrencecchen d605f32
test: keep app-host receipts across CI jobs
lawrencecchen 5a59e8c
fix: persist app-host process authority
lawrencecchen ca1ac9e
Merge remote-tracking branch 'origin/main' into fix-app-host-user-con…
lawrencecchen 687f65a
test: inject lsof into app-host retry guard
lawrencecchen c221490
test: preserve existing app-host authority scope
lawrencecchen e083962
fix: preserve existing app-host authority scope
lawrencecchen 226ef0e
test: model dead-pid lsof diagnostics
lawrencecchen accca0a
fix: classify dead-pid lsof diagnostics as stale
lawrencecchen 248c855
test: authenticate cleanup before ownership transfer
lawrencecchen f15dcb8
fix: authenticate app-host scope before ownership transfer
lawrencecchen a89bcc2
test: bind app-host receipts to process incarnation
lawrencecchen ef40365
fix: bind app-host receipts to process incarnation
lawrencecchen 2ca686f
test: make app-host owner fixture portable
lawrencecchen 4f94aad
test: require atomic app-host receipt publication
lawrencecchen 465d819
fix: publish app-host receipts atomically
lawrencecchen 9791ac1
test: cover app-host recovery review gaps
lawrencecchen b9be11c
fix: harden app-host recovery
lawrencecchen d29d25d
test: cover app-host ownership lifecycle gaps
lawrencecchen 74b7718
fix: scope app-host recovery to one run
lawrencecchen 406ed58
test: recover authenticated prior-run app hosts
lawrencecchen 3f7e9c5
test: cover multiple current app-host owners
lawrencecchen aced3f7
test: bound prior app-host recovery eligibility
lawrencecchen d7d98a9
test: model lsof machine descriptor fields
lawrencecchen 6609970
test: recover deleted prior app-host owners
lawrencecchen 8a97cb8
fix: recover authenticated prior app hosts
lawrencecchen cb159ca
test: model lsof access mode fields
lawrencecchen c978816
fix: verify lsof receipt access mode
lawrencecchen c6f1906
test: include lsof access mode in cleanup fixture
lawrencecchen 82b5c67
test: reject untrusted newest confirmations
lawrencecchen e2522e4
fix: authenticate newest app-host confirmations
lawrencecchen 4a290ce
test: cover app-host machine ownership boundaries
lawrencecchen 276101a
fix: bind app-host recovery to machine ownership
lawrencecchen File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,167 @@ | ||
| #!/usr/bin/env python3 | ||
| """Guard app-host XCTest against persistent console-user configuration.""" | ||
|
|
||
| from pathlib import Path | ||
| import xml.etree.ElementTree as ET | ||
|
|
||
| import yaml | ||
|
|
||
|
|
||
| ROOT = Path(__file__).resolve().parents[1] | ||
| WORKFLOW_PATH = ROOT / ".github/workflows/ci.yml" | ||
| WORKFLOW = yaml.safe_load(WORKFLOW_PATH.read_text(encoding="utf-8")) | ||
| CONSOLE_WRAPPER = (ROOT / "scripts/ci/run-in-console-session.sh").read_text( | ||
| encoding="utf-8" | ||
| ) | ||
| APP_HOST_WRAPPER = (ROOT / "scripts/ci/run-app-host-xcodebuild.sh").read_text( | ||
| encoding="utf-8" | ||
| ) | ||
| UNIT_SCHEME_PATH = ( | ||
| ROOT / "cmux.xcodeproj/xcshareddata/xcschemes/cmux-unit.xcscheme" | ||
| ) | ||
| PROJECT = (ROOT / "cmux.xcodeproj/project.pbxproj").read_text(encoding="utf-8") | ||
|
|
||
|
|
||
| def require(text: str, needle: str, context: str) -> None: | ||
| if needle not in text: | ||
| raise SystemExit(f"FAIL: {context} is missing {needle!r}") | ||
|
|
||
|
|
||
| def require_step(job_name: str, step_name: str) -> dict: | ||
| if not isinstance(WORKFLOW, dict): | ||
| raise SystemExit("FAIL: workflow must be a mapping") | ||
|
|
||
| jobs = WORKFLOW.get("jobs") | ||
| if not isinstance(jobs, dict): | ||
| raise SystemExit("FAIL: workflow jobs must be a mapping") | ||
|
|
||
| job = jobs.get(job_name) | ||
| if not isinstance(job, dict): | ||
| raise SystemExit(f"FAIL: workflow job {job_name!r} is missing") | ||
|
|
||
| steps = job.get("steps") | ||
| if not isinstance(steps, list): | ||
| raise SystemExit(f"FAIL: workflow job {job_name!r} steps must be a list") | ||
|
|
||
| matches = [] | ||
| for index, step in enumerate(steps): | ||
| if not isinstance(step, dict): | ||
| raise SystemExit( | ||
| f"FAIL: workflow job {job_name!r} step {index} must be a mapping" | ||
| ) | ||
| if step.get("name") == step_name: | ||
| matches.append(step) | ||
| if len(matches) != 1: | ||
| raise SystemExit( | ||
| f"FAIL: workflow job {job_name!r} must contain exactly one " | ||
| f"{step_name!r} step" | ||
| ) | ||
| return matches[0] | ||
|
|
||
|
|
||
| def main() -> int: | ||
| setup_step = require_step( | ||
| "app-host-unit-tests", "Prepare isolated app-host home" | ||
| ) | ||
| setup_run = setup_step.get("run") | ||
| if not isinstance(setup_run, str): | ||
| raise SystemExit("FAIL: isolated app-host setup step has no run script") | ||
|
|
||
| requirements = { | ||
| "per-shard app-host home": ( | ||
| "APP_HOST_HOME=\"${RUNNER_TEMP}/cmux-app-host-home-" | ||
| "${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}-shard-${{ matrix.shard }}\"" | ||
| ), | ||
| "Core Foundation home redirect": ( | ||
| 'echo "CFFIXED_USER_HOME=$APP_HOST_HOME" >> "$GITHUB_ENV"' | ||
| ), | ||
| "XDG configuration redirect": ( | ||
| 'echo "XDG_CONFIG_HOME=$APP_HOST_HOME/.config" >> "$GITHUB_ENV"' | ||
| ), | ||
| "console-user write access": 'chmod -R a+rwX "$APP_HOST_HOME"', | ||
| "real Cargo toolchain home": 'echo "CARGO_HOME=${HOME}/.cargo" >> "$GITHUB_ENV"', | ||
| "real rustup toolchain home": 'echo "RUSTUP_HOME=${HOME}/.rustup" >> "$GITHUB_ENV"', | ||
| } | ||
| for context, needle in requirements.items(): | ||
| require(setup_run, needle, context) | ||
|
|
||
| guard_step = require_step( | ||
| "workflow-guard-tests", "Validate app-host user configuration isolation" | ||
| ) | ||
| if guard_step.get("run") != "python3 tests/test_ci_app_host_home_isolation.py": | ||
| raise SystemExit("FAIL: workflow-guard-tests does not run this guard") | ||
|
|
||
| require( | ||
| CONSOLE_WRAPPER, | ||
| "CFFIXED_USER_HOME XDG_CONFIG_HOME CARGO_HOME RUSTUP_HOME", | ||
| "console-session environment forwarding", | ||
| ) | ||
| require( | ||
| CONSOLE_WRAPPER, | ||
| "unset SSH_AUTH_SOCK", | ||
| "ambient SSH agent removal", | ||
| ) | ||
| require( | ||
| CONSOLE_WRAPPER, | ||
| 'env HOME="$console_home"', | ||
| "console-session Unix home preservation", | ||
| ) | ||
|
|
||
| scheme_root = ET.parse(UNIT_SCHEME_PATH).getroot() | ||
| test_action = scheme_root.find("TestAction") | ||
| if test_action is None: | ||
| raise SystemExit("FAIL: cmux-unit scheme has no TestAction") | ||
| environment_variables = test_action.find("EnvironmentVariables") | ||
| if environment_variables is None: | ||
| raise SystemExit("FAIL: cmux-unit TestAction has no environment variables") | ||
| scheme_environment = { | ||
| item.get("key"): item.get("value") | ||
| for item in environment_variables.findall("EnvironmentVariable") | ||
| if item.get("isEnabled") == "YES" | ||
| } | ||
| expected_scheme_environment = { | ||
| "HOME": "$(CMUX_APP_HOST_HOME)", | ||
| "CFFIXED_USER_HOME": "$(CMUX_APP_HOST_HOME)", | ||
| "XDG_CONFIG_HOME": "$(CMUX_APP_HOST_XDG_CONFIG_HOME)", | ||
| "CMUX_APP_HOST_EXPECTED_HOME": "$(CMUX_APP_HOST_HOME)", | ||
| "CMUX_APP_HOST_EXPECTED_XDG_CONFIG_HOME": ( | ||
| "$(CMUX_APP_HOST_XDG_CONFIG_HOME)" | ||
| ), | ||
| } | ||
| for key, value in expected_scheme_environment.items(): | ||
| if scheme_environment.get(key) != value: | ||
| raise SystemExit( | ||
| f"FAIL: cmux-unit TestAction must set {key}={value}" | ||
| ) | ||
|
|
||
| for context, needle in { | ||
| "app-host HOME build-setting default": 'CMUX_APP_HOST_HOME = "$(HOME)";', | ||
| "app-host XDG build-setting default": ( | ||
| 'CMUX_APP_HOST_XDG_CONFIG_HOME = ' | ||
| '"$(XDG_CONFIG_HOME:default=$(HOME)/.config)";' | ||
| ), | ||
| }.items(): | ||
| if PROJECT.count(needle) < 2: | ||
| raise SystemExit( | ||
| f"FAIL: Debug and Release app targets need {context}: {needle}" | ||
| ) | ||
|
|
||
| for context, needle in { | ||
| "app-host HOME xcodebuild override": ( | ||
| '"CMUX_APP_HOST_HOME=$CFFIXED_USER_HOME"' | ||
| ), | ||
| "app-host XDG xcodebuild override": ( | ||
| '"CMUX_APP_HOST_XDG_CONFIG_HOME=$XDG_CONFIG_HOME"' | ||
| ), | ||
| "Ghostty app-support path validation": ( | ||
| "validate_app_host_config_paths" | ||
| ), | ||
| }.items(): | ||
| require(APP_HOST_WRAPPER, needle, context) | ||
|
|
||
| print("PASS: app-host XCTest receives an isolated launch home") | ||
| return 0 | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| raise SystemExit(main()) |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.