GitHub Actions pipelines
Goal
Run Qualytics CLI commands automatically inside GitHub Actions: validate quality check changes on pull requests, promote checks to production on tagged releases, run nightly scans on a schedule, and detect drift between Git and the live environment. The same patterns translate to GitLab CI, Jenkins, or any other CI provider.
Permissions
| Step | Endpoint | Role | Team permission |
|---|---|---|---|
checks import --dry-run |
GET /api/quality-checks, GET /api/containers |
Member |
Reporter (read-only validation) |
checks import (apply) |
POST / PUT /api/quality-checks |
Member |
Author |
config import (apply) |
POST/PUT on multiple resources |
Manager (if creating connections), Member + Editor otherwise |
Editor for non-connection resources |
operations sync/profile/scan |
POST /api/operations/run |
Member |
Editor |
Scope tokens per environment
Use a separate service token per environment (DEV_QUALYTICS_TOKEN, PROD_QUALYTICS_TOKEN). Never share Prod tokens with PR jobs that any contributor can trigger.
Prerequisites
- A GitHub repository with the Qualytics YAML configuration in version control.
- GitHub Environments configured for
test,production, etc., each with its own secrets. - Service tokens for each environment, stored as GitHub Secrets.
CLI workflow
Each subsection below is a complete workflow file. Drop them into .github/workflows/ and adjust environment names and secrets.
Validate on pull request, promote on release
A safe two-stage pattern: every PR runs a dry-run import to confirm the YAML is valid; merging the PR doesn't deploy anything; cutting a release deploys to Prod.
# .github/workflows/quality-checks.yml
name: Quality Checks
on:
pull_request:
paths: ['checks/**']
release:
types: [published]
jobs:
validate-on-pr:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install Qualytics CLI
run: pip install qualytics-cli
- name: Dry-run import to Test
env:
QUALYTICS_URL: ${{ secrets.TEST_QUALYTICS_URL }}
QUALYTICS_TOKEN: ${{ secrets.TEST_QUALYTICS_TOKEN }}
CI: true
run: |
qualytics checks import \
--datastore-id ${{ vars.TEST_DATASTORE_ID }} \
--input ./checks/ \
--dry-run
promote-to-prod:
if: github.event_name == 'release'
runs-on: ubuntu-latest
environment: production # required reviewers gate happens here
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- run: pip install qualytics-cli
- name: Apply checks to Prod
env:
QUALYTICS_URL: ${{ secrets.PROD_QUALYTICS_URL }}
QUALYTICS_TOKEN: ${{ secrets.PROD_QUALYTICS_TOKEN }}
CI: true
run: |
qualytics checks import \
--datastore-id ${{ vars.PROD_DATASTORE_ID }} \
--input ./checks/
Nightly quality scan
# .github/workflows/nightly-scan.yml
name: Nightly Scan
on:
schedule:
- cron: '0 6 * * *'
jobs:
scan:
runs-on: ubuntu-latest
steps:
- run: pip install qualytics-cli
- name: Run pipeline
env:
QUALYTICS_URL: ${{ secrets.QUALYTICS_URL }}
QUALYTICS_TOKEN: ${{ secrets.QUALYTICS_TOKEN }}
CI: true
run: |
qualytics operations sync --datastore-id ${{ vars.DATASTORE_ID }}
qualytics operations profile --datastore-id ${{ vars.DATASTORE_ID }}
qualytics operations scan --datastore-id ${{ vars.DATASTORE_ID }}
Full configuration promotion (with secrets)
# .github/workflows/promote-config.yml
name: Promote Quality Config
on:
push:
branches: [main]
paths: ['qualytics-config/**']
jobs:
promote:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- run: pip install qualytics-cli
- name: Dry-run
env:
QUALYTICS_URL: ${{ secrets.PROD_QUALYTICS_URL }}
QUALYTICS_TOKEN: ${{ secrets.PROD_QUALYTICS_TOKEN }}
DB_HOST: ${{ secrets.PROD_DB_HOST }}
DB_USER: ${{ secrets.PROD_DB_USER }}
DB_PASSWORD: ${{ secrets.PROD_DB_PASSWORD }}
CI: true
run: qualytics config import --input ./qualytics-config --dry-run
- name: Apply
env:
QUALYTICS_URL: ${{ secrets.PROD_QUALYTICS_URL }}
QUALYTICS_TOKEN: ${{ secrets.PROD_QUALYTICS_TOKEN }}
DB_HOST: ${{ secrets.PROD_DB_HOST }}
DB_USER: ${{ secrets.PROD_DB_USER }}
DB_PASSWORD: ${{ secrets.PROD_DB_PASSWORD }}
CI: true
run: qualytics config import --input ./qualytics-config
Drift detection
# .github/workflows/drift-check.yml
name: Drift Check
on:
schedule:
- cron: '0 9-17 * * 1-5'
workflow_dispatch:
jobs:
drift:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v4
- run: pip install qualytics-cli
- name: Re-export over committed folder
env:
QUALYTICS_URL: ${{ secrets.PROD_QUALYTICS_URL }}
QUALYTICS_TOKEN: ${{ secrets.PROD_QUALYTICS_TOKEN }}
CI: true
run: qualytics config export --datastore-id ${{ vars.PROD_DATASTORE_ID }} --output ./qualytics-config
- name: Open drift PR if anything changed
run: |
if ! git diff --quiet -- qualytics-config/; then
BRANCH="drift/$(date -u +%Y-%m-%dT%H%M)"
git checkout -b "$BRANCH"
git config user.email "drift-bot@example.com"
git config user.name "Qualytics Drift Bot"
git add qualytics-config/
git commit -m "drift: snapshot $(date -u +%Y-%m-%dT%H:%M:%SZ)"
git push origin "$BRANCH"
gh pr create --title "Qualytics drift detected" \
--body "Drift snapshot from production. Review and decide whether to merge or revert in the UI."
fi
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Behind the scenes
The CLI invocations inside each workflow hit the same API endpoints documented in the respective example pages (e.g., Promote checks Dev to Prod, Daily sync, profile, and scan). GitHub Actions just provides the trigger and credential injection.
| GitHub Actions feature | Qualytics use |
|---|---|
secrets.<name> |
Env-scoped tokens, DB credentials |
vars.<name> |
Datastore IDs (non-sensitive) |
environment: on a job |
Required-reviewers gate before Prod |
paths: filter |
Run only on relevant file changes |
schedule: trigger |
Nightly scans, drift checks |
Python equivalent
GitHub Actions itself is YAML; the actions invoke shell commands. There's no meaningful "Python equivalent" of GitHub Actions, but you can replace the pip install qualytics-cli + CLI invocations with direct API calls in Python if you prefer (see the Python sections of the per-scenario pages).
Variations and advanced usage
Required reviewers on Prod
In Settings → Environments, configure the production environment to require reviewer approval before any job that targets it can run. The job will pause until a reviewer approves.
OIDC instead of long-lived tokens
If your Qualytics instance supports OIDC token exchange (some self-hosted deployments do), you can mint short-lived tokens from GitHub's OIDC provider rather than storing a long-lived service token. The auth call changes; everything else stays the same.
Multiple environments in one workflow
Use a matrix and a per-environment secret prefix:
strategy:
matrix:
env: [dev, staging, prod]
steps:
- env:
QUALYTICS_URL: ${{ secrets[format('{0}_QUALYTICS_URL', matrix.env)] }}
QUALYTICS_TOKEN: ${{ secrets[format('{0}_QUALYTICS_TOKEN', matrix.env)] }}
run: qualytics datastores list
Output the import summary as a PR comment
After a dry-run, post the Import Summary table back to the PR so reviewers see it without leaving GitHub. Use a community action like marocchino/sticky-pull-request-comment or write a gh pr comment step.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Workflow log shows banner output | CI and QUALYTICS_NO_BANNER not set |
Add both to the env. |
Job fails with 403 Forbidden only on Prod |
Token scoped to Dev only | Confirm the secret used matches the environment. |
Resource not accessible by integration on gh pr create |
Workflow lacks PR write permission | Add permissions: { pull-requests: write } to the job. |
pip install is slow |
Default Python action | Switch to astral-sh/setup-uv@v3 and uv pip install. |
| Workflow runs but doesn't seem to do anything | paths: filter excluded the changed files |
Confirm the path filter matches the actual change. |
Related
- Automation & CI/CD command reference
- Promote checks Dev to Prod: the manual workflow that the promote pipeline above automates.
- Drift detection: the manual workflow that the drift pipeline above automates.
- Daily sync, profile, and scan: the manual workflow that the nightly-scan pipeline above automates.