services/api still carries a pre-existing critical — a finding the frontend team can neither fix nor merge past.
Components fix this. A component is a named subtree of the repository. It owns a set of paths, has its own thresholds, and posts its own status check, so a PR is only ever blocked by the components it actually touches.
What a component is
Two things: a key (web) and the paths it owns (apps/web, libs/shared).
The key selects which quality gate config applies and names the PR status check. The paths decide which findings count and whether the component cares about a given PR at all.
Define your components
Paths resolve from the first source that provides them:CLI flags (highest precedence)
TG_COMPONENT and TG_COMPONENT_PATH.A component's own .tigergate.yml
Useful when the component directory should carry its own configuration.Note the singular
component key alongside component_paths.The platform
Set Component and Component paths on the quality gate under CI/CD → Quality Gates, and every CI job for that component inherits them — no paths repeated in pipeline YAML.
How a component knows a PR isn’t its business
The CLI diffs the PR against its base branch and intersects the changed files with the component’s paths. This happens before any scanning. When nothing intersects, the CLI printsComponent "api" owns none of the 1 file(s) changed in this PR (services/api) — skipping scan, posts a passing status for that component, creates no scan run, uploads nothing, and exits 0.
| PR touches | --component web job | --component api job |
|---|---|---|
apps/web/** only | scans, web gate applies | skips, passes |
services/api/** only | skips, passes | scans, api gate applies |
| both | scans, web gate applies | scans, api gate applies |
| docs or root files only | skips, passes | skips, passes |
GITHUB_BASE_REF, CI_MERGE_REQUEST_TARGET_BRANCH_NAME, SYSTEM_PULLREQUEST_TARGETBRANCH or BITBUCKET_PR_DESTINATION_BRANCH — falling back to HEAD~1.
Detection fails open: when the diff can’t be computed (not a PR build, a shallow clone, no base ref), the component is treated as touched and is scanned. A component is never skipped on a guess.
Check out full history so the diff can be computed —
fetch-depth: 0 on GitHub Actions, GIT_DEPTH: 0 on GitLab, depth: full on Bitbucket. Without it every component falls back to scanning.--skip-untouched=false to scan regardless of the diff. That’s the right setting for a nightly or post-merge job.
What a component run contains
Findings outside the component’s paths are dropped before upload, so the component’s thresholds only ever see code it owns. This covers SAST, quality, secrets, IaC, SBOM packages and SCA vulnerabilities. Details worth knowing:- Prefix matching is path-segment aware.
apps/webmatchesapps/web/src/a.tsbut notapps/webhooks/x.ts. - A single component path also narrows the scanners themselves, so a component scan doesn’t pay for the whole monorepo. Reported paths stay repo-root-relative either way. A path that isn’t a real directory falls back to scanning the repo and relying on the filter, rather than silently scanning nothing.
- Dependencies that can’t be attributed to a path are kept, not dropped, so a component is never reported as cleaner than it is.
- Component scoping composes with
--scan-scope diff. With both, a finding must be inside the component and in the PR’s changed code to count.
Status checks and branch protection
Each component posts its own check, so components never overwrite each other’s verdict on the same commit:| Component | Check name |
|---|---|
| none (whole repo) | TigerGate Security |
web | TigerGate Security (web) |
api | TigerGate Security (api) |
TigerGate / Quality Gate (web).
Which gate judged the build
Configs resolve most-specific-first:- Repository matches and component matches
- Repository matches, component blank — the repo-wide gate
- Repository blank and component matches — an org-wide rule for that component name
- Repository blank, component blank — the Organization Default
Quality gate: acme/mono / component web, so an unconfigured component is visible in the build log instead of being a surprise.
The component is read from the recorded scan run, not from the request, so a pipeline cannot point itself at a laxer component’s thresholds.
Wiring your pipeline
One CI job per component. Only three things matter, on any platform:- One job per component, each passing
--component <key>— that is what produces a separate gate and a separate status check. - Full git history, so the base-branch diff can be computed.
- Independent failure, so one component’s failing gate does not cancel the rest.
--component <key>, so re-scoping a component becomes a UI edit. The component key itself must stay in the pipeline — the platform can say what a component owns, but only the job knows which component it is.
For threshold semantics — per-severity limits, new-code mode, license blocking and coverage floors — see Quality gates.