Skip to content

ci: run tests-job xcodebuild in the console GUI session (fix testmanagerd on self-hosted minis) - #6401

Merged
azooz2003-bit merged 2 commits into
mainfrom
feat-ci-tests-console-session
Jun 19, 2026
Merged

azooz2003-bit merged 2 commits into
mainfrom
feat-ci-tests-console-session

Conversation

@azooz2003-bit

@azooz2003-bit azooz2003-bit commented Jun 18, 2026 •

Copy link
Copy Markdown
Collaborator

Why

Diagnosis of the austin-mini testmanagerd failures: xcodebuild test needs testmanagerd's control service, which only exists in the console user's GUI (Aqua) login session bootstrap namespace. The tests job is the only macOS test job that runs xcodebuild with no session setup (unlike ui-regressions, which enables automation mode, and perf-activation, which launchctl asusers into the console session). So on a runner whose agent isn't itself in a logged-in GUI session, the control session never initiates (No such process, Timed out … initiating control session, Executed 0 tests).

What

  • New scripts/ci/run-in-console-session.sh: elevates a command into the logged-in console user's Aqua session via launchctl asuser. Guarded — if no real console user is logged in (/dev/console owned by root) or passwordless sudo is unavailable, it falls back to the current bootstrap, i.e. exactly today's behavior. It forwards only env vars that are actually set (so it can't blank a downstream ${VAR:-default}), and is a no-op on runners already in a session.
  • Wire it around the three app-host xcodebuild invocations in the tests job, and add the automation-mode enable step ui-regressions already uses.

Experiment value

The wrapper logs whether it found a logged-in console user, so the job output reports each runner's session state. If this job lands on an austin mini and the BrowserSystemProxyMirrorTests step (the one that was failing) now passes, that both fixes it and confirms a user is logged in. If it logs "No logged-in console user," that runner needs the machine-side fix (run the agent as the GUI user / log in and stay logged in).

Caveat

This rescues a runner where a console user is logged in but the agent runs outside that session. It cannot rescue a runner with no GUI login at all; that still needs the machine fix.

🤖 Generated with Claude Code


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Summary by cubic

Run macOS tests job xcodebuild steps in the console GUI session to restore testmanagerd control and stop 0-test runs on self-hosted minis. Also enable XCTest automation mode to match ui-regressions.

  • Bug Fixes
    • Add scripts/ci/run-in-console-session.sh to elevate commands into the logged-in console user via launchctl asuser; guarded fallback if no GUI login or no passwordless sudo; forwards only set env vars; logs session status.
    • Wrap all three app-host xcodebuild calls in tests with the new script.
    • Add an "Enable XCTest automation mode" step using automationmodetool; warn when missing or sudo not available.

Written for commit c1daa22. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Chores
    • Improved CI/CD test execution on macOS by enabling XCTest automation mode when supported, with non-blocking warnings when not available.
    • Updated macOS regression gates to run app hosting and simulator-related checks within the active logged-in console session to better match real user conditions.
    • Ensured unit test runs use the same console-session execution approach for more consistent behavior.

The `tests` job is the only macOS test job that runs xcodebuild test with no
session setup, unlike `ui-regressions` (enables automation mode) and
`perf-activation` (launchctl asuser into the console user's Aqua session). On a
self-hosted runner whose agent is not itself in a logged-in GUI session,
testmanagerd's control service is not in the runner's bootstrap namespace, so
xcodebuild times out initiating the control session and 0 tests run (the
austins-mac-mini failure).

Add scripts/ci/run-in-console-session.sh: it elevates a command into the
logged-in console user's Aqua session via `launchctl asuser`, guarded so it
falls back to the current bootstrap when no console user is logged in or
passwordless sudo is unavailable (never worse than today; a no-op on runners
already in a session). It also forwards only the env vars that are actually set,
so it can't blank out a downstream `${VAR:-default}`. Wire it around the three
app-host xcodebuild invocations in the `tests` job, and add the same
automation-mode enable step `ui-regressions` already uses.

The wrapper logs whether it found a logged-in console user, so the job output
now also reports each runner's session state.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 18, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Jun 19, 2026 12:07am

@coderabbitai

coderabbitai Bot commented Jun 18, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Introduces scripts/ci/run-in-console-session.sh, a new Bash wrapper that re-executes commands inside the logged-in macOS console user's GUI bootstrap via launchctl asuser and sudo, with environment forwarding and a fallback. The CI workflow wires this wrapper around xcodebuild invocations for unit tests and two regression gates, and adds a step to enable XCTest automation mode.

Changes

Console Session Wrapper and CI Wiring

Layer / File(s) Summary
run-in-console-session.sh implementation
scripts/ci/run-in-console-session.sh
New script that detects the logged-in console user from /dev/console, verifies passwordless sudo, builds an allowlist-based environment forwarding set, and re-executes the provided command via launchctl asuser + sudo -u inside GITHUB_WORKSPACE. Falls back to running in the current bootstrap with a warning when no suitable console user or sudo is available.
CI workflow: automation mode step and console-session wiring
.github/workflows/ci.yml
Adds a macOS step that conditionally enables XCTest automation mode via automationmodetool with passwordless sudo. Wraps the browser system proxy mirror regression, Option/Alt sided-modifier regression, and xcodebuild_noninteractive.py unit-test invocations with scripts/ci/run-in-console-session.sh.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • manaflow-ai/cmux#4940: Directly related — both PRs implement the same pattern of running xcodebuild commands inside the console user's GUI session via launchctl asuser, with automationmodetool and passwordless sudo; this PR generalizes that approach into the shared run-in-console-session.sh wrapper.
  • manaflow-ai/cmux#6289: Modifies the same tests job wiring for unit tests and regression gates; this PR wraps invocations in run-in-console-session.sh while the related PR sources environment variables from app-host-xctest-env.sh.

Poem

🐰 Hop into the console, no GUI delay,
launchctl asuser shows xcodebuild the way.
An allowlist of envs, forwarded with care,
Automation mode set—no timeouts to bear!
The CI now runs in the right session at last,
This bunny's proud patches are shipping fast. 🚀

🚥 Pre-merge checks | ✅ 21 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive The description covers the 'Why' and 'What' sections comprehensively, but is missing or incomplete on the required template sections for 'Testing', 'Demo Video', and the 'Checklist'. Add information about how the changes were tested locally, include testing verification details, and complete the checklist items to match the repository template.
✅ Passed checks (21 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: running xcodebuild in the console GUI session to fix testmanagerd issues on self-hosted minis.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Cmux Swift Actor Isolation ✅ Passed This PR introduces no Swift code changes. It only modifies a YAML workflow file (.github/workflows/ci.yml) and adds a Bash shell script (scripts/ci/run-in-console-session.sh). The custom check is s...
Cmux Swift Blocking Runtime ✅ Passed No Swift code was modified in this PR. Changes are limited to CI workflow configuration (.github/workflows/ci.yml) and a Bash shell script (scripts/ci/run-in-console-session.sh). The check is not a...
Cmux Expensive Synchronous Load ✅ Passed PR contains no Swift production code changes—only CI workflow YAML and Bash shell scripts. The check for expensive synchronous loaders on main actor is not applicable.
Cmux Cache Substitution Correctness ✅ Passed PR contains only CI workflow YAML and Bash shell script changes; no Swift/TypeScript/JavaScript production code is modified, so cache-substitution correctness check is not applicable.
Cmux No Hacky Sleeps ✅ Passed New shell script uses only deterministic mechanisms (stat, id, sudo checks, dscl queries, exec) with no sleep/polling/timing constructs. Workflow YAML changes are explicitly out of scope per the rule.
Cmux Algorithmic Complexity ✅ Passed Rule applies to production code with scalable user data; this PR adds only CI infrastructure (workflow/scripts) with fixed-size collections (11 hardcoded env vars), matching the "tiny fixed-size" p...
Cmux Swift Concurrency ✅ Passed No Swift code was modified in this PR; changes are only to CI workflow YAML and a bash script, making the Swift concurrency check inapplicable.
Cmux Swift @Concurrent ✅ Passed PR contains only CI workflow YAML and bash script changes; no Swift source code modifications, so @concurrent annotation check is not applicable.
Cmux Swift File And Package Boundaries ✅ Passed No Swift files are modified in this PR. Changes are limited to .github/workflows/ci.yml (YAML) and scripts/ci/run-in-console-session.sh (Bash), making the Swift file/package boundary check not appl...
Cmux Swiftpm Lockfiles ✅ Passed PR only modifies CI workflow and test execution scripts with no SwiftPM dependency, package, .gitignore, or Xcode project changes - rule about lockfile commits is not applicable.
Cmux Swift Logging ✅ Passed This PR contains no Swift source code changes—only YAML workflow config and a Bash shell script. The Swift logging check does not apply.
Cmux User-Facing Error Privacy ✅ Passed CI workflow changes contain only diagnostic warnings in operational infrastructure (GitHub Actions format), which are exempt from user-facing error privacy rules. No sensitive data, vendor names, o...
Cmux Full Internationalization ✅ Passed Changes are limited to CI automation scripts and workflow configuration—developer-only operational code with no user-facing text, Swift UI strings, or message catalogs modified.
Cmux Swiftui State Layout ✅ Passed PR contains no SwiftUI code; only CI workflow YAML and Bash script changes. The SwiftUI state layout check is not applicable to non-SwiftUI changes.
Cmux Architecture Rethink ✅ Passed PR modifies only CI/YAML and bash scripts, not Swift code. The architectural rule applies to Swift changes; no Swift files are modified here, and no forbidden patterns (sleeps, polling, locks, obse...
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR contains only CI workflow YAML and Bash script changes; no Swift code changes present, so the Swift auxiliary window close shortcuts check is not applicable.
Cmux Source Artifacts ✅ Passed Both changed files (.github/workflows/ci.yml and scripts/ci/run-in-console-session.sh) are intentional source/config files with test-system purposes, not source control artifacts.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-ci-tests-console-session

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps

greptile-apps Bot commented Jun 18, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes testmanagerd control-session failures on self-hosted macOS mini runners by introducing scripts/ci/run-in-console-session.sh, which elevates the three app-host xcodebuild calls in the tests job into the console user's Aqua GUI session via launchctl asuser; it also adds the automation-mode enable step that ui-regressions already uses.

  • New script run-in-console-session.sh uses a double-sudo pattern (sudo launchctl asuser <uid> sudo -u <user>) to switch both the bootstrap namespace and the user identity; it safely falls back to the current bootstrap when no console user is logged in or passwordless sudo is unavailable.
  • CI wiring wraps all three xcodebuild invocations in tests and adds automationmodetool enable-automationmode-without-authentication with a graceful warn-and-continue fallback.
  • Observability built in: the script logs whether it found a logged-in console user, so each run self-reports its runner's session state.

Confidence Score: 5/5

Safe to merge; the fallback path ensures this change can never make a runner worse than it is today.

The elevation logic is guarded: no console user or no passwordless sudo means the command runs exactly as before. The three previously-flagged issues (warning annotation on stderr, HOME fallback on dscl failure, sudo -E SETENV requirement) are all in the fallback/diagnostic path and do not break the happy path. The only new gap found is two lock-tuning env vars omitted from the forward list, which only matters if those vars are explicitly set in CI — they are not currently set in the workflow.

scripts/ci/run-in-console-session.sh — the env forward list and the diagnostic warning path are the areas most likely to need follow-up.

Important Files Changed

Filename Overview
scripts/ci/run-in-console-session.sh New helper that elevates commands into the console user's Aqua session via double-sudo + launchctl asuser; guarded fallback is sound, but the warning annotation goes to stderr so it won't render as a GHA annotation, the HOME fallback on dscl failure points to the runner's HOME rather than /Users/$console_user, sudo -E requires SETENV in sudoers, and the env forward list omits a couple of lock-tuning vars.
.github/workflows/ci.yml Adds automationmodetool enable step and wraps the three app-host xcodebuild invocations with run-in-console-session.sh; wiring is correct and the automation-mode step gracefully warns rather than failing when tooling or sudo is unavailable.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["CI tests job step\nruns run-in-console-session.sh CMD"] --> B{console user\nlogged in AND\npasswordless sudo?}
    B -- No --> C["warn to stderr\nexec CMD directly\n(current bootstrap)"]
    B -- Yes --> D["resolve console_uid\nbuild env_pairs"]
    D --> E["sudo -n launchctl asuser console_uid\n  sudo -n -u console_user -E\n    env HOME=console_home …\n      bash -c 'cd GITHUB_WORKSPACE && exec CMD'"]
    E --> F["CMD runs in\nconsole user's\nAqua GUI bootstrap"]
    F --> G{run-app-host-xcodebuild.sh\nor xcodebuild_noninteractive.py}
    G --> H["flock per-machine lock\n(app_host_test_lock.py)"]
    H --> I["xcodebuild test\ntestmanagerd control session\nreachable ✓"]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A["CI tests job step\nruns run-in-console-session.sh CMD"] --> B{console user\nlogged in AND\npasswordless sudo?}
    B -- No --> C["warn to stderr\nexec CMD directly\n(current bootstrap)"]
    B -- Yes --> D["resolve console_uid\nbuild env_pairs"]
    D --> E["sudo -n launchctl asuser console_uid\n  sudo -n -u console_user -E\n    env HOME=console_home …\n      bash -c 'cd GITHUB_WORKSPACE && exec CMD'"]
    E --> F["CMD runs in\nconsole user's\nAqua GUI bootstrap"]
    F --> G{run-app-host-xcodebuild.sh\nor xcodebuild_noninteractive.py}
    G --> H["flock per-machine lock\n(app_host_test_lock.py)"]
    H --> I["xcodebuild test\ntestmanagerd control session\nreachable ✓"]
Loading

Reviews (2): Last reviewed commit: "Merge origin/main into feat-ci-tests-con..." | Re-trigger Greptile

bash -c 'cd "$GITHUB_WORKSPACE" && exec "$@"' bash "$@"
fi

echo "::warning::No logged-in console user (or no passwordless sudo) on this runner; running in the current bootstrap. XCTest will fail here if this runner has no GUI session." >&2

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The ::warning:: workflow command is sent to stderr (>&2), but GitHub Actions processes ::workflow-command:: annotations from stdout only. This is the most important diagnostic message in the script — it tells operators their runner has no GUI session — but it will appear only in the raw log and not as a visible job annotation. The success-path log on line 53 is correctly stderr (it's not a workflow command), but this one should go to stdout.

Suggested change
echo "::warning::No logged-in console user (or no passwordless sudo) on this runner; running in the current bootstrap. XCTest will fail here if this runner has no GUI session." >&2
echo "::warning::No logged-in console user (or no passwordless sudo) on this runner; running in the current bootstrap. XCTest will fail here if this runner has no GUI session."

if [ -n "$console_user" ] && [ "$console_user" != "root" ] \
&& console_uid="$(id -u "$console_user" 2>/dev/null)" && sudo -n true 2>/dev/null; then
console_home="$( (dscl . -read "/Users/$console_user" NFSHomeDirectory 2>/dev/null || true) | awk '{print $2}')"
[ -n "$console_home" ] || console_home="$HOME"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 When dscl fails to read NFSHomeDirectory, console_home falls back to $HOME, which is the runner agent's home directory — not the console user's home. The inner sudo -n -u "$console_user" then runs as the console user with HOME pointing to a directory that user may not own or be able to write to, which can cause Xcode tooling (~/Library/Developer, etc.) to fail. A safer default is /Users/$console_user, which is the canonical macOS home path for the user and is very unlikely to be wrong when the user does exist.

Suggested change
[ -n "$console_home" ] || console_home="$HOME"
[ -n "$console_home" ] || console_home="/Users/$console_user"

Comment on lines +54 to +56
exec sudo -n launchctl asuser "$console_uid" sudo -n -u "$console_user" -E \
env HOME="$console_home" "${env_pairs[@]}" \
bash -c 'cd "$GITHUB_WORKSPACE" && exec "$@"' bash "$@"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 sudo -E may fail if SETENV is not granted in sudoers

sudo -n -u "$console_user" -E requires the SETENV tag (or !env_reset / an env_keep allowlist) for the runner account in /etc/sudoers. On hardened CI images that have Defaults env_reset without an explicit SETENV rule, this flag causes sudo to print an error and exit non-zero, failing the step entirely. Since the important variables are already forwarded explicitly via env HOME=... "${env_pairs[@]}", dropping -E would make the command resilient to stricter sudoers configs. As-is, if a new austin-mini runner image has tighter sudoers, the fallback path is never reached — the step just errors out.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.github/workflows/ci.yml (1)

414-418: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Missing error handling for automationmodetool command failure.

If automationmodetool enable-automationmode-without-authentication returns non-zero (e.g., tool error, unsupported configuration), the step fails the job due to set -euo pipefail. The analogous code in test-e2e.yml handles this gracefully with || echo "::warning::...".

🛡️ Proposed fix to match test-e2e.yml behavior
   if sudo -n true 2>/dev/null; then
-    sudo -n automationmodetool enable-automationmode-without-authentication
+    sudo -n automationmodetool enable-automationmode-without-authentication \
+      || echo "::warning::Could not enable Automation Mode"
   else
     echo "::warning::Passwordless sudo unavailable; XCTest will use its default automation-mode setup"
   fi
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 414 - 418, The automationmodetool
enable-automationmode-without-authentication command lacks error handling and
will cause the job to fail if the command returns a non-zero exit code due to
the set -euo pipefail setting. Add error handling to the sudo command that runs
automationmodetool by appending a fallback pattern similar to what is used in
test-e2e.yml, so that when the automationmodetool command fails, it logs a
warning message instead of failing the entire step.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In @.github/workflows/ci.yml:
- Around line 414-418: The automationmodetool
enable-automationmode-without-authentication command lacks error handling and
will cause the job to fail if the command returns a non-zero exit code due to
the set -euo pipefail setting. Add error handling to the sudo command that runs
automationmodetool by appending a fallback pattern similar to what is used in
test-e2e.yml, so that when the automationmodetool command fails, it logs a
warning message instead of failing the entire step.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5f159ed0-58d2-4d8f-8248-b020f23d86fd

📥 Commits

Reviewing files that changed from the base of the PR and between 25a382d and c1daa22.

📒 Files selected for processing (1)
  • .github/workflows/ci.yml

@azooz2003-bit
azooz2003-bit merged commit bed31b8 into main Jun 19, 2026
20 of 21 checks passed
@azooz2003-bit
azooz2003-bit deleted the feat-ci-tests-console-session branch June 19, 2026 00:08

This branch was successfully deployed

1 active deployment
Preview – cmux — c1daa224 Deployed Jun 19, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant