Skip to content

Use CI/CD to automate tasks

Continuous Integration and Continuous Deployment (CI/CD) help you automate repetitive work across your project lifecycle. In practice, CI/CD turns checklists into runnable workflows, so quality checks and delivery steps happen the same way every time.

This chapter introduces the core concepts, then shows practical GitHub Actions examples you can adapt to your own repository.

For foundational context and adjacent how-to guides:

Key Concepts of CI/CD

CI/CD automates integration and deployment so you can ship reliably and reduce manual coordination overhead. Before looking at workflow files, it helps to name the moving parts:

  • Runners: Machines (often cloud-hosted) that execute your workflow steps.
  • Workflows: YAML-defined job collections triggered by events like pushes and pull requests.
  • Jobs: Each workflow consists of multiple jobs, which are collections of steps.
  • Steps: Sequential tasks within a job.
  • Commands: Shell commands or scripts you would otherwise run manually.

Environment Considerations

One common failure mode in CI/CD is "works on my machine" drift. Runners do not inherit your local shell state, so your project environment must be declared in repository-managed configuration files. That is how you make local and CI runs behave the same way.

For more on project initialization, see project/repo.md.

Configuration and Environment Variables

Following 12-factor principles, keep runtime configuration in environment variables. You can store local defaults in a .env file and auto-load them with tools like direnv; CI systems inject them as secrets.

For more details, refer to ../machine/shell.md.

Common examples include:

  • DATABASE_URL or REDIS_URL for database connections
  • AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY for AWS access
  • AWS_REGION for specifying the AWS region
  • AWS_BUCKET_NAME and AWS_BUCKET_REGION for S3 bucket configuration
  • OPENAI_API_KEY for OpenAI API access
  • HUGGINGFACE_API_KEY for Hugging Face API access
  • GITHUB_TOKEN for GitHub API access

Leveraging Pixi Environment

Use your pixi environment to define repeatable project tasks. This gives you a single source of truth for commands and versions, while taking advantage of caching and reproducible execution.

For more detail, refer to the Pixi documentation.

Typical tasks include:

  • Building and publishing documentation to platforms like Confluence upon merging to the main branch
  • Running software tests on every commit
  • Deploying to production on a version bump
  • Running Python package installation tests

Bootstrap Tip: automate CI debugging Debugging failed CI runs is a great candidate for an Agent Skill. Instead of manually digging through logs, you can have an agent pull job output, identify likely root causes, and propose a fix.

Practical Examples with GitHub Actions

The following minimal GitHub Actions examples map directly to common CI/CD goals. The pinned action versions are intentional; they help keep behavior stable over time. Each example is intentionally minimal so you can copy, run, and then evolve it for your own repository. As your project grows, keep workflows small, task-focused, and explicit about credentials and environment setup.

Example 1: Running Tests

name: Run Tests
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - uses: prefix-dev/setup-pixi@v0.8.10
    - name: Run tests
      run: pixi run test

This example assumes the test task is defined in pixi.toml or pyproject.toml.

Example 2: Deploying to Production

name: Deploy to Production
on:
  push:
    tags:
    - 'v*.*.*'
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - uses: prefix-dev/setup-pixi@v0.8.10
    - name: Deploy
      run: pixi run deploy

This example assumes the deploy task is defined in pixi.toml or pyproject.toml.

Example 3: Building Documentation

name: Build Documentation
on:
  push:
    branches:
    - main
jobs:
  build-docs:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - uses: prefix-dev/setup-pixi@v0.8.10
    - name: Build Docs
      run: pixi run build-docs
    - name: Publish to Confluence
      run: uvx md2cf --space "TEAM" --parent-title "Project Docs" docs/
      env:
        CONFLUENCE_HOST: ${{ secrets.CONFLUENCE_HOST }}
        CONFLUENCE_TOKEN: ${{ secrets.CONFLUENCE_TOKEN }}

This example assumes the build-docs task is defined in pixi.toml or pyproject.toml.

Confluence publishing requires CI secrets. Add CONFLUENCE_HOST and CONFLUENCE_TOKEN as GitHub Actions secrets in Settings → Secrets and variables → Actions. If this workflow runs in a protected deployment environment, use environment-scoped secrets instead.

If your repository already stores the Confluence URL as CONFLUENCE_URL, map it in job env as CONFLUENCE_HOST: ${{ secrets.CONFLUENCE_URL }}. md2cf does not read CONFLUENCE_URL directly.

CONFLUENCE_HOST should be the full Confluence REST API base URL passed through -o/--host; for Atlassian Cloud, a common shape is https://<your-site>.atlassian.net/wiki/rest/api.

CONFLUENCE_TOKEN matches md2cf's --token (Bearer) path, which is common in Server/Data Center PAT flows. Confluence Cloud often uses CONFLUENCE_USERNAME (Atlassian account email) plus CONFLUENCE_PASSWORD set to an Atlassian API token; see the upstream md2cf README.

The identity behind your secret must be able to create and update pages in the target Confluence space. Replace TEAM with a real space key, and replace Project Docs with a visible --parent-title; wrong values often appear in CI as permission or not-found errors.

Example 4: Running Python package installation tests

name: Python Package Installation Tests
on: [push]
jobs:
  install-test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - uses: prefix-dev/setup-pixi@v0.8.10
    - name: Install Package
      run: pixi run install-test
    - name: Verify Installation
      run: pixi run verify-install

This example assumes the install-test and verify-install tasks are defined in pixi.toml or pyproject.toml.