This release includes 1 breaking change for platform teams planning a safe upgrade.
✓ No known CVEs patched in this version
Affected surfaces
Summary
AI summaryBroad release touches π Bug Fixes, β BREAKING CHANGES, β¨ Features, and 0.3.0.
Full changelog
0.3.0 (2026-05-20)
β BREAKING CHANGES
- config: .plumber.yaml schema is now per-provider. Existing
v1 files keep working with a deprecation warning; run
plumber config migrateto upgrade on disk.
Update .plumber.yaml from the flat top-level controls: block (legacy v1)
to a per-provider layout where each control lives under gitlab.controls:
or github.controls:. Same control name, different values per platform:
the trusted-registry list on GitLab is registry.gitlab.com/..., on
GitHub it's ghcr.io//..., and there was no way to express that
before. v2 fixes it.
Schema:
- version: "2.0" = current per-provider schema. version: "1.0" =
legacy flat schema (auto-converted in memory at load time, with a
deprecation warning). - Add ProviderConfig / AuthConfig types; PlumberConfig now carries
GitLab/GitHub fields and a ControlsFor(provider) helper. - LoadPlumberConfig detects v1 vs v2 by structural inspection, runs
convertV1ToV2 for legacy files, rejects unsupported version values,
and scans the raw YAML for the deprecated engine: key (warning
only β the field is gone from the struct). - ValidateKnownKeys handles both v1 (controls.X.subkey) and v2
(gitlab.controls.X.subkey, github.controls.X.subkey) paths.
Migration tooling:
- Add
plumber config migratesubcommand. Rewrites a v1 .plumber.yaml
into v2 on disk, preserving comments via the yaml.v3 node API.
Default writes .v2; --in-place overwrites with a .bak backup.
No-op on already-v2 files. Refuses to migrate mixed v1+v2 files
(top-level controls AND a gitlab.controls already present); the user
resolves manually.
Caller migration:
- cmd/legacy_json.go, cmd/render_details.go, cmd/init.go,
control/task.go (evaluatePolicies now takes a provider arg),
control/task_github.go, control/catalog.go (GitLabControls,
FilterFindingsByEnabledControls, DisabledControlNames take
*ControlsConfig directly) β all rewired to read through
ControlsFor("gitlab"|"github") so each provider sees its own values. - 14 legacy getter methods on PlumberConfig
(GetBranchMustBeProtectedConfig, etc.) updated to read from the v2
location. The branch-protection one was gating the collector β
without this fix, branch findings silently disappeared on real
GitLab pipelines.
Side-quest fix that surfaced during validation:
- policies/security_jobs_weakened.rego β _weakening_reason(job) was a
function with three := bodies that could each return a different
string for the same job. Real-world pipelines triggering two of them
at once (e.g. when:manual at job level AND a rules: override) hit
Rego's eval_conflict_error and the engine returned zero findings.
Each weakening signal is now its own deny rule; Rego set semantics
allows multiple findings per job naturally. New regression test
TestIssue410_MultipleWeakeningsOnOneJob.
Default config:
- .plumber.yaml updated to v2 shape with version: "2.0".
Docs:
- New docs/plumber-yaml-v2-migration.md: 30-second migration, before/
after, rationale, backward-compat, anchors for shared values, edge
cases, deprecation timeline, manual cookbook. - Replace the README "Multi-provider configuration (roadmap)"
subsection with a real "Multi-provider configuration" section
showing v2 example, anchor pattern, and a link to the migration
guide.
β¨ Features
- analysis: Rego/OPA engine with GitHub provider and GitLab parity (c487121), closes #148 #148
- config: per-provider YAML schema (v2.0); plumber config migrate; docs (bf84cfd)
- controls: add workflowMustIncludeRequiredActions; fix includes count (8d262f8)
- controls: ISSUE-203 GitHub hardening β expression env + $GITHUB_ENV script writes (all critical) (c85c1ab)
- controls: ship 4 GitHub controls + fix 2 collector caveats (3b903ef)
- github: bench-list gating + GitLab-semantic parity + GHES URL (b5e4293)
- github: branchMustBeProtected β first project-governance control (4200bf1)
- github: close the GitHub-side gaps β wizard, artifacts, filters, parity (2d49870)
- github: honest auth UX β preflight banner + postflight skipped markers (c033cdb)
- github: per-control parity output + skip API when no consumer ships (da2d8a7)
- github: upstream fetch via --github-url + --project (parity with GitLab) (5dca4f5)
π Bug Fixes
- artifact: Correctly display outputs (53f5df8)
- artifact: Flatten rego output from findings to root (5790964)
- comments: Copilot comments (d928465)
- control: Job variables override should not only detect user-overridden sensitive variables (a6dd96f)
- ctrls: Rename issue codes (b21942c)
- dockerfile: Fix image and disable automatic creation of cli (372620e)
- github: branch-protection accuracy + issue #158 (1da1d63)
- github: branchMustBeProtected β populate defaultBranch + drop access-level mapping (1ef4533)
- github: Fix sha bugs in github integration (ae08ae8)
- github: live progress + skip out-of-scope branches in protection fetch (74e2f66)
- issues: Update links to new issues doc (e1d01fc)
- test: Add missing test files) (1637920)
- test: Fix test to include new workflow control (c2e5f6a)
Breaking Changes
- .plumber.yaml schema now perβprovider (v2.0); legacy v1 files are deprecated and must be migrated with `plumber config migrate`.
Weekly OSS security release digest.
The CVE patches and breaking changes that affected production tools this week. One email, every Sunday.
No spam, unsubscribe anytime.
Share this release
About getplumber/plumber
All releases βBeta — feedback welcome: [email protected]