wxctl is built for unattended runs. The same config works across environments, re-applying is safe, and every command returns a predictable exit code a pipeline can gate on.

Idempotency

apply reconciles your config against live API state; there is no state file. It creates what is missing, updates what drifted, and leaves what already matches. Re-applying an unchanged config plans no changes, so a pipeline can run apply on every push without creating duplicates.
wxctl plan  -f config.yaml   # dry run: see what would change, no writes
wxctl apply -f config.yaml   # converge to the desired state
Run plan first as a review gate, then apply only when the diff is what you expect.

Exit-code contract

CodeMeaning
0Success. plan returns 0 for any valid plan, including one with pending changes.
1The command failed: a validation error, a failed plan/apply/destroy operation, or a failed test.
2Usage error: an unknown flag, a missing required argument, or an invalid --output value.
101An internal wxctl error (a panic / source bug). Re-run with --full-trace and inspect the run record.
130Interrupted (SIGINT / Ctrl-C).
apply, destroy, and test exit 1 if any resource or test fails, so a CI job fails automatically without extra scripting. Pair this with WXCTL_LOG_PATH for a machine-readable JSON log of the run (see Troubleshooting).

Secrets

Keep credentials out of YAML. Per-environment values come from the environment with ${env:VAR}:
kind: model
ref_name: my_model
url: ${env:WATSONX_URL}
Service endpoints and auth live in a profile (~/.wxctl/profiles.yaml). In CI, write the profile from a single secret and reference it, or point at a generated file with --profile-path. A missing or empty ${env:VAR} is caught during validation, before any service call.

GitHub Actions

This workflow builds wxctl from source, writes a profile from a repository secret, then plans and applies the config in your repo:
name: deploy-wxctl
on:
  push:
    branches: [main]
jobs:
  apply:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: dtolnay/rust-toolchain@stable

      - name: Build wxctl
        run: |
          git clone --depth 1 https://github.com/randyphoa/wxctl.git
          cargo build --release --manifest-path wxctl/Cargo.toml
          echo "$PWD/wxctl/target/release" >> "$GITHUB_PATH"

      - name: Write profile
        run: |
          mkdir -p ~/.wxctl
          printf '%s' "$WXCTL_PROFILES_YAML" > ~/.wxctl/profiles.yaml
          chmod 600 ~/.wxctl/profiles.yaml
        env:
          WXCTL_PROFILES_YAML: ${{ secrets.WXCTL_PROFILES_YAML }}

      - name: Plan
        run: wxctl plan -f config.yaml
        env:
          WATSONX_URL: ${{ secrets.WATSONX_URL }}

      - name: Apply
        run: wxctl apply -f config.yaml
        env:
          WATSONX_URL: ${{ secrets.WATSONX_URL }}
          WATSONX_APIKEY: ${{ secrets.WATSONX_APIKEY }}
Store the profile YAML and every ${env:VAR} value as repository or environment secrets. Map each ${env:VAR} your config references to a job env: entry sourced from secrets.*.

Next steps

Profiles & credentials

How wxctl stores endpoints and authenticates to each service.

Troubleshooting

Logging, concurrency, and timeout environment variables.