The TigerGate CLI runs the full code scan suite in your pipeline — SAST + SCA +
secrets + quality + IaC (plus a container-image scan when you pass --image) —
uploads findings to the platform, and by default enforces your quality gate to
block the build on new high-severity issues.
It’s distributed as the Docker image tigergate/tigergate-cli, whose entrypoint
is tigergate. Every scan engine is pre-baked, so there’s nothing to provision
on runners.
The scanner image
tigergate/tigergate-cli:1.0.0 # exact version pin (recommended — reproducible)
tigergate/tigergate-cli:latest # rolling latest
Each release publishes an exact MAJOR.MINOR.PATCH tag plus a moving :latest.
Pin the exact version in CI for deterministic builds. Run a scan directly — the
primitive behind every snippet on this page:
docker run --rm \
-e TIGERGATE_API_KEY \
-v "$PWD":/workspace \
-w /workspace \
tigergate/tigergate-cli:latest \
scan --type all --scan-scope full --upload
-e TIGERGATE_API_KEY — pass the CI key in; don’t bake it into the image.
-v "$PWD":/workspace -w /workspace — mount the repo and run inside it.
scan --type all --scan-scope full --upload — subcommand + flags (CLI reference). The gate runs by default; add --quality-gate=false for report-only.
The container exits 0 when the gate passes and non-zero when a finding matches
--fail-on (default critical,high) or the scan errors.
The image only needs network to reach your region’s platform API — https://api.tigergate.dev/api/ci
on us1, the default, and the --region flag picks any other (Regions) —
plus outbound access to refresh public vulnerability feeds. TIGERGATE_API_URL overrides
the region for a self-hosted instance.
The CLI reads its scan config and gate policy from the platform, so most teams
never touch a flag. Configure it once and every pipeline run picks up the right
config.
Under CI/CD → Quality Gates, edit the Organization Default (or add a
per-repo override). The gate is a set of numeric thresholds — a build fails when
a count exceeds its limit:
| Threshold | Default |
|---|
| Max new critical | 0 (any new CRITICAL fails) |
| Max new high | 10 |
| Max new medium / low | unlimited (-1) |
| Max complexity / duplication % | 25 / 5 |
| Min coverage · blocked licenses | optional |
To disable the gate for a run (scan + upload, never fail the build), pass
--quality-gate=false. See Quality gates for
the full editor.
2. Create a CI/CD key
Generate the key
Go to Settings → Organization → API Keys, open the CI/CD Keys sub-tab, and click Create CI Key. Give it a name (e.g. ci-cd-quality-gate) and optionally an expiry. Copy the tgci_… value (shown once). See API keys. Add it to your CI's secrets store as TIGERGATE_API_KEY
| CI | Where |
|---|
| GitHub Actions | Repo → Settings → Secrets and variables → Actions |
| GitLab CI | Project → Settings → CI/CD → Variables (mark “Masked”) |
| Bitbucket Pipelines | Repo → Repository settings → Repository variables (Secured) |
| Jenkins | Manage Jenkins → Credentials → Secret text |
| CircleCI | Project → Project Settings → Environment Variables |
| Azure Pipelines / TFS | Pipeline → Variables (mark “Keep this value secret”) |
| AWS CodeBuild | Project → Environment → Environment variables (type Secrets Manager) |
| Google Cloud Build | Secret Manager, referenced via availableSecrets |
| Drone CI | Repo → Settings → Secrets |
| Travis CI | Repo → Settings → Environment Variables |
| Buildkite | Agent secrets hook, or the AWS/GCP/Vault secrets plugin |
| TeamCity | Project → Parameters → password type |
| Tekton | A Kubernetes Secret, referenced from the Task |
Tell the pipeline which region the key belongs to
tgci_ keys are scoped to one region — a key issued in one is
rejected by every other, and the authentication error names the region and endpoint
that were tried.Every snippet below carries the region line next to the key, commented out —
they run as written on us1, the default, and one uncommented line moves a
pipeline to any other region. It is the region that issued the key, not
where the runner happens to be.Set it once per pipeline, wherever that pipeline keeps TIGERGATE_API_KEY:| CI | How |
|---|
| GitHub Actions | TIGERGATE_REGION in env:, or region: <code> on the action, or --region <code> on the CLI |
| Azure Pipelines / TFS | TIGERGATE_REGION in the step’s env:, or region: <code> on the task |
| Jenkins | TIGERGATE_REGION in environment { }, or the REGION build parameter |
| GitLab CI · Bitbucket Pipelines · everything else | TIGERGATE_REGION as a pipeline / repository variable, or --region <code> on the command |
Setting TIGERGATE_API_URL (self-hosted, staging, air-gapped) overrides the region
entirely — pass one or the other, not both. An unknown code fails the build rather
than falling back to us1.
3. Per-CI snippets
The canonical pattern is one scan per trigger:
- Pull / merge request →
--scan-scope diff — scans only the code the PR changed and gates on it. This is the enforcement point.
- Push to the default branch →
--scan-scope full — scans the whole repo.
Add --quality-gate=false to any command to upload results without failing the
build (report-only / observation mode).
Every job runs inside the CLI image (tigergate is on PATH, all GITHUB_*
variables are auto-present, so branch / base / PR context is detected
automatically).name: TigerGate Security
on:
push:
branches: [main]
pull_request:
env:
TIGERGATE_API_KEY: ${{ secrets.TIGERGATE_API_KEY }}
# TIGERGATE_REGION: eu1 # the region that issued the key; omit for us1
jobs:
pr-scan:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
container:
image: tigergate/tigergate-cli:latest
permissions:
contents: read
pull-requests: write # PR summary comment
statuses: write # commit status
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 } # full history for the diff gate
- run: tigergate scan --type all --scan-scope diff --upload
full-scan:
if: github.event_name == 'push'
runs-on: ubuntu-latest
container:
image: tigergate/tigergate-cli:latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- run: tigergate scan --type all --scan-scope full --upload
The GIT_CONFIG_* variables mark the checkout a safe.directory — required
when running the image, because GitLab checks the repo out as a different
user than the one inside the container (git otherwise refuses on “dubious
ownership”). GitLab auto-exports CI_MERGE_REQUEST_* for the CLI to read.variables:
# TIGERGATE_REGION: "eu1" # the region that issued the key; omit for us1
GIT_CONFIG_COUNT: "1"
GIT_CONFIG_KEY_0: "safe.directory"
GIT_CONFIG_VALUE_0: "*"
mr-scan:
image: tigergate/tigergate-cli:latest
script:
- tigergate scan --type all --scan-scope diff --upload
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
full-scan:
image: tigergate/tigergate-cli:latest
script:
- tigergate scan --type all --scan-scope full --upload
rules:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
Export GIT_CONFIG_* in each step (same dubious-ownership fix as GitLab).
Bitbucket auto-exports BITBUCKET_PR_* for PR / base detection.# TIGERGATE_API_KEY is a repository variable. Add TIGERGATE_REGION the same
# way for any region other than us1 — the region that issued the key.
image: tigergate/tigergate-cli:latest
clone:
depth: full
pipelines:
pull-requests:
'**':
- step:
name: PR scan (diff)
script:
- export GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=safe.directory GIT_CONFIG_VALUE_0='*'
- tigergate scan --type all --scan-scope diff --upload
branches:
main:
- step:
name: Full scan
script:
- export GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=safe.directory GIT_CONFIG_VALUE_0='*'
- tigergate scan --type all --scan-scope full --upload
The Docker agent runs the image, so sh steps call tigergate directly.
GIT_CONFIG_* trusts the workspace (owned by a different uid inside the
container). Multibranch PR builds set env.CHANGE_ID and env.CHANGE_TARGET
(the PR base), which the CLI reads.pipeline {
agent {
docker {
image 'tigergate/tigergate-cli:latest'
reuseNode true
}
}
environment {
TIGERGATE_API_KEY = credentials('tigergate-api-key')
// TIGERGATE_REGION = 'eu1' // the region that issued the key; omit for us1
GIT_CONFIG_COUNT = '1'
GIT_CONFIG_KEY_0 = 'safe.directory'
GIT_CONFIG_VALUE_0 = '*'
}
stages {
stage('Security Scan') {
steps {
script {
if (env.CHANGE_ID) {
sh 'tigergate scan --type all --scan-scope diff --upload'
} else {
sh 'tigergate scan --type all --scan-scope full --upload'
}
}
}
}
}
}
Run the job in the image. CircleCI exposes no base branch, so the CLI diffs
against the previous commit (HEAD~1) unless you run a whole-repo scan.version: 2.1
jobs:
tigergate:
docker:
- image: tigergate/tigergate-cli:latest
# TIGERGATE_API_KEY comes from a project env var. Add TIGERGATE_REGION
# the same way for any region other than us1.
steps:
- checkout
- run: tigergate scan --type all --fail-on critical,high --upload
workflows:
ci:
jobs:
- tigergate
Run the whole job inside the image with a container: job. Azure does not
auto-expose secret variables to the script environment, so map
TIGERGATE_API_KEY explicitly under env:. GIT_CONFIG_* trusts the
in-container checkout.trigger:
branches: { include: [main] }
pr:
branches: { include: [main] }
pool:
vmImage: ubuntu-latest
container:
image: tigergate/tigergate-cli:latest
env:
GIT_CONFIG_COUNT: '1'
GIT_CONFIG_KEY_0: safe.directory
GIT_CONFIG_VALUE_0: '*'
steps:
- checkout: self
fetchDepth: 0
# Azure does not auto-expose variables to the script environment, so outside
# us1 name the region here too — the region that issued the key:
# env: { TIGERGATE_API_KEY: $(TIGERGATE_API_KEY), TIGERGATE_REGION: eu1 }
- script: tigergate scan --type all --scan-scope full --upload
condition: ne(variables['Build.Reason'], 'PullRequest')
env: { TIGERGATE_API_KEY: $(TIGERGATE_API_KEY) }
- script: tigergate scan --type all --scan-scope diff --upload
condition: eq(variables['Build.Reason'], 'PullRequest')
env: { TIGERGATE_API_KEY: $(TIGERGATE_API_KEY) }
Team Foundation Server (rebranded Azure DevOps Server for on-prem) runs
the same Pipelines engine and predefined variables as Azure DevOps cloud, so
the CLI detects branch + PR context identically. The only difference: builds
run on a self-hosted agent pool instead of a Microsoft-hosted vmImage.
Use the Azure Pipelines snippet, swapping the pool:pool:
name: 'Default' # your self-hosted agent pool
The agent needs Docker (with the service account able to run containers) and
network access to your region’s CI endpoint — https://api.tigergate.dev/api/ci
on us1, or the api-host of whichever region the region parameter names — or
your self-hosted TigerGate URL via TIGERGATE_API_URL (a common pairing with
on-prem TFS). Set the CodeBuild project’s image to tigergate/tigergate-cli:latest
(Environment → Custom image), pull TIGERGATE_API_KEY from Secrets
Manager, and use a minimal buildspec.yml:version: 0.2
env:
variables:
TIGERGATE_REGION: us1 # the region that issued the key
phases:
build:
commands:
- tigergate scan --type all --scan-scope full --upload
Prefer the default CodeBuild image? Keep it and run the CLI with docker run
in the build phase (CodeBuild provides Docker):- docker run --rm -e TIGERGATE_API_KEY -e TIGERGATE_REGION -v "$PWD":/workspace -w /workspace tigergate/tigergate-cli:latest scan --type all --scan-scope full --upload
Cloud Build checks the source out into /workspace and runs each step from
there, so no volume mount is needed.steps:
- name: 'tigergate/tigergate-cli:latest'
args: ['scan', '--type', 'all', '--scan-scope', 'full', '--upload']
secretEnv: ['TIGERGATE_API_KEY']
# env: ['TIGERGATE_REGION=eu1'] # the region that issued the key; omit for us1
availableSecrets:
secretManager:
- versionName: projects/$PROJECT_ID/secrets/tigergate-api-key/versions/latest
env: 'TIGERGATE_API_KEY'
Drone has no native base-branch variable the CLI reads, so map its target
branch into CI_MERGE_REQUEST_TARGET_BRANCH_NAME for a true PR-vs-base diff.kind: pipeline
type: docker
name: security-scan
steps:
- name: tigergate-scan
image: tigergate/tigergate-cli:latest
environment:
TIGERGATE_API_KEY:
from_secret: tigergate_api_key
# TIGERGATE_REGION: eu1 # the region that issued the key; omit for us1
CI_MERGE_REQUEST_TARGET_BRANCH_NAME: ${DRONE_TARGET_BRANCH}
commands:
- tigergate scan --type all --upload
Store the key as the tigergate_api_key secret (Repo → Settings → Secrets). On a PR build Travis sets TRAVIS_BRANCH to the target branch; map it into
CI_MERGE_REQUEST_TARGET_BRANCH_NAME so the diff gate compares against the
right base.language: minimal
services:
- docker
script:
- >
docker run --rm
-e TIGERGATE_API_KEY
-e TIGERGATE_REGION
-e CI_MERGE_REQUEST_TARGET_BRANCH_NAME=$TRAVIS_BRANCH
-v "$PWD":/workspace -w /workspace
tigergate/tigergate-cli:latest
scan --type all --upload
Buildkite has no native base-branch variable the CLI reads; map
BUILDKITE_PULL_REQUEST_BASE_BRANCH into
CI_MERGE_REQUEST_TARGET_BRANCH_NAME for the diff gate.steps:
- label: "TigerGate scan"
command: "tigergate scan --type all --upload"
plugins:
- docker#v5.11.0:
image: "tigergate/tigergate-cli:latest"
environment:
- "TIGERGATE_API_KEY"
# - "TIGERGATE_REGION=eu1" # the region that issued the key; omit for us1
- "CI_MERGE_REQUEST_TARGET_BRANCH_NAME=${BUILDKITE_PULL_REQUEST_BASE_BRANCH}"
Expose TIGERGATE_API_KEY via your agent’s secrets mechanism. Add a Command Line build step (Docker via a Docker-capable agent). The
Pull Requests build feature provides %teamcity.pullRequest.targetBranch%.docker run --rm \
-e TIGERGATE_API_KEY \
-e TIGERGATE_REGION \
-e CI_MERGE_REQUEST_TARGET_BRANCH_NAME=%teamcity.pullRequest.targetBranch% \
-v "%teamcity.build.checkoutDir%":/workspace -w /workspace \
tigergate/tigergate-cli:latest \
scan --type all --upload
Store the key as a password parameter named env.TIGERGATE_API_KEY. A reusable Task (Tekton / OpenShift Pipelines):apiVersion: tekton.dev/v1
kind: Task
metadata:
name: tigergate-scan
spec:
workspaces:
- name: source
steps:
- name: scan
image: tigergate/tigergate-cli:latest
workingDir: $(workspaces.source.path)
env:
- name: TIGERGATE_API_KEY
valueFrom:
secretKeyRef:
name: tigergate-ci
key: api-key
# - name: TIGERGATE_REGION # the region that issued the key
# value: eu1 # omit for us1
script: |
tigergate scan --type all --upload
Create the Secret: kubectl create secret generic tigergate-ci --from-literal=api-key=tgci_…. Any runner with Docker can call the image directly — Woodpecker, Concourse,
Semaphore, Codefresh, Harness, Bamboo, a self-hosted runner, or a cron job:docker run --rm \
-e TIGERGATE_API_KEY="$TIGERGATE_API_KEY" \
-e TIGERGATE_REGION="${TIGERGATE_REGION:-us1}" \
-v "$PWD":/workspace -w /workspace \
tigergate/tigergate-cli:latest \
scan --type all --upload
To gate on the PR delta, also pass
-e CI_MERGE_REQUEST_TARGET_BRANCH_NAME=<base-branch>; otherwise the CLI
diffs against the previous commit (HEAD~1).
PR-delta detection — gating only on the code a PR introduces — is automatic
on GitHub Actions, GitLab CI, Azure Pipelines (and TFS / Azure DevOps Server,
which share the same variables), and Bitbucket Pipelines: the CLI reads their
native base-branch variables. On every other platform, export your target
branch into CI_MERGE_REQUEST_TARGET_BRANCH_NAME (shown in the snippets
above) for a true PR-vs-base diff, or run a whole-repo gate with
--scan-scope full. Without a base, the CLI diffs against HEAD~1.
4. Scan-type flags
--type all runs every scan type. To narrow or tune a run from the pipeline:
| Flag | Effect |
|---|
--type sast | Run only one type (sast, sca, secrets, quality, iac, image) |
--type sast,secrets | Run only these types |
--type all | Full suite (default) |
--scan-scope diff | Report only PR-changed code (full = whole repo, auto = both; default auto) |
--quality-gate=false | Report-only — upload but never fail the build |
--fail-on critical | Override which severities fail the build (default critical,high) |
--exclude node_modules,dist | Skip paths |
--component web --component-path apps/web | Scope the run to one monorepo component (own gate + own status check) |
Per-run overrides suit one-off triage (e.g. --type secrets --quality-gate=false
for a fast secret sweep). For permanent changes, edit the repo’s dashboard
settings so every PR sees the same policy.
# Secrets-only sweep, no gate
tigergate scan --type secrets --upload --quality-gate=false
# Full scan, fail only on CRITICAL
tigergate scan --type all --upload --fail-on critical
Want findings to link to the running cloud asset? Add the build/deploy metadata
flags — see Code → cloud tracking.
Container-image scanning runs automatically inside --type all when you pass
--image; standalone container scanning lives under
Container Security.
5. Monorepos
With one gate per repository, a frontend PR fails on a pre-existing backend critical the frontend team can’t fix. Split the repo into components — one CI job per component, each with its own gate, its own paths, and its own status check.
Define the components in the dashboard
Under CI/CD → Quality Gates, create one config per component: pick the repository, set Component (e.g. web) and Component paths (e.g. apps/web, libs/shared), then set that team’s thresholds.
Run one job per component
The contract is the same on every CI system, and only three things matter:
- One job per component, each passing
--component <key>. That is what
produces a separate quality gate and a separate status check.
- Full git history — the CLI decides whether a component is affected by
diffing against the PR base branch. A shallow clone has no base ref,
detection fails open, and every component scans.
- Do not let one component’s failure cancel the others, or you cannot
tell “skipped because untouched” from “cancelled by a sibling”.
Anything that can run N jobs can do this. Where the platform has a native
matrix, use it; where it does not, repeat the job per component — the
behaviour is identical.The region is set exactly as in step 3 and every job
inherits it; the docker run snippets below pass TIGERGATE_REGION through
explicitly, because a variable the CI holds is not visible inside the image
unless -e names it. GitHub Actions
GitLab CI
Bitbucket Pipelines
Jenkins
Azure Pipelines / TFS
CircleCI
AWS CodeBuild
Google Cloud Build
Drone CI
Travis CI
Buildkite
TeamCity
Tekton
Any CI (generic)
jobs:
security:
runs-on: ubuntu-latest
container: { image: tigergate/tigergate-cli:latest }
strategy:
fail-fast: false
matrix:
component: [web, api]
permissions:
contents: read
statuses: write
pull-requests: write
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- run: tigergate scan --scan-scope diff --component ${{ matrix.component }}
env:
TIGERGATE_API_KEY: ${{ secrets.TIGERGATE_API_KEY }}
# TIGERGATE_REGION: eu1 # omit for us1
parallel:matrix fans the job out; GIT_DEPTH: 0 gives the full history.variables:
GIT_CONFIG_COUNT: "1"
GIT_CONFIG_KEY_0: "safe.directory"
GIT_CONFIG_VALUE_0: "*"
mr-scan:
image: tigergate/tigergate-cli:latest
variables: { GIT_DEPTH: 0 }
parallel:
matrix:
- COMPONENT: [web, api]
script:
- tigergate scan --scan-scope diff --component "$COMPONENT"
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
No matrix construct — list the steps and wrap them in parallel.
depth: full is required.image: tigergate/tigergate-cli:latest
clone:
depth: full
pipelines:
pull-requests:
'**':
- parallel:
- step:
name: scan web
script:
- export GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=safe.directory GIT_CONFIG_VALUE_0='*'
- tigergate scan --scan-scope diff --component web
- step:
name: scan api
script:
- export GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=safe.directory GIT_CONFIG_VALUE_0='*'
- tigergate scan --scan-scope diff --component api
Declarative pipelines have a native matrix directive.pipeline {
agent none
environment {
TIGERGATE_API_KEY = credentials('tigergate-api-key')
// TIGERGATE_REGION = 'eu1' // omit for us1
GIT_CONFIG_COUNT = '1'
GIT_CONFIG_KEY_0 = 'safe.directory'
GIT_CONFIG_VALUE_0 = '*'
}
stages {
stage('TigerGate') {
matrix {
axes {
axis { name 'COMPONENT'; values 'web', 'api' }
}
agent {
docker { image 'tigergate/tigergate-cli:latest'; reuseNode true }
}
stages {
stage('scan') {
steps {
sh 'tigergate scan --scan-scope diff --component "$COMPONENT"'
}
}
}
}
}
}
}
Set the job to fetch full history (Multibranch: Advanced clone
behaviours → clear any shallow-clone depth). strategy.matrix names each leg; fetchDepth: 0 gives full history.jobs:
- job: tigergate
strategy:
matrix:
web: { COMPONENT: web }
api: { COMPONENT: api }
container: tigergate/tigergate-cli:latest
steps:
- checkout: self
fetchDepth: 0
- script: tigergate scan --scan-scope diff --component "$(COMPONENT)"
env:
TIGERGATE_API_KEY: $(TIGERGATE_API_KEY)
# TIGERGATE_REGION: eu1 # omit for us1 — Azure exposes only what is listed
matrix over a parameterised job. CircleCI exposes no base branch, so the
CLI diffs against HEAD~1 — prefer a full scan per component here unless
you map a base branch in yourself.version: 2.1
jobs:
tigergate:
parameters:
component: { type: string }
docker:
- image: tigergate/tigergate-cli:latest
steps:
- checkout
- run: tigergate scan --component << parameters.component >>
workflows:
security:
jobs:
- tigergate:
matrix:
parameters:
component: [web, api]
Use a batch build — batch: build-matrix runs one build per value.version: 0.2
batch:
build-matrix:
dynamic:
env:
variables:
COMPONENT: [web, api]
phases:
build:
commands:
- |
docker run --rm -v "$PWD":/workspace \
-e TIGERGATE_API_KEY -e TIGERGATE_REGION \
tigergate/tigergate-cli:latest \
scan --scan-scope diff --component "$COMPONENT"
Set the project’s Git clone depth to Full. No matrix construct — one step per component. Steps run sequentially
unless you give them the same waitFor, which runs them in parallel.steps:
- name: tigergate/tigergate-cli:latest
id: scan-web
waitFor: ['-']
args: ['scan', '--scan-scope', 'diff', '--component', 'web']
secretEnv: ['TIGERGATE_API_KEY']
# env: ['TIGERGATE_REGION=eu1'] # omit for us1
- name: tigergate/tigergate-cli:latest
id: scan-api
waitFor: ['-']
args: ['scan', '--scan-scope', 'diff', '--component', 'api']
secretEnv: ['TIGERGATE_API_KEY']
# env: ['TIGERGATE_REGION=eu1'] # omit for us1
Cloud Build shallow-clones by default; unshallow first if you want a true
base-branch diff. One step per component. Drone exposes no base branch the CLI reads, so map
its target branch in.kind: pipeline
type: docker
name: security-scan
clone:
depth: 0
steps:
- name: scan-web
image: tigergate/tigergate-cli:latest
environment:
TIGERGATE_API_KEY: { from_secret: tigergate_api_key }
# TIGERGATE_REGION: eu1 # omit for us1
CI_MERGE_REQUEST_TARGET_BRANCH_NAME: ${DRONE_TARGET_BRANCH}
commands:
- tigergate scan --scan-scope diff --component web
- name: scan-api
image: tigergate/tigergate-cli:latest
environment:
TIGERGATE_API_KEY: { from_secret: tigergate_api_key }
# TIGERGATE_REGION: eu1 # omit for us1
CI_MERGE_REQUEST_TARGET_BRANCH_NAME: ${DRONE_TARGET_BRANCH}
commands:
- tigergate scan --scan-scope diff --component api
The build matrix is an env list.language: minimal
services: [docker]
git:
depth: false
env:
- COMPONENT=web
- COMPONENT=api
script:
- docker run --rm -v "$PWD":/workspace
-e TIGERGATE_API_KEY -e TIGERGATE_REGION
tigergate/tigergate-cli:latest
scan --scan-scope diff --component "$COMPONENT"
One step per component (or a matrix on newer agents). Map Buildkite’s base
branch in for the diff.steps:
- label: "TigerGate :: web"
command: "tigergate scan --scan-scope diff --component web"
plugins:
- docker#v5.11.0:
image: "tigergate/tigergate-cli:latest"
environment:
- "TIGERGATE_API_KEY"
# - "TIGERGATE_REGION=eu1" # omit for us1
- "CI_MERGE_REQUEST_TARGET_BRANCH_NAME=${BUILDKITE_PULL_REQUEST_BASE_BRANCH}"
- label: "TigerGate :: api"
command: "tigergate scan --scan-scope diff --component api"
plugins:
- docker#v5.11.0:
image: "tigergate/tigergate-cli:latest"
environment:
- "TIGERGATE_API_KEY"
# - "TIGERGATE_REGION=eu1" # omit for us1
- "CI_MERGE_REQUEST_TARGET_BRANCH_NAME=${BUILDKITE_PULL_REQUEST_BASE_BRANCH}"
Set the pipeline’s clone flags so the checkout is not shallow. No YAML matrix. Either add one build step per component to a single
configuration, or template a build configuration and instantiate it once
per component — the latter gives each its own build status.# one build step per component, Command Line runner
docker run --rm -v "%teamcity.build.checkoutDir%":/workspace \
-e TIGERGATE_API_KEY="%env.TIGERGATE_API_KEY%" -e TIGERGATE_REGION \
tigergate/tigergate-cli:latest \
scan --scan-scope diff --component web
Set VCS root checkout depth to full. A matrix on the pipeline task (Tekton Pipelines v0.44+) fans out one
TaskRun per component.apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
name: tigergate-scan
spec:
tasks:
- name: scan
matrix:
params:
- name: component
value: [web, api]
taskRef:
name: tigergate-scan-task
The referenced Task runs the image with
scan --scan-scope diff --component $(params.component). With no matrix support at all, a shell loop is enough — but run each
component in its own job if you want independent status checks.for COMPONENT in web api; do
docker run --rm \
-v "$PWD":/workspace \
-e TIGERGATE_API_KEY -e TIGERGATE_REGION \
tigergate/tigergate-cli:latest \
scan --scan-scope diff --component "$COMPONENT" || FAILED=1
done
exit ${FAILED:-0}
The || FAILED=1 matters: without it the loop stops at the first failing
component and the rest never report. Require the component checks in branch protection
Each job reports as TigerGate Security (web) / TigerGate Security (api). Add those contexts instead of the single TigerGate Security one.
Keep one unscoped job on the default branch
Component runs only contain their own findings. Run tigergate scan --scan-scope full with no --component on merges to the default branch so the dashboard keeps the whole-repo inventory.
The CLI decides which components a PR concerns by diffing against the base branch and intersecting the changed files with each component’s paths. Untouched components exit 0 in seconds with a passing status and create no scan run.
Don’t filter component jobs with on.pull_request.paths (or GitLab rules:changes). A required check whose job never runs leaves the PR pending forever. Let every component job start and let the CLI skip the irrelevant ones.
Component model, path precedence, scoping rules and branch-protection detail: Monorepos.
What the scan does
- Detect repo + branch + PR context from the CI environment.
- Scan (parallel): SAST + SCA + secrets + quality + IaC (+ image with
--image), plus SBOM.
- Upload findings to the platform.
- Evaluate the quality gate and post commit status + PR comment.
- Exit
0 when the gate passes (or --quality-gate=false); exit non-zero when a finding matches --fail-on.
The gate decision:
- The CLI fails the build on new
CRITICAL / HIGH findings by default (--fail-on critical,high); --quality-gate=false makes it upload-only.
- On GitHub / GitLab / Azure / Bitbucket the CLI detects the PR’s base branch, distinguishing new-code findings from the existing backlog (elsewhere it diffs against
HEAD~1).
- The dashboard Quality Gate layers numeric thresholds on top — max new critical / high / medium / low, complexity, duplication, min coverage, blocked licenses — counting new-code-only or the whole codebase per its New code only setting.
Tune it under CI/CD → Quality Gates — see Quality gates.
When the connected provider credential (GitHub App / Azure DevOps PAT / GitLab
token / Bitbucket App Password) has PR-write permission, TigerGate posts a
summary comment with the gate verdict and a link to the findings. Inline
comments and AI-suggested fixes come from the AI code review — see
AI Code Review. Report-only mode
(--quality-gate=false) still posts comments; the build just doesn’t fail.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|
401 Unauthorized on upload | Bad / revoked API key | Recreate the CI/CD key, update the CI secret |
| Gate keeps passing despite findings | Thresholds too loose, or --quality-gate=false set | Lower the max-counts under CI/CD → Quality Gates; remove --quality-gate=false |
| Gate fires on backlog findings | Thresholds too strict for existing code | Raise the max-counts or enable New code only under CI/CD → Quality Gates |
dubious ownership / git errors in-container | Missing safe.directory config | Set GIT_CONFIG_COUNT / GIT_CONFIG_KEY_0 / GIT_CONFIG_VALUE_0 as shown in the GitLab / Bitbucket / Jenkins / Azure tabs |
| Diff gate scans the whole repo | No base branch detected | Export CI_MERGE_REQUEST_TARGET_BRANCH_NAME, or ensure fetch-depth: 0 |
| IaC findings not appearing | IaC needs --type all or --type iac | Confirm the scan type; see IaC scanning |
| Unknown-flag error | Outdated image predates the flag | docker pull tigergate/tigergate-cli:latest |
| PR stuck on a required check that never reports | A component job was filtered out with paths / rules:changes, so it never ran and never posted its status | Remove the path filter and let every component job start — the CLI skips untouched components itself and still posts a passing status |
| Component job scans the whole repo, or never skips | No component paths resolved. --component alone selects the gate and check name but does not scope findings or enable the skip | Pass --component-path, or set Component paths on that component’s gate; see Monorepos |
| One component’s verdict overwrites another’s on the same commit | Both jobs ran without --component, so both posted the same status context | Give each job its own --component so the contexts differ |
| New-code gate reports every check as skipped | New-code mode is on but the run carried no new-code signal | Add --scan-scope diff, or set a new-code baseline for the repo + branch; see Quality gates |
Air-gapped runners
Pull the image once and mirror it to your internal registry:
docker pull tigergate/tigergate-cli:latest
docker tag tigergate/tigergate-cli:latest registry.internal.example.com/tigergate/tigergate-cli:latest
docker push registry.internal.example.com/tigergate/tigergate-cli:latest
Then use registry.internal.example.com/tigergate/tigergate-cli:latest in the
snippets above, and point TIGERGATE_API_URL at your self-hosted TigerGate
instance.