Claude Code GitHub Actions should modify code in one job, then hand a fixed commit to a separately controlled remote Mac job for Xcode builds and tests. Keep signing assets away from untrusted Pull Requests; use an isolated release runner and manual approval when distribution is required.
This guide is for developers who want Claude Code to process Issues or Pull Requests while a real macOS node validates iOS or macOS projects. It is also for DevOps engineers managing GitHub self-hosted runners, and security owners limiting access to source code, networks, keychains, and release credentials.
Last updated September 7, 2026. Documentation and workflow assumptions were checked against the official Claude Code Action, GitHub Actions Runner, and Apple Xcode references listed below.
Start with the two-job boundary
A common failure looks like this: Claude Code completes a code change, but the general-purpose Runner cannot execute Apple tooling. The mistake is treating code generation and Apple validation as one job.
Use this split instead:
| Job | Primary responsibility | Runner requirement | Credentials |
|---|---|---|---|
| Agent job | Read the task, modify files, and create a commit or patch | A trusted supported Runner | No signing certificate or release token |
| Mac validation job | Check out the exact revision, build, test, and return evidence | A labeled self-hosted Mac Runner | Only test credentials, if genuinely needed |
| Release job | Archive, sign, or publish an approved artifact | A separate protected Mac Runner | Restricted signing and publishing credentials |
The official Claude Code Action usage documentation describes how to invoke the Action from GitHub workflows and how to connect its output to later workflow steps. Treat the Action as an automation component, not as proof that the resulting Apple project builds. Read the Claude Code Action usage documentation before choosing event triggers and permissions.
A successful Agent job means that the requested change was processed. It does not mean that Xcode accepted the project, that a Simulator test passed, or that an archive is safe to publish.
Define the handoff as data
The handoff should contain, at minimum:
- The repository and branch context.
- The exact commit SHA produced by the Agent job.
- The intended build or test command.
- The requested validation scope.
- A link or artifact name for the Agent log.
Do not let the Mac job implicitly use “the latest branch state.” A later push, merge, or workflow modification can otherwise change what you validate.
Choose the route by execution scenario
Do not send every Claude Code task to macOS. Documentation edits, static analysis, dependency inspection, and ordinary source changes can remain on the Agent side. Route work to the remote Mac only when the Apple toolchain is part of the acceptance condition.
| Scenario | Route to Mac? | Minimum evidence | Stop condition |
|---|---|---|---|
| Markdown, configuration, or general source edit | Usually no | Agent diff and review result | Stop if the requested change exceeds the declared path |
| Apple project compilation | Yes | Fixed SHA, selected Xcode, command output, exit status | Stop on missing scheme, unresolved dependency, or tool mismatch |
| Simulator test | Yes | Device runtime, test result bundle, logs, exit status | Stop if the device cannot boot or the session becomes opaque |
| Archive or distribution | Only through protected flow | Archive record, signing identity, approval, artifact digest | Stop without approved environment access |
| External Pull Request | Restricted path only | Source trust decision and isolated logs | Stop before reaching signing or release assets |
Use labels to express capability, not trust. A label such as macos says that the node has a macOS environment. It does not say that the repository is trusted or that the node is safe for production credentials. GitHub documents how to apply labels to self-hosted runners in its Runner label guidance.
Runner groups add a second control. Limit which repositories can target the Mac group, then make the workflow request both the required label and the permitted group. GitHub's Runner group access documentation explains the access boundary. You still need repository review rules and branch protection.
First scenario: Issue and Pull Request code changes
The trigger source determines how much trust you can grant. An Issue from a maintainer-controlled repository is different from a Pull Request submitted from an external fork. A Pull Request can change application code, workflow files, scripts, dependency declarations, and build commands.
Keep Agent permissions narrow
Start with the smallest permission set needed for the Agent task. Review the Claude Code Action security guidance and GitHub's secure use reference for Actions. The exact permission fields belong in your repository workflow, not in a reusable template copied without review.
Your review should answer these questions:
- Can the job write to the repository, or should it only create a patch?
- Can it alter workflow files?
- Can it read private repository content?
- Can it access secrets?
- Can it make network requests beyond the intended service?
- What happens if the task text contains an instruction to expose a secret?
The Agent job should return a reviewable diff. If the task requires a commit, record its SHA. If it only creates a patch, apply that patch in a later controlled job and record the resulting tree state.
Make the Mac job consume, not improvise
The Mac job should not ask Claude Code to decide whether it may access a keychain. Its input should be a commit and a declared validation mode.
A simplified structure can look like this:
jobs:
agent:
runs-on: ubuntu-latest
outputs:
revision: ${{ steps.commit.outputs.sha }}
steps:
- name: Run Claude Code Action
uses: anthropics/claude-code-action@<reviewed-ref>
- name: Publish revision
id: commit
run: echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
mac-validation:
needs: agent
runs-on:
group: <mac-validation-group>
labels: [<macos-label>]
steps:
- name: Check out exact revision
uses: actions/checkout@<reviewed-ref>
with:
ref: ${{ needs.agent.outputs.revision }}
- name: Run project validation
run: ./ci/<project-validation-script>
The action references, paths, labels, and group names are placeholders. Pin and review them according to your repository policy. Do not copy a floating reference into a production workflow without checking the current official example and your organization's change-control process.
Second scenario: Xcode build verification
A remote Mac is useful here because the acceptance condition is not “the code looks plausible.” It is “the intended Xcode environment can process this exact revision.”
Apple's Xcode command-line tool reference is the source for the commands and options you choose. Your repository should define the workspace, project, scheme, destination, dependency behavior, and output location. Avoid embedding assumptions in the Runner registration name.
Use this validation sequence:
- Check out the commit SHA from the Agent job.
- Confirm the expected project or workspace exists.
- Confirm the intended scheme is available to command-line builds.
- Select or verify the Xcode environment required by the repository.
- Resolve dependencies using the project's documented method.
- Run
xcodebuildwith an explicit destination and result bundle path. - Capture the process exit status.
- Upload the result bundle and structured logs.
- Mark the job failed when the build or test command returns failure.
- Clean the workspace according to the trust level of the repository.
The Runner showing as online is not build evidence. A green shell step is not test evidence if the command's failure is swallowed. A result bundle without its command status is incomplete.
| Evidence item | What it proves | What it does not prove |
|---|---|---|
| Commit SHA | The tested revision is identifiable | The revision is safe to release |
| Xcode selection output | The job used an observable tool environment | Every developer uses the same environment |
xcodebuild exit status |
The command succeeded or failed | UI behavior is correct |
.xcresult bundle |
Test and diagnostic data was produced | A real device was tested |
| Uploaded logs | Reviewers can inspect the failure | Secrets were not printed unless redaction is verified |
When a build fails, return the structured failure to the review process. Do not let the Agent repeatedly delete caches, change dependency versions, or edit the Runner until the failure disappears. A cache cleanup can be a controlled retry; it must not become an unlimited repair loop.
Third scenario: Simulator and graphical tests
Command-line builds, Simulator tests, and graphical UI automation are separate operating conditions.
A command-line build may only need a reachable host, a valid checkout, and Xcode tools. A Simulator test additionally needs an available runtime and a bootable device definition. A graphical test may depend on a logged-in user session, window-server visibility, permissions, display behavior, and timing that are not represented by a Runner's online status.
Apple's Xcode testing automation documentation should be used to align the command and result handling with the test type.
For Simulator acceptance, record:
- The selected device and runtime identifier.
- Whether the device boot completed.
- The test command and destination.
- The result bundle path.
- The exit status.
- Any screenshots, diagnostics, or automation logs required by the project.
- The state after a network interruption or remote session disconnect.
A Simulator boot proves only that the selected simulated environment started. It does not replace physical-device coverage. It also does not prove that every UI automation flow can run unattended. If a test needs a graphical login session, document that dependency and fail closed when it is absent.
Fourth scenario: signing, archiving, and release
Default to unsigned builds and tests. A Claude Code job should not see signing certificates, provisioning profiles, keychain passwords, Team identifiers, or distribution tokens merely because the repository is an Apple project.
Archiving is a different trust tier from compilation. Apple documents signing and distribution behavior in its code signing and archive guidance. Use that documentation to verify the Xcode operation, but design the access boundary yourself.
| Trust level | Allowed task | Runner placement | Approval |
|---|---|---|---|
| Development | Unsigned build and unit tests | Shared validation group with repository restrictions | Normal review |
| Team integration | Simulator tests and limited integration checks | Dedicated team group | Maintainer approval |
| Production release | Archive, sign, and publish | Independent protected runner | Protected environment approval |
An external Pull Request must not enter the production signing path. The same restriction applies to a branch that can modify workflow files, scripts, or dependency declarations without review.
For a release path, require all of the following:
- A protected branch or equivalent reviewed source.
- A separate Runner group.
- A dedicated operating-system account.
- Secrets available only to the release job.
- An environment approval before signing.
- A read-only or tightly scoped input boundary.
- An artifact record linking source SHA, archive, and release decision.
If any condition is missing, return to unsigned validation. The cost of a delayed release is easier to contain than an Agent or untrusted script reaching production credentials.
Fifth scenario: multiple repositories sharing one Mac
A long-running Mac Runner is not a disposable container. It can retain source files, dependency caches, logs, user preferences, keychain state, and temporary artifacts after a job ends. GitHub describes the operational model and limitations of self-hosted runners in its self-hosted Runner reference.
Plan isolation by trust level:
- Use separate Runner groups for unrelated repositories.
- Use labels that describe capability, not authorization.
- Use separate operating-system accounts where practical.
- Give each job a controlled working directory.
- Remove the checkout and sensitive temporary files after completion.
- Record cleanup results rather than assuming cleanup succeeded.
- Restrict network access for validation jobs where the project permits it.
A shared dependency cache can improve repeatability only when its trust boundary is understood. A cache created by one repository must not become an input channel for another repository. If you cannot explain who can write, who can read, and when the cache is invalidated, disable it for the sensitive path.
Compare the two operating models before deployment:
| Model | Strength | Main risk | Suitable starting point |
|---|---|---|---|
| Long-lived Runner | Stable tools and persistent administration | Residual state and drift | Controlled team validation |
| Per-task isolation | Smaller residue window | More provisioning and recovery work | Sensitive or mixed-trust workloads |
| Dedicated release node | Clear credential boundary | Higher operational overhead | Signing and publishing only |
If you need a managed Mac that you can administer, restart, and isolate for a trial, review the MACCOME remote Mac options before committing a production workflow. The relevant question is not only whether the node is reachable. It is whether your team can prove source isolation, cleanup, and recovery.
Sixth scenario: restart, queue, and recovery
Restart recovery is part of the workflow design. GitHub's self-hosted Runner operations documentation covers the Runner model, while your runbook must define what happens to an active job when the host disappears.
Test these states:
- Stop or restart the Mac during a non-production validation job.
- Confirm the workflow records a failure or interruption rather than a false success.
- Confirm the Runner reconnects with the intended labels and group.
- Confirm the working directory is clean before the next job.
- Confirm queued work can select the node again.
- Re-run the same commit SHA.
- Compare the new result bundle with the failed attempt.
- Check that no credentials or source files remain in temporary locations.
- Record the recovery result and the operator action required.
Do not silently resume an unknown partial archive. Re-run from a known revision and known workspace state. If the project cannot tolerate a restart during a release operation, keep the release node isolated and require an operator to inspect its state before another attempt.
A practical acceptance record should include the workflow run identifier, repository, source SHA, Runner group, host identity, working directory, command, exit status, uploaded artifacts, restart time, and cleanup result. This evidence lets you distinguish an Apple tool failure from a host failure, routing failure, or credential failure.
Apply the decision conditions before launch
Use these branches rather than choosing one universal Runner design:
- If Claude Code only changes general source files, then keep the Agent job off the remote Mac and review the diff normally.
- If the project requires Xcode or Simulator validation, then route the fixed SHA to a labeled Mac validation Runner.
- If the source comes from an external Pull Request, then block signing assets and use a restricted validation path.
- If the workflow file or build script changed, then require review before allowing the job to target any sensitive Runner group.
- If the build needs distribution signing, then create a separate protected release job with environment approval.
- If the Mac cannot prove cleanup after a job, then stop sharing it across trust boundaries.
- If restart recovery has not been tested, then keep the node in development or team-integration use.
- If you cannot return a fixed commit, command, exit status, and result bundle, then do not call the validation successful.
This is also where a remote Mac Xcode build node can help during non-production testing: you get a real macOS environment to validate the routing, logs, workspace cleanup, and restart behavior before purchasing or dedicating hardware.
FAQ
Can Claude Code GitHub Actions use a self-hosted Mac Runner?
Yes, but the routing must be explicit. Use a Runner group and capability label, then keep the Agent and Mac jobs separate. The Mac job should consume a fixed commit SHA. It should not receive signing credentials unless the workflow has already passed the required trust and approval gates.
How do you trigger Xcode validation after an Agent change?
Publish the Agent-created commit as a job output, then use that value in the Mac job's checkout step. Run the repository's documented Xcode command with an explicit scheme, destination, and result bundle path. Upload the logs and preserve the command exit status so reviewers can distinguish a real test result from a successful shell wrapper.
Can an external Pull Request run safely on a remote Mac?
Only under a restricted design. Treat its code, workflow edits, and scripts as untrusted until reviewed. Use a separate Runner group without signing assets, release tokens, or unrelated repository access. If the workflow cannot enforce that boundary, do not route the Pull Request to the remote Mac.
How does Claude Code Action avoid iOS signing certificate access?
Keep signing material out of the Agent job and the ordinary validation Runner. Put archive and publishing steps in a separate protected job with a dedicated node, limited account, protected environment, and manual approval. Unsigned builds and tests should be the default, not an exception after a secret has already been exposed.
How should a self-hosted Mac Runner recover after reboot?
Verify host reachability, Runner reconnection, group and label membership, workspace cleanup, and queue selection. Then re-run a known commit and compare its result bundle with the interrupted attempt. If the workflow cannot show what happened before the reboot, treat the recovery as failed and keep the node out of release use.
Your current alternative may be a local Mac, a shared workstation, or a general Linux cloud host. A local machine can be unavailable during power, network, or maintenance events; a shared workstation leaves unclear workspace and credential residue; a Linux host cannot replace Xcode, Simulator, or Apple signing tools. A remote Mac from MACCOME gives you a controllable environment for a non-production trial, with the option to test restart recovery and permissions before choosing a longer rental period. Start with unsigned builds, prove the Agent-to-Mac handoff on your real project, and add a separate release node only after the evidence and approval boundaries hold.