Docs

CI Recipes

pncli's configuration precedence was built for pipelines: environment variables always win, so CI can inject credentials at runtime without any config file on the runner. These recipes show the whole loop — install, authenticate, run, parse.

How config works in CI

Every config value resolves in the same order, highest priority first:

  1. PNCLI_* environment variable
  2. Well-known CI fallback variable, where one exists (table below)
  3. Repo config (.pncli.json, checked into git)
  4. Global user config (~/.pncli/config.json)

In practice: check .pncli.json into the repo for team defaults (project keys, target branches), store credentials as CI secrets, and export them as PNCLI_<SERVICE>_<KEY> variables in the job. No config init, no files written on the runner.

Zero-config credentials

Where a CI platform or a service's own tooling already has a canonical variable, pncli reads it automatically — directly below the PNCLI_* override and above any config file:

VariableProvided byFeeds
GITHUB_TOKENGitHub Actions (built-in)github.token
GITHUB_API_URLGitHub Actions (built-in)github.baseUrl
SONAR_TOKENSonarScanner conventionsonar.token
SYSTEM_ACCESSTOKENAzure Pipelines ($(System.AccessToken))ado.pat

Inside GitHub Actions this means pncli github commands work with zero pncli-specific setup — expose the workflow token and go.

GitHub Actions

Comment on a Jira issue when a deploy finishes — credentials come entirely from secrets:

jobs:
  notify:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: npm install -g @kolatts/pncli
      - name: Comment on the Jira issue
        env:
          PNCLI_JIRA_BASE_URL: https://jira.imagile.dev
          PNCLI_EMAIL: ${{ secrets.JIRA_EMAIL }}
          PNCLI_JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
          ISSUE_KEY: PROJ-123
        run: |
          pncli jira add-comment --key "$ISSUE_KEY" \
            --body "Deployed to staging in run $GITHUB_RUN_ID"

For GitHub itself, the built-in token is enough — this posts a PR comment with no secrets block at all (grant the job pull-requests: write permission):

      - name: Comment on the PR
        env:
          GITHUB_TOKEN: ${{ github.token }}
        run: |
          pncli github --owner "${{ github.repository_owner }}" \
            --repo "${{ github.event.repository.name }}" \
            add-comment --number "${{ github.event.pull_request.number }}" \
            --body "Smoke tests passed ✅"

Jenkins

Use withCredentials to map stored secrets onto PNCLI_* variables for exactly the duration of the step:

pipeline {
  agent any
  stages {
    stage('Quality gate') {
      steps {
        withCredentials([string(credentialsId: 'sonar-token', variable: 'SONAR_TOKEN')]) {
          sh '''
            npm install -g @kolatts/pncli
            export PNCLI_SONAR_BASE_URL=https://sonar.imagile.dev
            pncli sonar issues --project my-project --output-file sonar.json
          '''
        }
      }
    }
  }
}

SONAR_TOKEN needs no PNCLI_ prefix — it's the SonarScanner convention pncli already honors as a fallback.

Azure Pipelines

The predefined System.AccessToken maps straight onto ado.pat. Expose it explicitly — it is not in the environment by default:

steps:
  - script: npm install -g @kolatts/pncli
    displayName: Install pncli
  - script: |
      export PNCLI_ADO_BASE_URL="$(System.CollectionUri)"
      pncli ado work create --type Task --title "Follow-up from build $(Build.BuildNumber)"
    displayName: Create follow-up work item
    env:
      SYSTEM_ACCESSTOKEN: $(System.AccessToken)

Parsing the envelope

Every command prints one JSON envelope to stdout — success and failure alike — so pipeline logic is a jq expression, not log scraping:

# Gate a stage on the result
result=$(pncli jira get-issue --key PROJ-123)
if [ "$(echo "$result" | jq -r '.ok')" != "true" ]; then
  echo "Jira lookup failed: $(echo "$result" | jq -r '.error.message')"
  exit 1
fi
status=$(echo "$result" | jq -r '.data.fields.status.name')
  • Exit codes: non-zero on failure, and the error is still JSON — check .error.status and .error.message rather than parsing stderr.
  • Large payloads: add --output-file results.json to keep search results or logs out of the build log (and out of an agent's context).
  • Previewing: --dry-run prints the API request without executing — useful while wiring up a new pipeline.
  • Debugging: --debug traces every call on stderr and never logs credentials; stdout stays pure JSON.

Secrets hygiene

  • Store tokens only in the CI platform's secret store; export them as env vars in the narrowest scope (step-level beats job-level).
  • Never write credentials into .pncli.json — it's checked into git and meant for team defaults only.
  • Environment variables always beat config files, so a leaked or stale ~/.pncli/config.json on a shared runner can't override the pipeline's injected credentials.