Quality Gates and Promotion
Scenario: A scanner publishes a dashboard, but deployment starts without checking its result against policy.
A dashboard link is not the same thing as a decision
It's easy to assume that if a security scanner or code-quality tool is "hooked up" to your pipeline - producing a nice dashboard with charts - that your pipeline is automatically enforcing that quality bar. It usually isn't, unless something explicitly checks the result of that analysis and stops the pipeline when it fails. Publishing a report and enforcing a gate are two genuinely separate things.
Analogy: Think of a car that has a working "check engine" dashboard light, but where the driver has never once actually checked it before pulling out of the driveway. The light (the scanner's dashboard) can be technically working perfectly, faithfully reporting real problems - but if nobody's actual decision to drive depends on checking it first, it's providing information without providing any real enforcement. A genuine "quality gate" is the equivalent of a car that literally won't start until the check-engine light is confirmed off - the pipeline actively waits for and reads the result, and stops if it's bad, rather than just displaying it somewhere for anyone curious enough to go look.
Everything in this section is about closing that gap: making sure a scan result doesn't just exist somewhere, but actually, mechanically blocks the pipeline when it should.
Pipeline example
stage('Gate') { steps { timeout(time: 10, unit: 'MINUTES') { sh './quality-gate.sh' } } }
Publishing analysis does not, by itself, enforce it
A security or quality scanner may upload its results and let the Jenkins step that triggered it return successfully before the scanner's own server has finished processing that submission - meaning "the scan step ran" and "the scan result is ready" can be two different moments in time. A dashboard link existing proves that analysis was submitted, not that it passed any particular policy. A real gate stage must actively wait or poll for the specific analysis task associated with this build, translate that external system's status into Jenkins' own result semantics (pass/fail/unstable), and wrap that waiting in a timeout so a hung or unresponsive scanning service can't hold your pipeline hostage forever. Explicitly distinguish "the policy was actually violated" from "the scanner service was unreachable," from "the response Jenkins got back was malformed," and from "authentication to the scanning service failed" - these are four very different problems needing four different responses.
[Pipeline] { (Gate)
[Pipeline] timeout
[Pipeline] {
[Pipeline] sh
+ ./quality-gate.sh
Waiting for analysis task ab12cd34...
Analysis complete: QUALITY GATE FAILED (2 new critical vulnerabilities)
[Pipeline] }
[Pipeline] }
Finished: FAILURE
This is what a genuinely enforced gate looks like: the pipeline actively polled a specific analysis task by ID, got back an explicit pass/fail verdict, and stopped the build because of it - not because a shell script happened to exit non-zero for an unrelated reason.
Govern the actual thresholds deliberately, and record what was checked
Govern concrete thresholds - new critical vulnerabilities introduced, code-coverage percentage on changed lines specifically (not just overall project coverage, which can hide a badly-tested new feature inside an otherwise well-tested old codebase), license compliance, or supply-chain provenance requirements. Record the scanner's own version, the ruleset version it used, the project key, the source revision, the artifact digest being evaluated, and a link back to the full result - all of this matters later when someone asks "why did this specific build pass or fail this gate." Don't let every individual repository be free to silently disable a mandatory policy on its own initiative, but do support an explicit, reviewed, and audited process for evolving thresholds over time as standards genuinely change.
Gate the exact object that will actually ship
If your pipeline scans digest A but then deploys via a mutable tag like latest that happens to currently resolve to a different digest B, the entire quality gate you just ran is meaningless - you checked one thing and shipped a different thing. Promotion must consume the exact analyzed digest, along with its associated evidence, carried forward explicitly rather than re-resolved by name at deploy time. Any override of a failed gate must record who approved it, why, for how long the exception is valid, which specific artifact it applies to, and what compensating action (if any) is being taken - quietly editing the Jenkinsfile to ignore a non-zero exit code is not a documented waiver, it's an untracked, invisible policy change that the next person reading the Jenkinsfile has no way of knowing was ever a deliberate decision.
A pitfall: treating a scanner outage the same as a passed scan
On an actual policy failure, preserve the reports and route the finding to whoever owns that policy area for follow-up. On a scanner service outage - as opposed to an actual failed check - your pipeline needs to have already decided, in advance, whether it fails closed (blocks delivery until the scanner is back) or invokes a documented, explicitly time-limited exception process. The real incident this section exists to prevent: a scanner service has an outage, the gate-checking script's error handling wasn't written carefully, catches the resulting exception, and defaults to treating "I couldn't reach the scanner" the same as "the scanner said pass" - silently disabling your security policy during exactly the kind of unplanned, unmonitored moment when disabling it matters most. Shell-script parsing accidents and unconsidered default-success behavior must never be the thing quietly making your security decisions.
Goal: Practice the concrete behavior in jenkins-artifact-promotion. Learn sections have no Jenkins terminal; the Jenkins lab opens the real shared service in a new full-screen tab inside your private folder.