CI: gate PRs / MRs with Upliftr
Upliftr is AI debugging agents that debug your app, read-only by design. Once an agent confirms a flow, it saves that flow as a plain-English check and re-runs it in CI. Upliftr is designed to be the check that gates a pull request (GitHub) or merge request (GitLab): on every PR/MR an agent drives a real browser, reproduces any regression, root-causes it to the exact backend line, and files a deduped ticket, all without mutating your code, data, or infrastructure. The same engine and the same exit codes back three drop-in surfaces:
- A GitHub Action (
action.yml), PR gating with artifacts and auto-filed issues. - A GitLab CI job / component, MR gating on the public Upliftr image, JUnit in the MR widget.
- The CLI (
upliftr run), the underlying gate, usable from any CI system or terminal.
Exit codes (the gate)
The CLI's exit code is the gate. It's the same everywhere:
| Code | Meaning |
|---|---|
0 | All cases passed. |
1 | At least one case failed (an assertion didn't hold). |
2 | At least one case is broken (the run couldn't be carried out). |
3 | Spec / config error (bad path, no suites matched, missing API key). |
Broken (2) outranks failed (1) outranks ok (0). A non-zero exit fails the job and blocks the PR/MR.
GitHub Action
Add one step. The action runs on the public Upliftr image (Chromium + engine baked in, no install step), runs your suites headless, writes reports, and exits non-zero to gate the PR.
# .github/workflows/upliftr.yml
name: Upliftr E2E
on: pull_request
jobs:
e2e:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: upliftrhq/upliftr-action@v1
with:
suite: "suites/*.upliftr.yaml"
file-issues: "github" # optional; '' to disable
anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
env:
GITHUB_TOKEN: ${{ github.token }} # only for file-issues: githubSet ANTHROPIC_API_KEY as a repo secret. Inputs: suite, file-issues (default ''), anthropic-api-key (required), out. Ready-to-copy: examples/ci/example-upliftr.yml. The Action is published from a small public repo (upliftrhq/upliftr-action); the full source lives in upliftrhq/upliftr.
GitLab CI
The job runs on the public Upliftr image (Chromium + engine baked in, no install step), writes a JUnit report GitLab shows in the MR widget, and exits non-zero to gate the MR.
# .gitlab-ci.yml
upliftr:
image: ghcr.io/upliftrhq/upliftr-api:latest
variables:
UPLIFTR_SUITE: "suites/*.upliftr.yaml"
# FILE_ISSUES: "gitlab" # optional, needs GITLAB_TOKEN (api scope)
GITLAB_PROJECT: "$CI_PROJECT_ID"
GITLAB_URL: "$CI_SERVER_URL"
GIT_SHA: "$CI_COMMIT_SHA"
CI_RUN_URL: "$CI_PIPELINE_URL"
script:
- ARGS=(); [ -n "${FILE_ISSUES:-}" ] && ARGS=(--file-issues "$FILE_ISSUES")
- upliftr run "$UPLIFTR_SUITE" "${ARGS[@]}" --out upliftr-artifacts
artifacts:
when: always
paths: ["upliftr-artifacts"]
reports:
junit: "upliftr-artifacts/junit.xml"
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"Setup (once): add ANTHROPIC_API_KEY as a masked CI/CD variable, the engine uses your Anthropic key to drive & root-cause the browser. Copy-paste example: examples/ci/.gitlab-ci.yml.
The CLI directly
The same gate, from any CI system or your terminal, see the CLI reference.
ANTHROPIC_API_KEY=sk-ant-... upliftr run "suites/*.upliftr.yaml" \
--file-issues gitlab \
--out artifacts
echo "exit: $?" # 0 / 1 / 2 / 3--file-issues <provider>files failures via GitHub, GitLab, or Jira (needs the matching env,GITHUB_TOKEN/GITHUB_REPO,GITLAB_TOKEN/GITLAB_PROJECT, …). Comma-separate to file to several at once. See Filing issues.--out <dir>writesreport.json,junit.xml, andreport.html.--headedruns with a visible browser (default is headless, required for CI).
Next: Filing issues · Connecting server logs