@docker-doctor/cli@0.3.0
Minor Changes
-
77aed82: Replace the saturating score formula with a monotonic, asymptotic curve.
The score used to be
100 - penalty(floored at 0), which meant any project with roughly 10 errors or 25 warnings scored exactly 0 and stayed there no matter how much worse it got — the score became inert on messy repos, and score-regression checks (e.g. "re-scan, confirm the score did not drop") stopped working once a project hit the floor.The score is now
round(100 * e^(-penalty / 70)), using the same per-severity penalty weights as before (error 10, warning 4, info 1). This curve approaches 0 without ever getting stuck there, so the score keeps moving and stays meaningful across the whole range.Breaking:
- Score values have changed. A given set of diagnostics will now produce a different numeric score than before (e.g. a single error used to score 90, now scores ~87; a project that used to floor at 0 will now show a small nonzero number that keeps decreasing as issues pile up).
- Existing badges, dashboards, or stored scores will show different numbers after upgrading — this is expected, not a regression.
- The
Excellent 🏆/Good ✅/Needs Work ⚠️/Critical 🚨label thresholds (>=90 / >=75 / >=50 / below) are unchanged. - The JSON report now includes a
schemaVersionfield (currently2) to make future score/shape changes detectable.
Patch Changes
-
ff17a27: The Dockerfile parser now understands heredoc (
<<EOF) syntax — bodies are folded into the owning instruction instead of being mis-parsed as separate instructions — so content-based rules now inspect heredoc commands and phantom instructions no longer appear. -
21fb2e8:
effectis no longer a runtime dependency of@docker-doctor/cli. The singleSchema.Structused to validatedocker-doctor.config.*and the threeData.TaggedErrorerror classes (ConfigError,ParseError,FileNotFoundError) have been replaced with a hand-rolled validator and plainErrorsubclasses that preserve the exact same validation rules, error_tagdiscriminants, and public API. The package's public types no longer referenceeffect/Schema, so consumers no longer pull in a pinned pre-release beta package for a tiny surface area. -
9b855c5: Fixed the "Docs:" link printed after every scan. It previously pointed at
https://github.com/PunGrumpy/docker-doctor/docs, which 404s (there is nodocs/directory in the repository). It now points athttps://docker-doctor.vercel.app, the project's actual documentation site. -
859f48c: Fix four rule-specificity bugs in the Dockerfile rule engine:
no-root-usernow resets its tracked user at eachFROM(build stage), instead of letting aUSERset in an earlier stage (e.g. a builder) silently satisfy the check for a later stage that never sets one. Multi-stage Dockerfiles may now surface a new diagnostic here.no-add-remote,prefer-copy-over-add, anduse-dockerignorenow read the first non-flag operand ofADD/COPYinstead of always taking the first whitespace-separated token, so a leading flag like--chownor--fromno longer silently disables the check (e.g.ADD --chown=node:node https://...now correctly tripsno-add-remote). As a side effect,use-dockerignorenow explicitly skipsCOPY --from=<stage>since that never reads the build context. These fixes may surface new diagnostics.no-secrets-in-envno longer matches secret keywords as an unanchored substring, so keys likeAUTHORorOAUTH_ISSUER_URLno longer false-positive as "potential secret found" (this is the only error-severity rule, so this previously failed CI on Dockerfiles with no secrets). Genuine segment matches likeDB_PASSWORDstill match. This removes diagnostics.order-layersnow resets its copy-tracking at each build stage, so a copy-everything in an earlier stage no longer implicates an install command in a later, correctly-ordered stage, and no longer substring-matches"src"anywhere in a path (e.g./usr/src/lib,mysrcdir). This removes diagnostics.
These are bug fixes to rule specificity; expect docker-doctor scores to move on existing Dockerfiles as false negatives are corrected and false positives are removed.
-
a5641ab: Piped or redirected
--json/--scoreoutput is no longer silently truncated. The scan action's output paths usedprocess.exit()right afterconsole.log(), which could abort the process before the async write to a pipe/file finished flushing. They now setprocess.exitCodeand return, letting Node exit naturally once stdout has fully drained. Exit codes (--scorebelow 50 exits 1;--json/default exit 1 on any error-severity diagnostic) are unchanged.The score threshold table (labels/emoji at 90/75/50/0) is now exported as
SCORE_BUCKETS(andgetScoreBucket) from@docker-doctor/core, replacing what used to be duplicated inline logic incalculateScore. The printed/JSONlabelstrings are byte-identical to before. -
b4cc9a3: Fix image-reference parsing in
pin-image-versionandprefer-slim-base, which both used to split on the first:and got several common forms wrong:- Images with a registry port (
myregistry.example.com:5000/team/app) are no longer mistaken for pinned/tagged just because the port number looks like a tag.pin-image-versionnow correctly flags these as unpinned. - Digest-pinned images (
node@sha256:...) are no longer misread as an untagged full-OS image byprefer-slim-base— the strongest possible pin is now recognized and silently accepted by both rules. - Multi-stage build aliases (
FROM builder, referencing an earlierFROM ... AS builder) are no longer flagged as unpinned or non-slim. ${ARG}-driven base images are skipped instead of producing a false diagnostic, since the actual image can't be determined statically.
Image-reference parsing is now handled by a single shared parser (
parseImageRef/collectStageAliasesin@docker-doctor/core) instead of duplicated ad-hoc string splitting in each rule. Expect diagnostics to change on Dockerfiles that use registry ports, digest pins, or multi-stage aliases as theirFROMtarget. - Images with a registry port (