Skip to content

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.