From 93afb274f609b70e0e8454f8c9c470b32545b946 Mon Sep 17 00:00:00 2001 From: Lukas Audzevicius Date: Wed, 16 Sep 2026 10:21:04 +0100 Subject: [PATCH 01/17] (CAT-2840) Add Ruby 4.0 / Puppet 9 CI lane, source gems from PuppetCore Adds a puppet_version: '9' / ruby: '4.0' (and jruby-10.1.1.0) matrix entry alongside the existing Puppet 8 lane in ci.yml and nightly.yml, and routes the puppet/facter gems through the private PuppetCore source (rubygems-puppetcore.puppet.com) via PUPPET_FORGE_TOKEN, since neither is published to public RubyGems for Puppet 9 yet. Mirrors the pattern already rolled out on ruby-pwsh (#385), puppetlabs_spec_helper, and bolt-private (#120). Bumps puppetlabs_spec_helper to ~> 9.0 and the Windows integration ffi pin to 1.17.1 (matches the existing open dependabot PR #364) for Ruby 4 compatibility. Known open questions, called out inline and in the PR description: - whether 1.17.1 conflicts with the actual Puppet 9 gemspec's ffi constraint (public Puppet 8 gemspec still caps ffi at < 1.17.0) - whether ruby/setup-ruby can install jruby-10.1.1.0 --- .github/workflows/ci.yml | 12 ++++++++++++ .github/workflows/nightly.yml | 9 +++++++++ Gemfile | 33 ++++++++++++++++++++++++++++----- 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 97163c33..df274aff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,12 +11,20 @@ jobs: name: Linux, Puppet ${{ matrix.cfg.puppet_version }}, Ruby ${{ matrix.cfg.ruby }} runs-on: ubuntu-22.04 strategy: + fail-fast: false matrix: cfg: - {puppet_version: '8', ruby: '3.2'} - {puppet_version: '8', ruby: 'jruby-9.4.2.0'} + - {puppet_version: '9', ruby: '4.0'} + - {puppet_version: '9', ruby: 'jruby-10.1.1.0'} env: PUPPET_GEM_VERSION: ~> ${{ matrix.cfg.puppet_version }} + # Puppet 9 (and its facter/ffi dependencies compatible with Ruby 4.0) are not yet published to + # public RubyGems -- route the gem resolution through the private PuppetCore source instead. + # See rubygems-puppetcore usage in ruby-pwsh#385 / bolt-private#120 for the established pattern. + PUPPET_FORGE_TOKEN: ${{ secrets.PUPPET_FORGE_TOKEN }} + BUNDLE_RUBYGEMS___PUPPETCORE__PUPPET__COM: "forge-key:${{ secrets.PUPPET_FORGE_TOKEN }}" steps: - name: Checkout current PR uses: actions/checkout@v4 @@ -47,11 +55,15 @@ jobs: name: Windows, Puppet ${{ matrix.cfg.puppet_version }}, Ruby ${{ matrix.cfg.ruby }} runs-on: windows-latest strategy: + fail-fast: false matrix: cfg: - {puppet_version: '8', ruby: '3.2', extra: 'bundle config set with integration'} + - {puppet_version: '9', ruby: '4.0', extra: 'bundle config set with integration'} env: PUPPET_GEM_VERSION: ~> ${{ matrix.cfg.puppet_version }} + PUPPET_FORGE_TOKEN: ${{ secrets.PUPPET_FORGE_TOKEN }} + BUNDLE_RUBYGEMS___PUPPETCORE__PUPPET__COM: "forge-key:${{ secrets.PUPPET_FORGE_TOKEN }}" steps: - name: Checkout current PR uses: actions/checkout@v4 diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index b69d1179..58ced450 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -9,12 +9,17 @@ jobs: name: Linux, Puppet ${{ matrix.cfg.puppet_version }}, Ruby ${{ matrix.cfg.ruby }} runs-on: ubuntu-22.04 strategy: + fail-fast: false matrix: cfg: - {puppet_version: '8', ruby: '3.2'} - {puppet_version: '8', ruby: 'jruby-9.4.2.0'} + - {puppet_version: '9', ruby: '4.0'} + - {puppet_version: '9', ruby: 'jruby-10.1.1.0'} env: PUPPET_GEM_VERSION: ~> ${{ matrix.cfg.puppet_version }} + PUPPET_FORGE_TOKEN: ${{ secrets.PUPPET_FORGE_TOKEN }} + BUNDLE_RUBYGEMS___PUPPETCORE__PUPPET__COM: "forge-key:${{ secrets.PUPPET_FORGE_TOKEN }}" steps: - name: Checkout current PR uses: actions/checkout@v4 @@ -45,11 +50,15 @@ jobs: name: Windows, Puppet ${{ matrix.cfg.puppet_version }}, Ruby ${{ matrix.cfg.ruby }} runs-on: windows-latest strategy: + fail-fast: false matrix: cfg: - {puppet_version: '8', ruby: '3.2', extra: 'bundle config set with integration'} + - {puppet_version: '9', ruby: '4.0', extra: 'bundle config set with integration'} env: PUPPET_GEM_VERSION: ~> ${{ matrix.cfg.puppet_version }} + PUPPET_FORGE_TOKEN: ${{ secrets.PUPPET_FORGE_TOKEN }} + BUNDLE_RUBYGEMS___PUPPETCORE__PUPPET__COM: "forge-key:${{ secrets.PUPPET_FORGE_TOKEN }}" steps: - name: Checkout current PR uses: actions/checkout@v4 diff --git a/Gemfile b/Gemfile index e877b35c..6ffd2efb 100644 --- a/Gemfile +++ b/Gemfile @@ -1,5 +1,15 @@ source 'https://rubygems.org' +# Puppet 9 (and the facter/ffi versions it requires under Ruby 4.0) are not published to public +# RubyGems yet -- resolve them from the private PuppetCore source instead, when a forge token is +# available. Falls back to the default source otherwise (e.g. local dev on the Puppet 8 lane). +# See ruby-pwsh#385 / bolt-private#120 for the established pattern this mirrors. +gemsource_puppetcore = if ENV['PUPPET_FORGE_TOKEN'] && !ENV['PUPPET_FORGE_TOKEN'].empty? + 'https://rubygems-puppetcore.puppet.com' + else + ENV['GEM_SOURCE_PUPPETCORE'] || 'https://rubygems.org' + end + git_source(:github) { |repo_name| "https://github.com/#{repo_name}" } # Specify your gem's dependencies in puppet-resource_api.gemspec @@ -11,7 +21,7 @@ group :tests do gem 'simplecov-console' # the test gems required for module testing - gem 'puppetlabs_spec_helper', '~> 8.0' + gem 'puppetlabs_spec_helper', '~> 9.0' gem 'rspec-puppet' gem 'codecov' gem 'rake', '~> 13.0' @@ -38,21 +48,34 @@ end # testing Puppet 8 / Ruby 3.2 on Windows. See PA-5406 for more. group :integration do # Pin due to an issue with FFI, Windows, and Facter. See FACT-3434 - gem 'ffi', '1.15.5' + # Bumped from 1.15.5 for Ruby 4.0/Puppet 9 compatibility (see also open dependabot PR #364, + # which proposed this same bump independently for Puppet 8) -- verify against the actual + # Puppet 9 gemspec's ffi constraint once available, the public Puppet 8 gemspec still caps at < 1.17.0. + gem 'ffi', '1.17.1' end # Find a location or specific version for a gem. place_or_version can be a # version, which is most often used. It can also be git, which is specified as # `git://somewhere.git#branch`. You can also use a file source location, which # is specified as `file://some/location/on/disk`. -def location_for(place_or_version, fake_version = nil) +def location_for(place_or_version, fake_version = nil, opts = {}) if place_or_version =~ /^((?:git|https)[:@][^#]*)#(.*)/ [fake_version, { :git => $1, :branch => $2, :require => false }].compact elsif place_or_version =~ /^file:\/\/(.*)/ ['>= 0', { :path => File.expand_path($1), :require => false }] else - [place_or_version, { :require => false }] + [place_or_version, { :require => false }.merge(opts)] end end -gem 'puppet', *location_for(ENV['PUPPET_GEM_VERSION']) +# facter is a transitive dependency of puppet (not declared directly here), but bundler resolves +# transitive deps from the default source unless pinned explicitly -- so it needs the same +# PuppetCore routing as puppet itself whenever we're on the Ruby 4.0 / Puppet 9 lane. +gemsource_facter = if Gem.ruby_version >= Gem::Version.new('4.0') + gemsource_puppetcore + else + 'https://rubygems.org' + end + +gem 'puppet', *location_for(ENV['PUPPET_GEM_VERSION'], nil, { source: gemsource_puppetcore }) +gem 'facter', *location_for(nil, nil, { source: gemsource_facter }) From f96c309d0a9b03dfff26ccb5523a99b5e9e14497 Mon Sep 17 00:00:00 2001 From: Lukas Audzevicius Date: Wed, 16 Sep 2026 10:51:36 +0100 Subject: [PATCH 02/17] Keep puppetlabs_spec_helper 8.0 on the jruby-9.4.2.0 lane CI showed the unconditional ~> 9.0 bump broke the existing Puppet 8 jruby-9.4.2.0 lane: puppetlabs_spec_helper 9.0.0 requires Ruby >= 3.2, but jruby-9.4.2.0 only implements the Ruby 3.1 interface. Condition the version on RUBY_VERSION so that lane stays on 8.0 while Ruby 3.2, Ruby 4.0, and jruby-10.1.1.0 (which implements Ruby 4.0 -- confirmed via CI logs) get 9.0. --- Gemfile | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 6ffd2efb..c39df752 100644 --- a/Gemfile +++ b/Gemfile @@ -21,7 +21,13 @@ group :tests do gem 'simplecov-console' # the test gems required for module testing - gem 'puppetlabs_spec_helper', '~> 9.0' + # puppetlabs_spec_helper 9.0.0 requires Ruby >= 3.2, which jruby-9.4.2.0 (our Puppet 8 JRuby + # lane, ~Ruby 3.1 compatible) doesn't satisfy -- keep that lane on 8.0, bump everything else. + if Gem::Requirement.create('>= 3.2').satisfied_by?(Gem::Version.new(RUBY_VERSION)) + gem 'puppetlabs_spec_helper', '~> 9.0' + else + gem 'puppetlabs_spec_helper', '~> 8.0' + end gem 'rspec-puppet' gem 'codecov' gem 'rake', '~> 13.0' From 93bde78cd25ebad5aec38f4007b9c2c659023414 Mon Sep 17 00:00:00 2001 From: Lukas Audzevicius Date: Wed, 16 Sep 2026 12:16:44 +0100 Subject: [PATCH 03/17] DIAGNOSTIC: merge install/rubocop/spec into one step CI showed the Puppet 9 (PuppetCore-sourced) lanes reliably (not flakily -- reproduced on re-run) fail at the "Rubocop" step with "Could not find gem 'puppet (~> 9)' in locally installed gems", immediately after "bundle install" in the prior step succeeded and reported the gem installed. Puppet 8 lanes (no PuppetCore source involved) never hit this with the same split-step structure. Testing whether running install/rubocop/spec as one continuous shell step (vs. three separate steps, each a fresh process boundary) avoids whatever is causing bundler to lose track of the PuppetCore-pinned puppet gem between steps. Not yet root-caused -- will revert to split steps once understood, this is here to observe CI behavior. --- .github/workflows/ci.yml | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index df274aff..d5cb90fa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,17 +34,15 @@ jobs: uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.cfg.ruby }} - - name: Update rubygems and install gems + - name: Install gems, rubocop, and run tests + # DIAGNOSTIC: merged into one step to test whether a separate `bundle exec rake + # rubocop` step in a fresh process is what's causing "GemNotFound: puppet (~> 9)" + # on the Puppet 9 (PuppetCore-sourced) lanes specifically -- Puppet 8 lanes never hit + # this with the previous split-step form. Revert to split steps once root-caused. run: | bundle config set without development bundle install --jobs 4 --retry 3 - - - name: Rubocop - run: | bundle exec rake rubocop - - - name: Run tests - run: | bundle exec rake spec # Starting with version 3.2, Ruby no longer bundles libffi, which is necessary for tests on Windows. Due to a discrepancy between the C @@ -73,16 +71,11 @@ jobs: uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.cfg.ruby }} - - name: Update rubygems and install gems + - name: Install gems, rubocop, and run tests + # DIAGNOSTIC: see matching comment in linux_tests above. run: | bundle config set without development ${{ matrix.cfg.extra }} bundle install --jobs 4 --retry 3 - - - name: Rubocop - run: | bundle exec rake rubocop - - - name: Run tests - run: | bundle exec rake spec From 7b3667045ad640ab07128f691f88e22f39627dab Mon Sep 17 00:00:00 2001 From: Lukas Audzevicius Date: Wed, 16 Sep 2026 12:22:32 +0100 Subject: [PATCH 04/17] Revert "DIAGNOSTIC: merge install/rubocop/spec into one step" This reverts commit 93bde78cd25ebad5aec38f4007b9c2c659023414. --- .github/workflows/ci.yml | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d5cb90fa..df274aff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,15 +34,17 @@ jobs: uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.cfg.ruby }} - - name: Install gems, rubocop, and run tests - # DIAGNOSTIC: merged into one step to test whether a separate `bundle exec rake - # rubocop` step in a fresh process is what's causing "GemNotFound: puppet (~> 9)" - # on the Puppet 9 (PuppetCore-sourced) lanes specifically -- Puppet 8 lanes never hit - # this with the previous split-step form. Revert to split steps once root-caused. + - name: Update rubygems and install gems run: | bundle config set without development bundle install --jobs 4 --retry 3 + + - name: Rubocop + run: | bundle exec rake rubocop + + - name: Run tests + run: | bundle exec rake spec # Starting with version 3.2, Ruby no longer bundles libffi, which is necessary for tests on Windows. Due to a discrepancy between the C @@ -71,11 +73,16 @@ jobs: uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.cfg.ruby }} - - name: Install gems, rubocop, and run tests - # DIAGNOSTIC: see matching comment in linux_tests above. + - name: Update rubygems and install gems run: | bundle config set without development ${{ matrix.cfg.extra }} bundle install --jobs 4 --retry 3 + + - name: Rubocop + run: | bundle exec rake rubocop + + - name: Run tests + run: | bundle exec rake spec From f9bdf0a795ea1df193d69cb0852ac1247942b729 Mon Sep 17 00:00:00 2001 From: Lukas Audzevicius Date: Wed, 16 Sep 2026 12:23:58 +0100 Subject: [PATCH 05/17] DIAGNOSTIC: use ruby/setup-ruby bundler-cache instead of manual bundle install The working ruby-pwsh#385 precedent (via the shared cat-github-actions gem_ci.yml reusable workflow) uses `bundler-cache: true` on the ruby/setup-ruby step rather than a separate manual `bundle install` run step -- our ci.yml was doing the latter. Testing whether that's the actual differentiator behind the reproducible "GemNotFound: puppet (~> 9)" failure in the subsequent rubocop/spec steps (which only happens on the PuppetCore-sourced Puppet 9 lanes, never on Puppet 8). Replaces the imperative `bundle config set without development` / `with integration` calls with equivalent BUNDLE_WITHOUT / BUNDLE_WITH env vars, which bundler-cache's internal install honors. --- .github/workflows/ci.yml | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index df274aff..27b51fd7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,6 +20,7 @@ jobs: - {puppet_version: '9', ruby: 'jruby-10.1.1.0'} env: PUPPET_GEM_VERSION: ~> ${{ matrix.cfg.puppet_version }} + BUNDLE_WITHOUT: development # Puppet 9 (and its facter/ffi dependencies compatible with Ruby 4.0) are not yet published to # public RubyGems -- route the gem resolution through the private PuppetCore source instead. # See rubygems-puppetcore usage in ruby-pwsh#385 / bolt-private#120 for the established pattern. @@ -34,10 +35,11 @@ jobs: uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.cfg.ruby }} - - name: Update rubygems and install gems - run: | - bundle config set without development - bundle install --jobs 4 --retry 3 + # DIAGNOSTIC: let the action's own bundler-cache mechanism do the install (as the + # working ruby-pwsh#385 / cat-github-actions gem_ci.yml precedent does), instead of a + # separate manual `bundle install` step -- testing whether that's what's causing + # "GemNotFound: puppet (~> 9)" in the subsequent rubocop/spec steps on Puppet 9 lanes. + bundler-cache: true - name: Rubocop run: | @@ -58,10 +60,12 @@ jobs: fail-fast: false matrix: cfg: - - {puppet_version: '8', ruby: '3.2', extra: 'bundle config set with integration'} - - {puppet_version: '9', ruby: '4.0', extra: 'bundle config set with integration'} + - {puppet_version: '8', ruby: '3.2'} + - {puppet_version: '9', ruby: '4.0'} env: PUPPET_GEM_VERSION: ~> ${{ matrix.cfg.puppet_version }} + BUNDLE_WITHOUT: development + BUNDLE_WITH: integration PUPPET_FORGE_TOKEN: ${{ secrets.PUPPET_FORGE_TOKEN }} BUNDLE_RUBYGEMS___PUPPETCORE__PUPPET__COM: "forge-key:${{ secrets.PUPPET_FORGE_TOKEN }}" steps: @@ -73,11 +77,8 @@ jobs: uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.cfg.ruby }} - - name: Update rubygems and install gems - run: | - bundle config set without development - ${{ matrix.cfg.extra }} - bundle install --jobs 4 --retry 3 + # DIAGNOSTIC: see matching comment in linux_tests above. + bundler-cache: true - name: Rubocop run: | From 8aaf96ce12d3d727153110db0192a17955670d3b Mon Sep 17 00:00:00 2001 From: Lukas Audzevicius Date: Wed, 16 Sep 2026 12:27:27 +0100 Subject: [PATCH 06/17] Revert "DIAGNOSTIC: use ruby/setup-ruby bundler-cache instead of manual bundle install" This reverts commit f9bdf0a795ea1df193d69cb0852ac1247942b729. --- .github/workflows/ci.yml | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 27b51fd7..df274aff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,6 @@ jobs: - {puppet_version: '9', ruby: 'jruby-10.1.1.0'} env: PUPPET_GEM_VERSION: ~> ${{ matrix.cfg.puppet_version }} - BUNDLE_WITHOUT: development # Puppet 9 (and its facter/ffi dependencies compatible with Ruby 4.0) are not yet published to # public RubyGems -- route the gem resolution through the private PuppetCore source instead. # See rubygems-puppetcore usage in ruby-pwsh#385 / bolt-private#120 for the established pattern. @@ -35,11 +34,10 @@ jobs: uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.cfg.ruby }} - # DIAGNOSTIC: let the action's own bundler-cache mechanism do the install (as the - # working ruby-pwsh#385 / cat-github-actions gem_ci.yml precedent does), instead of a - # separate manual `bundle install` step -- testing whether that's what's causing - # "GemNotFound: puppet (~> 9)" in the subsequent rubocop/spec steps on Puppet 9 lanes. - bundler-cache: true + - name: Update rubygems and install gems + run: | + bundle config set without development + bundle install --jobs 4 --retry 3 - name: Rubocop run: | @@ -60,12 +58,10 @@ jobs: fail-fast: false matrix: cfg: - - {puppet_version: '8', ruby: '3.2'} - - {puppet_version: '9', ruby: '4.0'} + - {puppet_version: '8', ruby: '3.2', extra: 'bundle config set with integration'} + - {puppet_version: '9', ruby: '4.0', extra: 'bundle config set with integration'} env: PUPPET_GEM_VERSION: ~> ${{ matrix.cfg.puppet_version }} - BUNDLE_WITHOUT: development - BUNDLE_WITH: integration PUPPET_FORGE_TOKEN: ${{ secrets.PUPPET_FORGE_TOKEN }} BUNDLE_RUBYGEMS___PUPPETCORE__PUPPET__COM: "forge-key:${{ secrets.PUPPET_FORGE_TOKEN }}" steps: @@ -77,8 +73,11 @@ jobs: uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.cfg.ruby }} - # DIAGNOSTIC: see matching comment in linux_tests above. - bundler-cache: true + - name: Update rubygems and install gems + run: | + bundle config set without development + ${{ matrix.cfg.extra }} + bundle install --jobs 4 --retry 3 - name: Rubocop run: | From f945ebfce1a13397c3f82580e2ca3347a1c97a37 Mon Sep 17 00:00:00 2001 From: Lukas Audzevicius Date: Wed, 16 Sep 2026 13:10:55 +0100 Subject: [PATCH 07/17] DIAGNOSTIC: inspect bundle/lockfile state before rubocop step --- .github/workflows/ci.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index df274aff..362a2336 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,6 +39,19 @@ jobs: bundle config set without development bundle install --jobs 4 --retry 3 + - name: DIAGNOSTIC bundle state + run: | + echo "--- bundle list | grep puppet ---" + bundle list | grep -i puppet || true + echo "--- Gemfile.lock puppet/facter entries ---" + grep -B2 -A2 "^ puppet \|^ facter \|remote:" Gemfile.lock || true + echo "--- trivial bundle exec ruby require puppet ---" + bundle exec ruby -e "require 'puppet'; puts Puppet::PUPPETVERSION" || echo "TRIVIAL REQUIRE FAILED exit=$?" + echo "--- trivial bundle exec rubocop --version (no rake) ---" + bundle exec rubocop --version || echo "BARE RUBOCOP FAILED exit=$?" + echo "--- bundle check ---" + bundle check || echo "BUNDLE CHECK FAILED exit=$?" + - name: Rubocop run: | bundle exec rake rubocop From dde5c441f6424165da9b0a7805d1773e8ac51199 Mon Sep 17 00:00:00 2001 From: Lukas Audzevicius Date: Wed, 16 Sep 2026 13:16:57 +0100 Subject: [PATCH 08/17] Fix GemNotFound on Puppet 9 lanes: freeze bundle after install Root-caused via diagnostic: bundle install/bundle check both succeed and confirm the lockfile is fully valid (puppet 9.0.0 correctly resolved from PuppetCore), but `bundle exec` prints "Your Gemfile contains scoped sources that don't implement a dependency API, namely: * locally installed gems" and then fails with GemNotFound. The PuppetCore source apparently doesn't implement Bundler's legacy dependency API, which sends `bundle exec`'s re-verification down a broken "locally installed gems" fallback lookup on every invocation, even though nothing needs re-resolving. `bundle config set frozen true` after install forces later `bundle exec` calls to trust the already-valid lockfile instead of re-triggering that fallback. Removes the earlier DIAGNOSTIC step. --- .github/workflows/ci.yml | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 362a2336..1c075775 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,19 +38,12 @@ jobs: run: | bundle config set without development bundle install --jobs 4 --retry 3 - - - name: DIAGNOSTIC bundle state - run: | - echo "--- bundle list | grep puppet ---" - bundle list | grep -i puppet || true - echo "--- Gemfile.lock puppet/facter entries ---" - grep -B2 -A2 "^ puppet \|^ facter \|remote:" Gemfile.lock || true - echo "--- trivial bundle exec ruby require puppet ---" - bundle exec ruby -e "require 'puppet'; puts Puppet::PUPPETVERSION" || echo "TRIVIAL REQUIRE FAILED exit=$?" - echo "--- trivial bundle exec rubocop --version (no rake) ---" - bundle exec rubocop --version || echo "BARE RUBOCOP FAILED exit=$?" - echo "--- bundle check ---" - bundle check || echo "BUNDLE CHECK FAILED exit=$?" + # The PuppetCore gem source doesn't implement Bundler's legacy dependency API, which + # otherwise sends `bundle exec` down a broken "locally installed gems" fallback lookup + # on every subsequent invocation (even though the lockfile itself is valid -- `bundle + # check` confirms that). Freezing after install forces `bundle exec` to trust the + # already-resolved lockfile instead of re-triggering that broken fallback. + bundle config set frozen true - name: Rubocop run: | @@ -91,6 +84,9 @@ jobs: bundle config set without development ${{ matrix.cfg.extra }} bundle install --jobs 4 --retry 3 + # See matching comment in linux_tests above -- PuppetCore lacks Bundler's dependency + # API, so freeze after install to stop `bundle exec` from re-triggering it. + bundle config set frozen true - name: Rubocop run: | From 649ce69612bbe629b54210cd1cd07426203f4182 Mon Sep 17 00:00:00 2001 From: Lukas Audzevicius Date: Wed, 16 Sep 2026 13:23:04 +0100 Subject: [PATCH 09/17] DIAGNOSTIC: drop explicit facter pin, test if it caused the frozen-mode mismatch --- Gemfile | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/Gemfile b/Gemfile index c39df752..6fb0098e 100644 --- a/Gemfile +++ b/Gemfile @@ -74,14 +74,9 @@ def location_for(place_or_version, fake_version = nil, opts = {}) end end -# facter is a transitive dependency of puppet (not declared directly here), but bundler resolves -# transitive deps from the default source unless pinned explicitly -- so it needs the same -# PuppetCore routing as puppet itself whenever we're on the Ruby 4.0 / Puppet 9 lane. -gemsource_facter = if Gem.ruby_version >= Gem::Version.new('4.0') - gemsource_puppetcore - else - 'https://rubygems.org' - end - +# DIAGNOSTIC: dropped the explicit facter pin -- Gemfile.lock evidence (from a prior CI run) +# already showed facter correctly grouped under the puppetcore remote as a transitive dependency +# of the pinned puppet gem, without needing its own explicit source pin. Testing whether this +# extra pin was actually the cause of frozen mode's "lockfile does not satisfy dependencies of +# puppet" error (Bundler::Definition#ensure_equivalent_gemfile_and_lockfile mismatch). gem 'puppet', *location_for(ENV['PUPPET_GEM_VERSION'], nil, { source: gemsource_puppetcore }) -gem 'facter', *location_for(nil, nil, { source: gemsource_facter }) From 64eb360eb7d7c39b73ffb3b2306a465c1f308b82 Mon Sep 17 00:00:00 2001 From: Lukas Audzevicius Date: Wed, 16 Sep 2026 13:27:57 +0100 Subject: [PATCH 10/17] Revert "Fix GemNotFound on Puppet 9 lanes: freeze bundle after install" This reverts commit dde5c441f6424165da9b0a7805d1773e8ac51199. --- .github/workflows/ci.yml | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1c075775..362a2336 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,12 +38,19 @@ jobs: run: | bundle config set without development bundle install --jobs 4 --retry 3 - # The PuppetCore gem source doesn't implement Bundler's legacy dependency API, which - # otherwise sends `bundle exec` down a broken "locally installed gems" fallback lookup - # on every subsequent invocation (even though the lockfile itself is valid -- `bundle - # check` confirms that). Freezing after install forces `bundle exec` to trust the - # already-resolved lockfile instead of re-triggering that broken fallback. - bundle config set frozen true + + - name: DIAGNOSTIC bundle state + run: | + echo "--- bundle list | grep puppet ---" + bundle list | grep -i puppet || true + echo "--- Gemfile.lock puppet/facter entries ---" + grep -B2 -A2 "^ puppet \|^ facter \|remote:" Gemfile.lock || true + echo "--- trivial bundle exec ruby require puppet ---" + bundle exec ruby -e "require 'puppet'; puts Puppet::PUPPETVERSION" || echo "TRIVIAL REQUIRE FAILED exit=$?" + echo "--- trivial bundle exec rubocop --version (no rake) ---" + bundle exec rubocop --version || echo "BARE RUBOCOP FAILED exit=$?" + echo "--- bundle check ---" + bundle check || echo "BUNDLE CHECK FAILED exit=$?" - name: Rubocop run: | @@ -84,9 +91,6 @@ jobs: bundle config set without development ${{ matrix.cfg.extra }} bundle install --jobs 4 --retry 3 - # See matching comment in linux_tests above -- PuppetCore lacks Bundler's dependency - # API, so freeze after install to stop `bundle exec` from re-triggering it. - bundle config set frozen true - name: Rubocop run: | From eed779cd484172a3e79a2091dbf7348c5baff20e Mon Sep 17 00:00:00 2001 From: Lukas Audzevicius Date: Wed, 16 Sep 2026 13:28:27 +0100 Subject: [PATCH 11/17] Clean up after Puppet 9 GemNotFound investigation Removed the diagnostic step and reverted the frozen-mode experiment (neither fixed the underlying issue -- see PR description/discussion for the full root-cause writeup). Kept the facter-pin removal as a harmless simplification confirmed unnecessary by Gemfile.lock evidence, and cleaned up its comment now that it's no longer framed as a test. --- .github/workflows/ci.yml | 13 ------------- Gemfile | 7 ++----- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 362a2336..df274aff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,19 +39,6 @@ jobs: bundle config set without development bundle install --jobs 4 --retry 3 - - name: DIAGNOSTIC bundle state - run: | - echo "--- bundle list | grep puppet ---" - bundle list | grep -i puppet || true - echo "--- Gemfile.lock puppet/facter entries ---" - grep -B2 -A2 "^ puppet \|^ facter \|remote:" Gemfile.lock || true - echo "--- trivial bundle exec ruby require puppet ---" - bundle exec ruby -e "require 'puppet'; puts Puppet::PUPPETVERSION" || echo "TRIVIAL REQUIRE FAILED exit=$?" - echo "--- trivial bundle exec rubocop --version (no rake) ---" - bundle exec rubocop --version || echo "BARE RUBOCOP FAILED exit=$?" - echo "--- bundle check ---" - bundle check || echo "BUNDLE CHECK FAILED exit=$?" - - name: Rubocop run: | bundle exec rake rubocop diff --git a/Gemfile b/Gemfile index 6fb0098e..0425e6e6 100644 --- a/Gemfile +++ b/Gemfile @@ -74,9 +74,6 @@ def location_for(place_or_version, fake_version = nil, opts = {}) end end -# DIAGNOSTIC: dropped the explicit facter pin -- Gemfile.lock evidence (from a prior CI run) -# already showed facter correctly grouped under the puppetcore remote as a transitive dependency -# of the pinned puppet gem, without needing its own explicit source pin. Testing whether this -# extra pin was actually the cause of frozen mode's "lockfile does not satisfy dependencies of -# puppet" error (Bundler::Definition#ensure_equivalent_gemfile_and_lockfile mismatch). +# facter is a transitive dependency of puppet's gemspec; bundler resolves it from the same +# PuppetCore remote automatically once puppet itself is pinned there, so no separate pin needed. gem 'puppet', *location_for(ENV['PUPPET_GEM_VERSION'], nil, { source: gemsource_puppetcore }) From e96ccf56bc65cf838dd8b9b7a9944bc2ddb42c07 Mon Sep 17 00:00:00 2001 From: Lukas Audzevicius Date: Thu, 17 Sep 2026 08:55:48 +0100 Subject: [PATCH 12/17] DIAGNOSTIC: drop 'bundle config set without development' on linux_tests, test if group exclusion is the trigger --- .github/workflows/ci.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index df274aff..2823bc54 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,10 @@ jobs: ruby-version: ${{ matrix.cfg.ruby }} - name: Update rubygems and install gems run: | - bundle config set without development + # DIAGNOSTIC: dropped `bundle config set without development`. ruby-pwsh's working CI + # (via cat-github-actions/gem_ci.yml) never excludes any group -- testing whether + # excluding a group is what breaks `bundle exec`'s re-verification against the + # PuppetCore-pinned puppet gem on subsequent Puppet 9 steps. bundle install --jobs 4 --retry 3 - name: Rubocop From a3fcf976830a755e3ed87e0e3ceeecada4d8ba20 Mon Sep 17 00:00:00 2001 From: Lukas Audzevicius Date: Thu, 17 Sep 2026 09:00:51 +0100 Subject: [PATCH 13/17] Revert "DIAGNOSTIC: drop 'bundle config set without development' on linux_tests, test if group exclusion is the trigger" This reverts commit e96ccf56bc65cf838dd8b9b7a9944bc2ddb42c07. --- .github/workflows/ci.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2823bc54..df274aff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,10 +36,7 @@ jobs: ruby-version: ${{ matrix.cfg.ruby }} - name: Update rubygems and install gems run: | - # DIAGNOSTIC: dropped `bundle config set without development`. ruby-pwsh's working CI - # (via cat-github-actions/gem_ci.yml) never excludes any group -- testing whether - # excluding a group is what breaks `bundle exec`'s re-verification against the - # PuppetCore-pinned puppet gem on subsequent Puppet 9 steps. + bundle config set without development bundle install --jobs 4 --retry 3 - name: Rubocop From 9a9ce676c14fdfc99a339ff11ae0a7a50676f234 Mon Sep 17 00:00:00 2001 From: Lukas Audzevicius Date: Thu, 17 Sep 2026 09:01:02 +0100 Subject: [PATCH 14/17] DIAGNOSTIC: dump full Gemfile.lock to check for a puppet-resource_api self-dependency conflict --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index df274aff..0356ee28 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,6 +39,9 @@ jobs: bundle config set without development bundle install --jobs 4 --retry 3 + - name: DIAGNOSTIC full lockfile + run: cat Gemfile.lock + - name: Rubocop run: | bundle exec rake rubocop From f3ca1918eb38cdef921ed1942a392bed33c96c6a Mon Sep 17 00:00:00 2001 From: Lukas Audzevicius Date: Thu, 17 Sep 2026 09:05:51 +0100 Subject: [PATCH 15/17] Revert "DIAGNOSTIC: dump full Gemfile.lock to check for a puppet-resource_api self-dependency conflict" This reverts commit 9a9ce676c14fdfc99a339ff11ae0a7a50676f234. --- .github/workflows/ci.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0356ee28..df274aff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,9 +39,6 @@ jobs: bundle config set without development bundle install --jobs 4 --retry 3 - - name: DIAGNOSTIC full lockfile - run: cat Gemfile.lock - - name: Rubocop run: | bundle exec rake rubocop From 0226c9efd8d4d74057eac7510c62623568a7dd3d Mon Sep 17 00:00:00 2001 From: Lukas Audzevicius Date: Thu, 17 Sep 2026 09:43:19 +0100 Subject: [PATCH 16/17] DEFINITIVE TEST (temporary, will revert): bump local VERSION to 1.9.99 to satisfy puppet 9.0.0's declared puppet-resource_api (~> 1.9) requirement -- checking if this is really what's causing the bundle exec failure --- lib/puppet/resource_api/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/resource_api/version.rb b/lib/puppet/resource_api/version.rb index 8bb91079..bd47874e 100644 --- a/lib/puppet/resource_api/version.rb +++ b/lib/puppet/resource_api/version.rb @@ -2,6 +2,6 @@ module Puppet module ResourceApi - VERSION = '2.0.1' + VERSION = '1.9.99' end end From f5f50e4fa59c8b44d5d562fafb260a6114f9b666 Mon Sep 17 00:00:00 2001 From: Lukas Audzevicius Date: Thu, 17 Sep 2026 10:03:21 +0100 Subject: [PATCH 17/17] Revert "DEFINITIVE TEST (temporary, will revert): bump local VERSION to 1.9.99 to satisfy puppet 9.0.0's declared puppet-resource_api (~> 1.9) requirement -- checking if this is really what's causing the bundle exec failure" This reverts commit 0226c9efd8d4d74057eac7510c62623568a7dd3d. --- lib/puppet/resource_api/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/resource_api/version.rb b/lib/puppet/resource_api/version.rb index bd47874e..8bb91079 100644 --- a/lib/puppet/resource_api/version.rb +++ b/lib/puppet/resource_api/version.rb @@ -2,6 +2,6 @@ module Puppet module ResourceApi - VERSION = '1.9.99' + VERSION = '2.0.1' end end