WonderTwin in GitLab CI
WonderTwin in GitLab CI
Section titled “WonderTwin in GitLab CI”Run a twin against your test suite in a GitLab CI pipeline. Capture replay artifacts on every run, including failures.
Snippet
Section titled “Snippet”Drop this into .gitlab-ci.yml (or merge into your existing config):
test-with-twin: image: ubuntu:24.04 variables: WT_TWIN_URL: "http://127.0.0.1:<TWIN_PORT>" CI: "true" before_script: - apt-get update -qq && apt-get install -y -qq curl ca-certificates - curl -sSL "https://github.com/wondertwin-ai/wondertwin/releases/latest/download/wt-linux-amd64" -o /usr/local/bin/wt - chmod +x /usr/local/bin/wt - | if [ -n "$WONDERTWIN_LICENSE" ]; then printf '%s' "$WONDERTWIN_LICENSE" > /tmp/license.json wt license install /tmp/license.json rm /tmp/license.json fi - wt install <TWIN>@<TWIN_VERSION> - wt up > /tmp/twin.log 2>&1 & - echo $! > /tmp/twin.pid - | for i in $(seq 1 30); do curl -sf "$WT_TWIN_URL/admin/state" > /dev/null && break sleep 0.2 done - | wt runs start \ --twin "$WT_TWIN_URL" \ --seed 42 \ --run-id "${CI_PIPELINE_ID}-${CI_JOB_ID}" script: - <TEST_COMMAND> after_script: - | wt runs finish \ --twin "$WT_TWIN_URL" \ --run-id "${CI_PIPELINE_ID}-${CI_JOB_ID}" \ --export wondertwin-replay.jsonl.gz || true - | if [ -f /tmp/twin.pid ]; then kill "$(cat /tmp/twin.pid)" 2>/dev/null || true fi artifacts: name: "wondertwin-replay-${CI_PIPELINE_ID}" paths: - wondertwin-replay.jsonl.gz when: always expire_in: 14 daysSetup checklist
Section titled “Setup checklist”- Pick your twin and version. Replace
<TWIN>(e.g.stripe,twilio),<TWIN_VERSION>(e.g.0.1.0), and<TWIN_PORT>(twin-stripe:4111). - Replace
<TEST_COMMAND>with your actual test invocation. - Set the license CI/CD variable. In GitLab: Settings → CI/CD → Variables → Add variable. Key:
WONDERTWIN_LICENSE. Value: the full contents of the license JSON file (paste it as-is; do not base64-encode). Mark as Masked if your value is short enough; otherwise mark as File and adjust the snippet to read from the path. - Tell your test code where the twin is. The job exports
WT_TWIN_URL. Most SDKs let you override the base URL via env var. - Choose a base image.
ubuntu:24.04is the safe default. If your tests need a specific runtime, usenode:20,golang:1.22,python:3.12, etc. — and addapt-get install -y curl ca-certificatesif those aren’t already in the image.
Variations
Section titled “Variations”With BYO synthetic fixtures
Section titled “With BYO synthetic fixtures”Add --fixtures to the wt runs start invocation:
- | wt runs start \ --twin "$WT_TWIN_URL" \ --seed 42 \ --fixtures fixtures/stripe-snapshot.json \ --run-id "${CI_PIPELINE_ID}-${CI_JOB_ID}"Without an explicit run lifecycle (using wt runs wrap)
Section titled “Without an explicit run lifecycle (using wt runs wrap)”Replace the script block with a single wrap call. before_script still installs wt and starts the twin; after_script only kills the twin process:
script: - | wt runs wrap \ --twin "$WT_TWIN_URL" \ --seed 42 \ --run-id "${CI_PIPELINE_ID}-${CI_JOB_ID}" \ --export wondertwin-replay.jsonl.gz \ -- <TEST_COMMAND>wrap always finishes the run on exit. Cleaner for simple test commands.
Use a service container instead of wt up &
Section titled “Use a service container instead of wt up &”GitLab CI supports services: for sidecar containers. If you publish the twin as a Docker image:
test-with-twin: image: ubuntu:24.04 services: - name: wondertwin/twin-<TWIN>:<TWIN_VERSION> alias: twin variables: WT_TWIN_URL: "http://twin:<TWIN_PORT>"This is cleaner if you have a stable container registry path for the twin. See the [twin docker docs] for image availability — not all twins ship as containers today.
Pin both wt and the twin version
Section titled “Pin both wt and the twin version”before_script: - curl -sSL "https://github.com/wondertwin-ai/wondertwin/releases/download/v0.4.2/wt-linux-amd64" -o /usr/local/bin/wt - chmod +x /usr/local/bin/wt - wt install stripe@0.1.0Troubleshooting
Section titled “Troubleshooting”My replay artifact is missing on failed jobs
Section titled “My replay artifact is missing on failed jobs”GitLab CI’s artifacts.when: always ensures the artifact uploads regardless of the job’s exit status. If you don’t see it, confirm the after_script block’s wt runs finish --export actually ran — check the job log for the export line. The || true after the export prevents the after_script from failing if the run was already finished elsewhere.
before_script background process gets killed prematurely
Section titled “before_script background process gets killed prematurely”GitLab CI runners terminate background processes when the job ends. The after_script block is the last to run; the twin survives until that block kills it. If you see the twin dying before script: finishes, check that you’re not running with --clean-shell or similar runner option that aggressively cleans up.
Job hangs in before_script
Section titled “Job hangs in before_script”The health check loop is bounded at 6 seconds. If the twin doesn’t come up, the loop exits and the next step’s wt runs start fails — surfacing a clear error. If the loop appears to hang, your runner may have a different seq semantics; replace with for i in 1 2 3 ... 30; explicitly or use a counter.
License doesn’t validate
Section titled “License doesn’t validate”See the GitHub Actions guide’s troubleshooting — the causes are platform-agnostic.
Reference
Section titled “Reference”wt runs --helpfor full CLI optionsconcepts.md— twins, runs, replay, licensestrategy.md— why docs first- GitLab CI artifacts documentation