GitHub Actions
pdcli is built for non-interactive use, so it drops into GitHub Actions cleanly:
authenticate with secrets as environment variables, run a preflight, then let the
deterministic exit codes and --output json decide
whether the job passes. This page is the GitHub-specific recipe; see
CI recipes for the flags and scheduled-job patterns that apply
to any CI.
Wire the secrets
Section titled “Wire the secrets”Store the token as an encrypted repository or environment secret (never in the workflow file), and set the company domain and token as environment variables. Env vars take precedence over the keychain, so the runner needs no keychain and pdcli never prompts.
env: PDCLI_COMPANY_DOMAIN: acme PDCLI_API_TOKEN: ${{ secrets.PIPEDRIVE_API_TOKEN }}Add the token under Settings → Secrets and variables → Actions as
PIPEDRIVE_API_TOKEN. The domain is not secret (it’s your {company}.pipedrive.com
subdomain), so it can sit in plain text.
Option A: pinned npm install
Section titled “Option A: pinned npm install”Install a pinned version so a background release can’t change behaviour mid-pipeline.
name: pipedriveon: workflow_dispatch: schedule: - cron: '0 6 * * 1' # Mondays 06:00 UTC
jobs: deals: runs-on: ubuntu-latest env: PDCLI_COMPANY_DOMAIN: acme PDCLI_API_TOKEN: ${{ secrets.PIPEDRIVE_API_TOKEN }} steps: - uses: actions/setup-node@v5 with: node-version: 20 - run: npm install -g @wavyx/pdcli@0.22.0
# Preflight: config + keychain checks, zero network egress. Exits 78 on a # config problem, failing the job before it spends any API budget. - run: pdcli doctor --offline
- name: Open deals to JSON run: pdcli deal list --status open --output json --jq '[.[] | {id, title, value}]' > deals.json
- uses: actions/upload-artifact@v4 with: name: deals path: deals.jsonOption B: the container image
Section titled “Option B: the container image”Skip the Node setup entirely by running the job inside the published image. The
container: key runs every step inside it, and the token/domain still arrive as env
vars.
jobs: deals: runs-on: ubuntu-latest container: ghcr.io/wavyx/pdcli:0.22.0 env: PDCLI_COMPANY_DOMAIN: acme PDCLI_API_TOKEN: ${{ secrets.PIPEDRIVE_API_TOKEN }} steps: - run: pdcli doctor --offline - run: pdcli deal list --status open --output json --jq '.[].id'Pin the tag (:0.22.0) rather than latest for reproducible runs. See
Distribution for the image details.
Gate on the exit code
Section titled “Gate on the exit code”Every command returns a deterministic sysexit code, so
a step fails the job on the right condition without parsing text. A non-zero exit fails
the step by default, which is usually what you want. When you need to branch, read
$?:
- name: Fail only on real errors, tolerate rate limits run: | set +e pdcli deal list --output json > deals.json code=$? case $code in 0) echo "ok" ;; 75|69) echo "::warning::transient ($code) — will retry next run"; exit 0 ;; 77) echo "::error::auth failed — rotate PIPEDRIVE_API_TOKEN"; exit 1 ;; *) echo "::error::pdcli exited $code"; exit 1 ;; esacBecause the error output also mirrors the format (JSON on a piped or --output json
run, written to stderr), a failed step leaves a parseable object you can inspect or
upload. --output json on the happy path means downstream jq and other tools get
structured data every time.
Data-quality gate
Section titled “Data-quality gate”pdcli audit --strict exits 1 when any must-severity hygiene check has findings, so
it works as a required check on a schedule or before a deploy that depends on clean
data:
- run: pdcli audit --strictExit 1 fails the job and the findings print for the log. See
Data-hygiene audit.
Fail fast under load
Section titled “Fail fast under load”For CI you often want a clean failure over a long retry. --no-retry surfaces the
first 429/5xx immediately (exit 75 or 69) instead of sleeping through backoff,
and --timeout <ms> caps each request so a hung network can’t stall the job:
- run: pdcli deal list --no-retry --timeout 10000 --output jsonLet the workflow’s own retry or schedule re-run the job. See CI recipes for nightly backups, incremental pulls, and bulk imports.