Running in CI
Deploying from a pipeline, with short-lived credentials instead of stored keys.
clawops init is interactive and must not run in CI. Write the config file directly and let the
pipeline's identity supply the cloud credentials.
OIDC instead of stored keys
Every cloud can hand a GitHub Actions job a short-lived credential in exchange for its OIDC token, so no long-lived key sits in your repository secrets. It works on all three providers, and clawops needs no special support for it, the credential lands in the environment, which is where clawops already looks.
permissions:
id-token: write
contents: read
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/clawops-deploy
aws-region: us-east-1GCP uses Workload Identity Federation, Azure uses a federated credential on an Entra app
registration. Trust policies and role bindings for all three are in
docs/github-actions-oidc.md.
Writing the config
- name: Write clawops config
run: |
mkdir -p ~/.clawops
cat > ~/.clawops/config.json <<EOF
{
"version": 1,
"defaults": { "provider": "${{ vars.CLAWOPS_PROVIDER }}", "stack": "default" },
"stacks": {
"default": {
"provider": "${{ vars.CLAWOPS_PROVIDER }}",
"region": "${{ vars.CLAWOPS_REGION }}",
"stateUrl": "${{ vars.CLAWOPS_STATE_URL }}"
}
},
"ssh": {
"keyPath": "~/.clawops/id_ed25519",
"knownHostsPath": "~/.clawops/known_hosts"
}
}
EOF
- name: Restore SSH key
run: |
install -m 600 /dev/null ~/.clawops/id_ed25519
echo "${{ secrets.CLAWOPS_SSH_PRIVATE_KEY }}" > ~/.clawops/id_ed25519The pipeline itself
Plan and apply are separate steps, so the plan can be reviewed between them.
- run: npx @clawops/cli doctor --provider aws
- run: npx @clawops/cli plan --stack prod --out plan.json
- uses: actions/upload-artifact@v4
with: { name: plan, path: plan.json }
# gate the next job on a review or an environment approval
- run: npx @clawops/cli apply plan.json --yesdoctor exits non-zero when any check fails, so it works as a gate. Run it with --provider
before the plan: an unregistered Azure resource provider or a missing state bucket fails the job
in seconds rather than partway through provisioning.
Set the state passphrase explicitly
A self-managed state backend needs PULUMI_CONFIG_PASSPHRASE. Locally clawops generates and
stores one; in CI there is no home directory to carry it between runs, so set it from a secret
or every run will create a stack it cannot read next time.
Pinning
latest moves between major lines. Pin the CLI in CI:
- run: npm install -g @clawops/cli@2.0.2Pin the OpenClaw version in the plan as well. clawops resolves a moving tag to a concrete release and refuses one it cannot resolve, so an unpinned deploy fails rather than drifting, but the resolved version depends on when the plan was generated.