Skip to content
GitHub

Recipe: CI/CD pipeline

This recipe shows how to run bino build in a CI/CD pipeline so reports are generated automatically on every commit, on a schedule, or on demand.

  • A bino report bundle committed to a Git repository.
  • CI runner with internet access (for installing bino and Chrome headless shell).
  • Any secrets (database passwords, API keys) stored as CI/CD environment variables.

bino ships a single binary. Install it and its rendering engine in one step:

# Install bino
/bin/bash -c "$(curl -fsSL https://github.com/bino-bi/bino-cli-releases/releases/latest/download/install.sh)"

# Install Chrome headless shell (required for PDF rendering)
bino setup

Or skip the install and use the container image

Section titled “Or skip the install and use the container image”

If your runner can run containers, the image already contains bino and Chromium:

docker run --rm \
  -v "$PWD:/work" \
  -e POSTGRES_PASSWORD \
  ghcr.io/bino-bi/bino-cli:latest build --out-dir dist/

See Running bino in Docker for tag pinning and file ownership.

A typical CI job:

  1. Checks out the repository.
  2. Installs bino and Chrome headless shell.
  3. Runs bino build --out-dir dist/.
  4. Uploads the generated PDFs as build artefacts or to object storage.
name: Build Reports
on:
  push:
    branches: [main]
  schedule:
    - cron: "0 6 1 * *"   # 6 AM on the 1st of each month
  workflow_dispatch:        # manual trigger

jobs:
  build:
    runs-on: ubuntu-latest
    env:
      CI: "1"
      POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }}
      DB_HOST: ${{ secrets.DB_HOST }}

    steps:
      - uses: actions/checkout@v4

      - name: Install bino
        run: |
          /bin/bash -c "$(curl -fsSL https://github.com/bino-bi/bino-cli-releases/releases/latest/download/install.sh)"
          bino setup

      - name: Build reports
        run: bino build --out-dir dist/

      - name: Upload PDFs
        uses: actions/upload-artifact@v4
        with:
          name: reports
          path: dist/*.pdf

Use workflow_dispatch inputs to pass parameters at trigger time:

on:
  workflow_dispatch:
    inputs:
      region:
        description: "Region filter"
        default: "EU"
      year:
        description: "Reporting year"
        default: "2024"

jobs:
  build:
    runs-on: ubuntu-latest
    env:
      CI: "1"
      REGION: ${{ github.event.inputs.region }}
      REPORT_YEAR: ${{ github.event.inputs.year }}
    steps:
      # ... install + build as above

Your manifests reference ${REGION} and ${REPORT_YEAR} as environment variables.

build-reports:
  image: ubuntu:latest
  variables:
    CI: "1"
  script:
    - apt-get update && apt-get install -y curl
    - /bin/bash -c "$(curl -fsSL https://github.com/bino-bi/bino-cli-releases/releases/latest/download/install.sh)"
    - bino setup
    - bino build --out-dir dist/
  artifacts:
    paths:
      - dist/*.pdf
  rules:
    - if: $CI_PIPELINE_SOURCE == "schedule"
    - if: $CI_PIPELINE_SOURCE == "web"
    - if: $CI_COMMIT_BRANCH == "main"

Chrome headless shell is ~150 MB. Cache it between runs to speed up pipelines:

- name: Cache Chrome headless shell
  uses: actions/cache@v4
  with:
    path: ~/.cache/bino
    key: bino-chrome-${{ runner.os }}

- name: Install bino + Chrome
  run: |
    /bin/bash -c "$(curl -fsSL https://github.com/bino-bi/bino-cli-releases/releases/latest/download/install.sh)"
    bino setup

None of this is needed with the container image — Chromium is already inside it.

Instead of CI-specific upload steps, you can use bino's post-build hook to push PDFs to S3, GCS, or any storage:

# bino.toml
[build.hooks]
post-build = ["./scripts/upload.sh"]
#!/bin/bash
# scripts/upload.sh
aws s3 sync "$BINO_OUTPUT_DIR" "s3://reports-bucket/$(date +%Y-%m)/" \
  --exclude "*.log" --exclude "*.json"

This keeps the upload logic inside the report bundle, independent of your CI platform.

Build only specific artefacts to speed up CI:

# Build only the executive summary
bino build --artefact executive_summary

# Build everything except the appendix
bino build --exclude-artefact appendix_detail
  • Pin the bino version in CI by downloading a specific release URL instead of latest.
  • Keep secrets in CI variables, never in manifests. Use ${VAR} syntax and *FromEnv fields in ConnectionSecret.
  • Use bino lint as a fast validation step in PRs — it checks manifests without building PDFs.
  • Run bino build --verbose in CI for detailed logs when debugging failures.

See also: Hooks and ETL workflows for more automation patterns, and Monthly client report recipe for an end-to-end example.