Skip to content

Keep metadata.json dependencies up-to-date dynamically via the Forge API (single lookup pass) #68

Description

@silug

Problem

Keeping metadata.json dependencies current across ~90 modules is manual and stale-prone, and the existing tooling has aged out:

  • dist/puppetsync/tasks/modernize_metadata_json.rb is static: hardcoded version requirements (puppetlabs/stdlib '>= 8.0.0 < 10.0.0', puppet/systemd '>= 4.0.2 < 7.0.0'), one-off rename rules (herculesteam→puppet, trlinkin→puppet), and a static data/version_requirements.json lookup table. Every new release cycle means editing the task.
  • Its bump_version helper also hardcodes a specific author line and an iptables-specific changelog comment — leftovers from the last session it was used for.

There is a working dynamic prototype outside the repo (update-metadata): for each dependency it queries the Forge API (/v3/modules/<slug>), follows superseded_by chains to rename moved/deprecated modules, checks whether the current release satisfies the existing version_requirement, and if not bumps the upper bound to < next major (keeping the lower bound; skipping requirements that aren't a clean two-bound range). That logic is right — but it does per-process lookups, which multiplied across ~90 repos would hammer the Forge API with mostly-duplicate requests.

Proposed design

A new pipeline task (replacing the static parts of modernize_metadata_json) with a single pass of version lookups shared across all repos:

  1. Collect: scan every repo's metadata.json (dependencies + simp.optional_dependencies) and build the set of unique module slugs. Across the whole org this is a few dozen unique modules, not hundreds of requests.
  2. Resolve once: one Forge API request per unique slug, following superseded_by chains (chain hops also cached), producing a resolution table: slug → {current_slug, current_version, deprecated?}.
  3. Apply per repo: rewrite each repo's dependencies from the table — rename superseded modules, bump the upper bound to < nextmajor(current_version) when the current release falls outside the existing range, leave in-range requirements untouched, and warn-and-skip anything that isn't a two-bound range.

Implementation notes:

  • The simplest way to get the shared cache is to make this a single task invocation over all repo paths (the pattern checkout_git_feature_branch_in_each_repo already uses), rather than one task process per repo. Alternatively a pre-stage fetches the table once and passes it as a task parameter.
  • Version bump + changelog entry generation should be parameterized (message/author from the session config), not hardcoded like the current bump_version.
  • JSON needs no comment preservation, so this is JSON.parse + pretty_generate like the other metadata tooling.
  • Retire data/version_requirements.json and the hardcoded transform rules once this lands.

Relationship to other work

  • Renovate: Renovate manages pinned dependencies well, but metadata.json carries version ranges with semantics Renovate doesn't handle for us today (supersede-following, bump-upper-bound-only). This task keeps range maintenance in puppetsync, consistent with the Renovate/puppetsync division of labor (versions vs. structure) — if Renovate's puppet datasource ever covers this properly, we can hand it over.
  • Renovate-resilient templates: split file management into bootstrap and in-place merge stages #50: this is a natural fit for the in-place merge stage family — it updates an existing file while preserving everything it doesn't understand.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions