Add merge_gemfile task and pipeline stage (#50 slice 2) - #72
Conversation
Second slice of the Renovate-resilient template work: with the Gemfile now bootstrap-mode (laid down only when missing), this is how baseline Gemfile changes reach existing repos — an in-place merge that never touches what Renovate manages. The merge_gemfile task: - Adds template gems missing from the target into their matching `group ... do` block (with attached comment lines, e.g. renovate manager hints); missing groups are copied verbatim at EOF - Brings along group-local variable assignments that inserted lines reference (transitively — `gem 'puppet', puppet_version` needs puppet_version; the pdk line needs major_puppet_version, which needs puppet_version), inserted at the top of the group in template order - Removes gems listed in remove_gems (plus an attached `# renovate:` comment line) - Never modifies existing gems' version constraints or any other existing content; writes the full template when the file is missing The plan stage resolves the template with the same per-module override chain as the profile (Gemfile.<module_name> beats Gemfile), targets skeleton/Gemfile for pupmod_skeleton repos, runs only against pupmod-ish project types, and reads remove_gems from the session config (puppetsync.plans.sync.merge_gemfile.remove_gems). Verified with 11 new specs (driven by the real baseline template, including an eval check that merged output executes) and an e2e run: a fixture Gemfile with a custom simp-rake-helpers constraint and most of the baseline missing came out with all gems/groups/assignments added, the constraint byte-for-byte intact, remove_gems honored, and a second run reporting "1 unchanged". Refs simp#50 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Nice slice — the hard parts are correct and well-proven: constraints are never revisited (Renovate-bump spec holds byte-for-byte), the transitive assignment fixpoint ( 1. Drop the 2. Latent tgroup = template_parsed[:groups][tmeta[:group]] # nil for a top-level gem
group_body = template_lines[(tgroup[:start] + 1)...tgroup[:end]] # nil[:start] → raisesIt can't trigger today (the baseline template has no top-level gems, and Everything else — parsing, idempotency, removals, missing-group copy, plan scoping/template resolution — checks out. |
…ions - Remove the `warn stdin` debug line (it echoed the full template to stderr on every invocation) - Guard the group-local assignment block for top-level gems: it dereferenced template_parsed[:groups][nil], so the first bare `gem 'foo'` added to the baseline template would have raised NoMethodError on every existing repo. Covered by a new spec (the one addition case the suite didn't exercise) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Both taken in 349c8cf:
🤖 Generated with Claude Code |
Second slice of #50: with the Gemfile in bootstrap mode after #70 (laid down only when missing), this adds the other half — an in-place merge stage that delivers baseline Gemfile changes to existing repos without ever touching what Renovate manages. Pairs with #70 but has no code overlap and merges independently in either order.
The
merge_gemfiletaskgroup ... doblock, together with attached comment lines (e.g.# renovate:manager hints). Groups missing entirely are copied verbatim at EOF.gem 'puppet', puppet_versionneedspuppet_version; thepdkline needsmajor_puppet_version, which itself needspuppet_version. They're inserted at the top of the group in template order. (The e2e caught this class of bug before the specs did — a merged Gemfile that referenced assignments it didn't have wouldNameErroron evaluation; there's now a spec thatevals the merged output.)remove_gems(plus an attached# renovate:comment) — the mechanism for cleanups like the oldpuppet-lint-empty_string-checkremoval.{changed, added, removed}JSON, so no-change repos flow into Full idempotency: repos that need no changes must pass through cleanly #49'sunchangedbucket.The pipeline stage
merge_gemfile(opt-in via the session config'sstageslist, like everything else):Gemfile.<module_name>beatsGemfile), viafile()skeleton/Gemfileforpupmod_skeletonrepos; only runs against pupmod-ish project typesremove_gemscomes from the session config (puppetsync.plans.sync.merge_gemfile.remove_gems)Verification
modules/profile/files/pupmod/Gemfile: no-op on identical file, constraint preservation under a simulated Renovate bump, group-end insertion, attached comments, missing-group creation, transitive assignment ordering + anevalof the merged result, removals, custom-gem preservation, idempotency, param validation. 167 examples total, 0 failures.file://fixture (temp config, not committed): a Gemfile with a customsimp-rake-helpersconstraint and most of the baseline missing came out with all gems/groups/assignments added, the constraint byte-for-byte intact,remove_gemshonored, the merged file passing an evaluation check — and a second run reporting0 ok / 1 unchanged / 0 failed.puppet parser validate --tasks,bolt plan show, stage-list dry run, and the CI idempotency e2e all green.Slice 3 (the psych-pure YAML merger for workflow files) is next; once it lands,
profile::github_actionsflips to bootstrap via Hiera and #50 closes.Refs #50
🤖 Generated with Claude Code