Skip to main content

CI/CD integration

Every pipeline does the same four things: install vulkro-sf, sign in with a CI key, scan, and fail the job on exit code 1.

curl -fsSL https://dist.vulkro.com/install-sf.sh | bash
vulkro-sf login # uses VULKRO_ACCOUNT_TOKEN
vulkro-sf scan ./force-app --format sarif > vulkro-sf.sarif

Create the CI key in the portal (app.vulkro.com) and store it as a secret named VULKRO_ACCOUNT_TOKEN. Without it the scan exits 1 with license_required. An air-gapped runner sets VULKRO_LICENSE_B64 to a base64 .lic file instead. See Licensing.

GitHub Actions​

# .github/workflows/vulkro-sf.yml
name: Vulkro for Salesforce
on:
pull_request:
branches: [main]
push:
branches: [main]
permissions:
contents: read
security-events: write # to upload SARIF
jobs:
scan:
runs-on: ubuntu-latest
env:
VULKRO_ACCOUNT_TOKEN: ${{ secrets.VULKRO_ACCOUNT_TOKEN }}
steps:
- uses: actions/checkout@v4
- name: Install vulkro-sf
run: curl -fsSL https://dist.vulkro.com/install-sf.sh | bash
- name: Sign in with the CI key
run: vulkro-sf login
- name: Scan
run: vulkro-sf scan ./force-app --format sarif > vulkro-sf.sarif
- name: Upload SARIF
if: always()
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: vulkro-sf.sarif
category: vulkro-sf

if: always() uploads the SARIF even when the scan step fails on findings.

GitLab CI​

# .gitlab-ci.yml
vulkro-sf-scan:
image: ubuntu:24.04
before_script:
- apt-get update && apt-get install -y curl ca-certificates
- curl -fsSL https://dist.vulkro.com/install-sf.sh | bash
- vulkro-sf login # VULKRO_ACCOUNT_TOKEN: a masked CI/CD variable
script:
- vulkro-sf scan ./force-app --format sarif > vulkro-sf.sarif
artifacts:
when: always
paths:
- vulkro-sf.sarif
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

Bitbucket Pipelines​

# bitbucket-pipelines.yml
image: ubuntu:24.04
pipelines:
pull-requests:
'**':
- step:
name: Vulkro for Salesforce
script:
- apt-get update && apt-get install -y curl ca-certificates
- curl -fsSL https://dist.vulkro.com/install-sf.sh | bash
- vulkro-sf login # VULKRO_ACCOUNT_TOKEN: a secured repository variable
- vulkro-sf scan ./force-app --format sarif > vulkro-sf.sarif
artifacts:
- vulkro-sf.sarif

Fail only on new findings​

To keep existing findings from blocking every pull request, compare against the target branch with --gate-vs. It scans the base ref too and exits 1 only for findings the change introduced. Check out the full history first (fetch-depth: 0 on GitHub).

vulkro-sf scan ./force-app --gate-vs origin/main

--min-confidence high makes the gate stricter while a team works through its backlog. See Baselines.

Exit codes​

CodeMeaningIn CI
0No findingsPass
1Findings reported, or no licence (license_required on stderr)Fail
2Error (bad arguments, unreadable path, internal failure)Fail, read stderr

Air-gapped runners​

Bake a pinned binary into the runner image (VULKRO_SF_VERSION), set VULKRO_OFFLINE=1, and license it with VULKRO_LICENSE_B64 or vulkro-sf activate. A scan then sends nothing anywhere. Live-org commands are refused under VULKRO_OFFLINE, because they need HTTPS to your Salesforce instance.

See also: GitHub Actions, GitLab CI, SARIF output