Docs · Any other CI

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.

bitbucket-pipelines.yml
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.

.circleci/config.yml
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-tests

Azure Pipelines

Add NOMAC_API_KEY as a secret pipeline variable. Azure gives secrets to a step only through env.

azure-pipelines.yml
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.

Jenkinsfile
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.

.buildkite/pipeline.yml
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.

.gitlab-ci.yml
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

Terminal
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 sync uploads it: not .git, build output, dependency folders or secret files. --no-sync uploads nothing.
Caches
None by default, so nothing carries over between runs. With --drive NAME the 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 --queue seconds (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-cost dollars (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.