Any CI with nomac run
From any CI system or script, nomac run runs one command on a fresh Mac of its own, then stops the Mac. The job itself stays on your CI's own machine.
On GitHub Actions and GitLab.com, the whole job can run on a NoMac Mac instead: see GitHub Actions and GitLab CI.
Add your API key as a secret
Make a key on the Keys page and store it as a secret variable named NOMAC_API_KEY. Anyone who can run a pipeline with it can use your NoMac account, and the Mac gets your NoMac secrets, so keep it away from pipelines that run code from forks.
Run the Mac step
The job needs Node 20 or later and the OpenSSH client (ssh, ssh-keygen): node:24 and cimg/node have both, slim and alpine images have no ssh. nomac run uploads the folder, runs the command there, prints its output as it arrives and exits with its code. --pull copies a result back, even when the command failed.
Bitbucket Pipelines
Add NOMAC_API_KEY as a secured repository or workspace variable.
image: node:24
pipelines:
default:
- step:
name: iOS tests
script:
- npx -y @nomac/cli run --pull build/tests.xcresult -- xcodebuild test -scheme App -destination 'platform=iOS Simulator,name=iPhone 17 Pro' -resultBundlePath build/tests.xcresult
artifacts:
- build/tests.xcresult/**CircleCI
Add NOMAC_API_KEY to the project's environment variables or a context.
version: 2.1
jobs:
ios-tests:
docker:
- image: cimg/node:lts
steps:
- checkout
# nomac run is quiet while it waits for a Mac; CircleCI stops a step silent for 10 minutes.
- run:
command: npx -y @nomac/cli run --pull build/tests.xcresult -- xcodebuild test -scheme App -destination 'platform=iOS Simulator,name=iPhone 17 Pro' -resultBundlePath build/tests.xcresult
no_output_timeout: 30m
- store_artifacts:
path: build/tests.xcresult
workflows:
ios:
jobs:
- ios-testsAzure Pipelines
Add NOMAC_API_KEY as a secret pipeline variable. Azure gives secrets to a step only through env.
pool:
vmImage: ubuntu-latest
steps:
- script: npx -y @nomac/cli run --pull build/tests.xcresult -- xcodebuild test -scheme App -destination 'platform=iOS Simulator,name=iPhone 17 Pro' -resultBundlePath build/tests.xcresult
displayName: iOS tests
env:
NOMAC_API_KEY: $(NOMAC_API_KEY)
- publish: build/tests.xcresult
artifact: tests
condition: always()Jenkins
Add the key as a Secret text credential with the ID nomac-api-key. The agent needs Node and OpenSSH.
pipeline {
agent any
environment {
NOMAC_API_KEY = credentials('nomac-api-key')
}
stages {
stage('iOS tests') {
steps {
sh "npx -y @nomac/cli run --pull build/tests.xcresult -- xcodebuild test -scheme App -destination 'platform=iOS Simulator,name=iPhone 17 Pro' -resultBundlePath build/tests.xcresult"
}
}
}
post {
always {
archiveArtifacts artifacts: 'build/tests.xcresult/**', allowEmptyArchive: true
}
}
}Buildkite
Add NOMAC_API_KEY as a Buildkite secret (agent 3.106 or later), or set it in your agent's environment hook. The agent needs Node and OpenSSH.
steps:
- label: "iOS tests"
command: npx -y @nomac/cli run --pull build/tests.xcresult -- xcodebuild test -scheme App -destination 'platform=iOS Simulator,name=iPhone 17 Pro' -resultBundlePath build/tests.xcresult
secrets: [NOMAC_API_KEY]
artifact_paths: "build/tests.xcresult/**/*"GitLab CI, from a Linux job
Use this on a self-managed GitLab, or to keep the job on a Linux runner; on GitLab.com the whole job can also run on a NoMac Mac. Add NOMAC_API_KEY as a masked CI/CD variable. Leave it unprotected if merge request pipelines need it: a protected variable reaches only protected branches and tags.
ios-tests:
image: node:24
script:
- npx -y @nomac/cli run --pull build/tests.xcresult -- xcodebuild test -scheme App -destination 'platform=iOS Simulator,name=iPhone 17 Pro' -resultBundlePath build/tests.xcresult
artifacts:
when: always
paths: [build/tests.xcresult]A script
export NOMAC_API_KEY=...
npx -y @nomac/cli run -- bash -lc 'pod install && xcodebuild test -workspace App.xcworkspace -scheme App -destination "platform=iOS Simulator,name=iPhone 17 Pro"'The command is one argument list; for a shell line, run it through bash -lc.
What you get
- The Mac
- A fresh Mac for each run, stopped when the command ends. It never uses or stops a Mac you or your agents run.
- What is uploaded
- The folder, as
nomac syncuploads it: not.git, build output, dependency folders or secret files.--no-syncuploads nothing. - Caches
- None by default, so nothing carries over between runs. With
--drive NAMEthe run has that drive, and caches and build products carry over to the next run with the same name. - Waiting
- A run counts as one of your account's Macs. When the account already runs as many as it may, or no Mac is free, it waits in line up to
--queueseconds (600 by default). - Limits
- One command runs at most an hour (
--timeout, 3540 s by default), and its output keeps 8 MiB, so pipe long logs through xcbeautify or pull the full log. A run spends at most--max-costdollars (5 by default). - Cancelling
- A cancelled job (SIGTERM, SIGINT or SIGHUP) stops the Mac. A job killed outright (SIGKILL) cannot: the command runs on to its limit and the Mac stops 15 minutes after.
- Price
- 1.5¢ a minute ($0.90 an hour) from your NoMac credit, billed per minute, a minute at least. No subscription.
Every option is in nomac run --help and the CLI reference.