feat(tag-release): support nested field paths in .version-bump.json#47
Merged
feat(tag-release): support nested field paths in .version-bump.json#47
Conversation
Refactor the single-workflow WORKFLOW/SCRIPTS pair into an INLINE_PAIRS list keyed by 'workflow:script', enabling additional workflows (next: tag-release.yml's bump-version-files inline copy) to participate in the same drift-check infrastructure. Existing dependency-cooldown.yml sync coverage is preserved verbatim.
Move the inline bash from tag-release.yml's 'Bump version files' step into a standalone, bats-testable script. Initial coverage is the legacy 'field' codepath (regression baseline) — top-level key write via jq --arg parameterization. Subsequent commits add path_expr support, schema validation, and security-boundary regex coverage.
Three new bats acceptance cases lock in: top-level .version via path_expr (sugar equivalence with field), .packages[0].version (the motivating nexus-mcp case), and .packages[N].version with sibling-index untouched assertion. One additional security-boundary smoke test asserts the validator rejects a path_expr containing a pipe — full per-metacharacter coverage lands in T8, but this smoke test ensures the regex isn't shipping with zero rejection-class coverage in any commit.
…nfigs Three new cases: deeply-nested (3+ levels), two path_expr entries targeting the same file (the canonical nexus-mcp server.json case once it migrates), and a config mixing legacy 'field' with new 'path_expr' entries against different files. The multi-entry case proves the bumper writes both locations before committing the file.
…rors Four cases assert exit code 1 (workflow failure) for: both 'field' and 'path_expr' on one entry, neither key present, missing 'path', and missing top-level 'files' array. The both-keys test additionally asserts the target file is untouched, proving the schema-validation pass runs BEFORE any disk writes.
Ten cases — one per dangerous jq construct: pipe, wildcard, slice, negative index, recursive descent, parens, arithmetic, format string, variable reference, quoted-string key. Each asserts exit 2 (per-entry skip), no file mutation, and a 'skipped (invalid path_expr)' summary row. These tests are the regression suite for the validator's anchored allowlist — any future regex relaxation must continue to reject every case in this file or the security boundary is broken.
Five cases targeting the regex's ^ and $ anchors specifically: leading whitespace, trailing whitespace, unanchored-prefix junk, empty input (which the schema layer catches), and the canonical injection attempt '.version; rm -rf /'. A regression in the regex (e.g. forgetting an anchor) would let any of these through — these tests are the canary.
…d-JSON skip Five cases preserve the inline bumper's existing FILE_PATH guards (absolute path, '..' traversal, missing target, non-JSON extension) and add one new behavior: invalid-JSON targets are now skipped with a warning instead of aborting the script via 'set -euo pipefail' on the jq read failure. Skip-with-warning matches the per-entry treatment of all other filesystem issues.
…tial-progress Three cases close the bats suite: (1) path_expr entries render the full path in the new Path column; (2) legacy field='version' entries render as '.version' (proving Q2-B's display unification); (3) a config with one valid + one invalid entry applies the valid one and skips the invalid one — the 'partial progress is preserved' guarantee called out in the design's failure-modes table.
Replace the inline 'Bump version files' step body with a normalized copy of scripts/bump-version-files.sh between sync-check sentinels, plus a tri-state dispatch wrapper that maps the script's 0/1/2 exit to commit-and-push, fail-workflow, or no-op-with-summary respectively. The check-inline-sync.sh INLINE_PAIRS list gains the third pair to cover this drift.
…yntax Adds a 'Version file bumping' subsection under tag-release.yml's documentation covering the schema (field + path_expr), allowed and rejected path_expr syntax, the security-boundary rationale, and the step-summary table format. The legacy 'field' form was previously undocumented despite being a working consumer-facing contract; shipping path_expr is the natural moment to write the docs that should already exist.
…json One paragraph in the existing 'Using shared-workflows for releases' section pointing consumers to the workflows README's version-bump documentation. Keeps the main README user-focused (here's the workflow, here's how you call it, here's where to read more) without sprawling the schema doc into two places.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
path_exprkey to per-entry.version-bump.jsonschema, accepting strict jq-style path expressions (.packages[0].version,.metadata.semver, etc.) — the existing top-level-onlyfieldshorthand continues to work unchanged.tag-release.ymlintoscripts/bump-version-files.shwith 36 bats cases covering acceptance, schema-rejection, path-expression rejection (one per dangerous jq metachar), anchor-bypass attempts, and filesystem-safety regressions.scripts/check-inline-sync.shto cover multiple workflows.Fixes #44
Release category
feat(tag-release):— semver minor bump →v2.5.0. Strictly additive; existing legacy-fieldconfigs continue to work byte-identically. Floating@v2callers automatically inherit the new capability.Per global commit-prefix rule: every commit subject in this PR uses
feat(tag-release):,test(tag-release):, ordocs(tag-release):— none usechore:orrefactor:even though some commits add brand-new test files. The PR-level intent (introduce a new capability) governs the prefix.Design
See
docs/superpowers/specs/2026-04-19-version-bump-nested-paths-design.mdfor the full design rationale, including:fieldandpath_exprkeep separate jq invocation paths despite schema unificationBump version filesstep body became its own bats-tested script(Note: spec/plan files live under
docs/superpowers/per local-only convention; not committed in this PR.)Test plan
batsjob: 36 new cases undertests/bump-version-files.batsplus the existing test files (53 total)inline-syncjob: three OK lines (addedtag-release.yml:bump-version-files.shpair)lint-workflow-calljob: unchanged, must remain greenTag Releasedispatch against a.version-bump.jsonwith both.versionand.packages[0].versionentriesFollow-ups (filed before this PR opened)
["kebab-case"]) for path_expr[*]selectors for path_exprBoth are strictly additive future minor releases. Neither is in scope here.
Out of scope