Integrating Pulsar into CI/CD
Add Pulsar to your GitHub Actions pipeline
This tutorial covers adding Pulsar to your continuous integration pipeline with caching, JSON output, and PR blocking.
Basic GitHub Actions workflow
name: CI
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Pulsar
run: |
curl -L https://github.com/CarlosEduJs/pulsar/releases/latest/download/pulsar-cli-x86_64-unknown-linux-gnu.tar.xz | tar xJ
sudo mv pulsar /usr/local/bin/
- name: Check with Pulsar
run: pulsar check src/This is the simplest setup. On every push and PR, Pulsar downloads and runs against src/.
Caching the binary
Avoid re-downloading on every run by caching the binary:
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Cache Pulsar binary
id: cache-pulsar
uses: actions/cache@v4
with:
path: /usr/local/bin/pulsar
key: pulsar-${{ runner.os }}
- name: Install Pulsar (if not cached)
if: steps.cache-pulsar.outputs.cache-hit != 'true'
run: |
curl -L https://github.com/CarlosEduJs/pulsar/releases/latest/download/pulsar-cli-x86_64-unknown-linux-gnu.tar.xz | tar xJ
sudo mv pulsar /usr/local/bin/
- name: Check with Pulsar
run: pulsar check src/The cache key is based on the runner OS. Invalidate the cache manually when you want to force a Pulsar upgrade.
JSON output for custom tooling
Use --format json to pipe diagnostics into jq or custom scripts:
- name: Check with Pulsar (JSON)
continue-on-error: true
run: |
pulsar check src/ --format json > diagnostics.json
- name: Fail if any errors
run: |
errors=$(jq '[.[] | select(.severity == "Error")] | length' diagnostics.json)
if [ "$errors" -gt 0 ]; then
echo "Found $errors errors"
exit 1
fiThe continue-on-error: true flag lets the job continue so the jq step can run. Without it,
pulsar check would exit 1 and the remaining steps would be skipped. The jq step still fails the
job when errors are present.
Blocking PRs on new violations
Combine with reviewdog or actions/github-script to annotate PRs:
- name: Check with Pulsar (JSON)
continue-on-error: true
run: pulsar check src/ --format json > pulsar-report.json
- name: Annotate PR
uses: actions/github-script@v7
if: github.event_name == 'pull_request'
with:
script: |
const fs = require('fs');
const diagnostics = JSON.parse(fs.readFileSync('pulsar-report.json', 'utf8'));
diagnostics.forEach(d => {
core.error(d.message, {
file: d.location.file,
line: d.location.line,
title: d.rule_id,
});
});
- name: Fail on errors
run: |
errors=$(jq 'length' pulsar-report.json)
if [ "$errors" -gt 0 ]; then
echo "Pulsar found $errors diagnostic(s)"
exit 1
fiThe continue-on-error: true flag allows the job to continue past the check step so annotations can be created. The final step ensures the job still fails the PR when diagnostics exist.
Matrix builds (multi-OS)
jobs:
lint:
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Install Pulsar
run: |
case "${{ runner.os }}" in
Linux)
url="https://github.com/CarlosEduJs/pulsar/releases/latest/download/pulsar-cli-x86_64-unknown-linux-gnu.tar.xz"
;;
macOS)
url="https://github.com/CarlosEduJs/pulsar/releases/latest/download/pulsar-cli-aarch64-apple-darwin.tar.xz"
;;
esac
curl -L "$url" | tar xJ
sudo mv pulsar /usr/local/bin/
- name: Check with Pulsar
run: pulsar check src/GitLab CI
pulsar-check:
stage: lint
image: ubuntu:latest
script:
- apt-get update && apt-get install -y curl xz-utils
- curl -L https://github.com/CarlosEduJs/pulsar/releases/latest/download/pulsar-cli-x86_64-unknown-linux-gnu.tar.xz | tar xJ
- mv pulsar /usr/local/bin/
- pulsar check src/
only:
- merge_requestsBest practices
| Practice | Why |
|---|---|
| Cache the binary | Save 15-30s per run |
| Run on PRs only | Avoid wasting CI minutes on every push to WIP branches |
Use --format json in CI | Easier to parse, annotate, and forward to monitoring |
Keep pulsar.toml in version control | Ensures consistent rules across machines |
| Run Pulsar before tests | Fail fast — no point running tests if the code has schema mismatches |
Last updated on