From 48b975c7c150bd6c9efad5f1918bcb42b4b00f60 Mon Sep 17 00:00:00 2001 From: Gavin Didrichsen Date: Wed, 19 Aug 2026 17:06:29 +0100 Subject: [PATCH 1/8] (CAT-2763) Flip runtime dep from openvox to puppet Per fork stance R7 in the CAT-2760 project knowledge base: puppetlabs-syntax is explicitly Puppet-core, not core-agnostic. Flip the gemspec runtime dep from `openvox` (Vox's upstream choice) to `puppet` and widen the range to `>= 8, < 10` in preparation for the Puppet 9 / Ruby 4.0 rollout. This is the piece that was missed by PR #1 (CODEOWNERS) and PR #2 (workflow switch to puppetlabs/cat-github-actions), both of which touched .github/ and CODEOWNERS only. The nightly workflow surfaced the miss on Windows. --- puppet-syntax.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/puppet-syntax.gemspec b/puppet-syntax.gemspec index 5101001..7a14aae 100644 --- a/puppet-syntax.gemspec +++ b/puppet-syntax.gemspec @@ -20,7 +20,7 @@ Gem::Specification.new do |spec| spec.required_ruby_version = '>= 3.2' - spec.add_dependency 'openvox', '>= 8', '< 9' + spec.add_dependency 'puppet', '>= 8', '< 10' spec.add_dependency 'rake', '~> 13.1' spec.add_development_dependency 'voxpupuli-rubocop', '~> 5.2.0' From 613a5b4919ee6826f0fb3a4a5d74ee75d044d2f1 Mon Sep 17 00:00:00 2001 From: Gavin Didrichsen Date: Wed, 19 Aug 2026 17:06:38 +0100 Subject: [PATCH 2/8] (CAT-2763) Declare Windows platform runtime deps (ffi, win32ole) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes the nightly Windows spec failures: # Ruby 3.2 / Puppet 8 Failure/Error: require 'puppet' LoadError: cannot load such file -- ffi # Ruby 4.0 / Puppet 9 Failure/Error: require 'puppet' LoadError: cannot load such file -- win32ole Both upstream puppet.gemspec and openvox.gemspec guard the ffi/win32ole runtime deps with build-host platform checks (`Gem.win_platform?` / `platform == 'x64-mingw32'`). Those evaluate on the packaging host at gem-build time, not the install host at install time, so the Linux-published rubygems.org artefacts of both puppet and openvox ship without the deps. Ruby 3.4+ removed win32ole from default gems, which is what made the missing declaration fatal for the first time on Ruby 4.0. Declare both deps in this Gemfile under a `platforms :mingw, :x64_mingw, :mswin` block — evaluated on the install host at bundle time, so bundler only pulls them on Windows. Also flips the CI matrix override (`PUPPET_VERSION`) from openvox to puppet, to match the gemspec flip in the previous commit. --- Gemfile | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 7ae6873..94c8e26 100644 --- a/Gemfile +++ b/Gemfile @@ -21,10 +21,22 @@ gemspec # Override gemspec for CI matrix builds. # But only if the environment variable is set -gem 'openvox', *location_for(ENV['PUPPET_VERSION']) if ENV['PUPPET_VERSION'] +gem 'puppet', *location_for(ENV['PUPPET_VERSION']) if ENV['PUPPET_VERSION'] # Puppet on Ruby 3.3 / 3.4 has some missing dependencies gem 'syslog', '~> 0.3' if RUBY_VERSION >= '3.4' +# Windows platform runtime deps. The published puppet/openvox rubygems.org +# artefacts are built on Linux and guard `ffi` / `win32ole` with build-host +# platform checks, so those deps never make it into the Linux-published +# artefact. Ruby 3.4+ removed win32ole from default gems, making the missing +# declaration fatal at require-time on Windows. Declaring them here in the +# Gemfile is evaluated on the install host at bundle time, so bundler pulls +# them on Windows only. +platforms :mingw, :x64_mingw, :mswin do + gem 'ffi', '>= 1.15.5', '< 1.17.0', '!= 1.16.0', '!= 1.16.1', '!= 1.16.2' + gem 'win32ole', '>= 1.8', '< 2.0' +end + group :test do gem 'rspec' end From dde3a69daff2dc24d824320a7ba0ca1dbc55077d Mon Sep 17 00:00:00 2001 From: Gavin Didrichsen Date: Wed, 19 Aug 2026 17:37:50 +0100 Subject: [PATCH 3/8] (CAT-2763) Route puppet through Puppetcore gem source The public rubygems.org catalogue tops out at puppet 8.10.0; no 9.x gem has been published there yet. Bundler resolving puppet ~> 9.0 against public rubygems falls back to puppet 8.x, whose transitive facter pins Ruby < 4.0 and fails the Ruby 4.0 / Puppet 9 matrix cell at bundle install. Puppet 9 IS published to the private Puppetcore registry at https://rubygems-puppetcore.puppet.com. Route the puppet gem through gemsource_puppetcore (via the {source:} option on location_for) so bundler resolves against Puppetcore when PUPPET_FORGE_TOKEN is set. The reusable puppetlabs/cat-github-actions/gem_ci.yml@main already wires that env var from the puppetlabs org-level PUPPET_FORGE_TOKEN_PUBLIC secret, so no workflow-side change is needed. Fork PRs get no token under GitHub's pull_request platform policy, so gemsource_puppetcore falls through to gemsource_default (public rubygems.org) via the `if ENV['PUPPET_FORGE_TOKEN']` guard - no auth attempted, no exposure. Pattern mirrors puppetlabs/rspec-puppet's Gemfile, whose nightly has been green for 13 consecutive days on the same Ruby 4.0 / Puppet 9 matrix cell. --- Gemfile | 44 +++++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/Gemfile b/Gemfile index 94c8e26..0c9e254 100644 --- a/Gemfile +++ b/Gemfile @@ -1,27 +1,37 @@ # frozen_string_literal: true -source 'https://rubygems.org' - -# 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) - if place_or_version =~ /^(https[:@][^#]*)#(.*)/ - [fake_version, { git: Regexp.last_match(1), branch: Regexp.last_match(2), require: false }].compact - elsif place_or_version =~ %r{^file://(.*)} - ['>= 0', { path: File.expand_path(Regexp.last_match(1)), require: false }] +# For puppetcore, set GEM_SOURCE_PUPPETCORE = 'https://rubygems-puppetcore.puppet.com' +gemsource_default = ENV['GEM_SOURCE'] || 'https://rubygems.org' +gemsource_puppetcore = if ENV['PUPPET_FORGE_TOKEN'] + 'https://rubygems-puppetcore.puppet.com' + else + ENV['GEM_SOURCE_PUPPETCORE'] || gemsource_default + end +source gemsource_default + +gemspec + +def location_for(place_or_version, fake_version = nil, opts = {}) + git_url_regex = /\A(?(https?|git)[:@][^#]*)(#(?.*))?/ + file_url_regex = %r{\Afile://(?.*)} + + if place_or_version && (git_url = place_or_version.match(git_url_regex)) + [fake_version, { git: git_url[:url], branch: git_url[:branch], require: false }].compact + elsif place_or_version && (file_url = place_or_version.match(file_url_regex)) + ['>= 0', { path: File.expand_path(file_url[:path]), require: false }] else - [place_or_version, { require: false }] + [place_or_version, { require: false }.merge(opts)] end end -# Specify your gem's dependencies in puppet-syntax.gemspec -gemspec - # Override gemspec for CI matrix builds. -# But only if the environment variable is set -gem 'puppet', *location_for(ENV['PUPPET_VERSION']) if ENV['PUPPET_VERSION'] +# But only if the environment variable is set. +# Route through gemsource_puppetcore so CI can resolve puppet ~> 9.0 from the +# private Puppetcore registry (public rubygems.org tops out at puppet 8.10.0). +# When PUPPET_FORGE_TOKEN is unset (e.g. fork PRs, local dev without a token), +# gemsource_puppetcore falls through to gemsource_default (public rubygems.org) +# and no auth is attempted against Puppetcore. +gem 'puppet', *location_for(ENV['PUPPET_VERSION'], nil, { source: gemsource_puppetcore }) if ENV['PUPPET_VERSION'] # Puppet on Ruby 3.3 / 3.4 has some missing dependencies gem 'syslog', '~> 0.3' if RUBY_VERSION >= '3.4' From 6664e1b0266009506c5d0c66b9f4339cbddf9432 Mon Sep 17 00:00:00 2001 From: Gavin Didrichsen Date: Wed, 19 Aug 2026 17:38:09 +0100 Subject: [PATCH 4/8] (CAT-2763) Fix CI triggers: main -> master The ci.yml and mend.yml workflows were triggering on branch "main", but the repo's default branch is "master". This meant ci.yml never ran on any push or pull request against master, and mend.yml never ran on PRs. PR #3's checks page confirms this: only Vox's leftover Test workflow fired; the puppetlabs cat-github-actions gem_ci reusable workflow did not. Fix both files to trigger on "master" so subsequent PRs get automatic CI coverage. The nightly.yml workflow is unchanged - it triggers on schedule + workflow_dispatch, which is branch-agnostic and already works correctly. The eventual master -> main default-branch rename is a separate, deliberate outward-facing change and is NOT part of this PR. --- .github/workflows/ci.yml | 4 ++-- .github/workflows/mend.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b0b8fde..3b55e6c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,10 +3,10 @@ name: "ci" on: push: branches: - - "main" + - "master" pull_request: branches: - - "main" + - "master" workflow_dispatch: env: diff --git a/.github/workflows/mend.yml b/.github/workflows/mend.yml index 095e9e5..33a8e28 100644 --- a/.github/workflows/mend.yml +++ b/.github/workflows/mend.yml @@ -3,7 +3,7 @@ name: "mend" on: pull_request: branches: - - "main" + - "master" schedule: - cron: "0 0 * * *" workflow_dispatch: From ceb332186d4ef4984790cf8b026419d3b9a49dc4 Mon Sep 17 00:00:00 2001 From: Gavin Didrichsen Date: Thu, 20 Aug 2026 09:33:55 +0100 Subject: [PATCH 5/8] Remove email Signed-off-by: Gavin Didrichsen --- puppet-syntax.gemspec | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/puppet-syntax.gemspec b/puppet-syntax.gemspec index 7a14aae..40f127c 100644 --- a/puppet-syntax.gemspec +++ b/puppet-syntax.gemspec @@ -7,13 +7,13 @@ require 'puppet-syntax/version' Gem::Specification.new do |spec| spec.name = 'puppet-syntax' spec.version = PuppetSyntax::VERSION - spec.authors = ['Vox Pupuli'] - spec.email = ['voxpupuli@groups.io'] - spec.description = 'Syntax checks for Puppet manifests and templates' + spec.authors = ['DevX team at Puppet by Perforce'] spec.summary = 'Syntax checks for Puppet manifests, templates, and Hiera YAML' - spec.homepage = 'https://github.com/voxpupuli/puppet-syntax' + spec.homepage = 'https://github.com/puppetlabs/puppetlabs-syntax/' spec.license = 'MIT' - + spec.description = <<-EOF + Syntax checks for Puppet manifests and templates. + EOF spec.files = `git ls-files`.split($/) spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.require_paths = ['lib'] From d529af33447b554383ef20b4f338c48f1b119baf Mon Sep 17 00:00:00 2001 From: Gavin Didrichsen Date: Thu, 20 Aug 2026 10:06:10 +0100 Subject: [PATCH 6/8] (CAT-2763) Fix CI: wrong env var name broke Puppetcore routing, fix rubocop offenses Gemfile checked ENV['PUPPET_VERSION'] to trigger the puppet gem/source override, but CI's gem_ci.yml reusable workflow only ever sets PUPPET_GEM_VERSION, so the override silently never fired and bundler fell back to public rubygems.org, which fails to resolve puppet ~> 9.0 for the ruby 4.0 matrix leg. Rename to match, and reorder gemsource_puppetcore so an explicit GEM_SOURCE (e.g. an internal Artifactory mirror for VPN'd dev) takes priority over PUPPET_FORGE_TOKEN-triggered Puppetcore routing. Also fixes the two rubocop offenses blocking the ruby 3.2 legs: a mixed named/numbered regex capture in Gemfile's location_for, and a non-descriptive EOF heredoc delimiter in the gemspec. Co-Authored-By: Claude Sonnet 5 --- Gemfile | 8 +++++--- puppet-syntax.gemspec | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Gemfile b/Gemfile index 0c9e254..10570a5 100644 --- a/Gemfile +++ b/Gemfile @@ -2,7 +2,9 @@ # For puppetcore, set GEM_SOURCE_PUPPETCORE = 'https://rubygems-puppetcore.puppet.com' gemsource_default = ENV['GEM_SOURCE'] || 'https://rubygems.org' -gemsource_puppetcore = if ENV['PUPPET_FORGE_TOKEN'] +gemsource_puppetcore = if ENV['GEM_SOURCE'] + gemsource_default + elsif ENV['PUPPET_FORGE_TOKEN'] 'https://rubygems-puppetcore.puppet.com' else ENV['GEM_SOURCE_PUPPETCORE'] || gemsource_default @@ -12,7 +14,7 @@ source gemsource_default gemspec def location_for(place_or_version, fake_version = nil, opts = {}) - git_url_regex = /\A(?(https?|git)[:@][^#]*)(#(?.*))?/ + git_url_regex = /\A(?(?:https?|git)[:@][^#]*)(?:#(?.*))?/ file_url_regex = %r{\Afile://(?.*)} if place_or_version && (git_url = place_or_version.match(git_url_regex)) @@ -31,7 +33,7 @@ end # When PUPPET_FORGE_TOKEN is unset (e.g. fork PRs, local dev without a token), # gemsource_puppetcore falls through to gemsource_default (public rubygems.org) # and no auth is attempted against Puppetcore. -gem 'puppet', *location_for(ENV['PUPPET_VERSION'], nil, { source: gemsource_puppetcore }) if ENV['PUPPET_VERSION'] +gem 'puppet', *location_for(ENV['PUPPET_GEM_VERSION'], nil, { source: gemsource_puppetcore }) if ENV['PUPPET_GEM_VERSION'] # Puppet on Ruby 3.3 / 3.4 has some missing dependencies gem 'syslog', '~> 0.3' if RUBY_VERSION >= '3.4' diff --git a/puppet-syntax.gemspec b/puppet-syntax.gemspec index 40f127c..638da02 100644 --- a/puppet-syntax.gemspec +++ b/puppet-syntax.gemspec @@ -11,9 +11,9 @@ Gem::Specification.new do |spec| spec.summary = 'Syntax checks for Puppet manifests, templates, and Hiera YAML' spec.homepage = 'https://github.com/puppetlabs/puppetlabs-syntax/' spec.license = 'MIT' - spec.description = <<-EOF + spec.description = <<-DESC Syntax checks for Puppet manifests and templates. - EOF + DESC spec.files = `git ls-files`.split($/) spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.require_paths = ['lib'] From 35f468452097fa2670e0180b59da0a831828cc24 Mon Sep 17 00:00:00 2001 From: Gavin Didrichsen Date: Thu, 20 Aug 2026 10:12:32 +0100 Subject: [PATCH 7/8] (CAT-2763) Fix CI: point rake_task at the spec task that actually exists ci.yml passed rake_task: "spec:coverage" to the shared gem_ci.yml workflow, but the Rakefile only ever defined a plain "spec" task - no spec:coverage namespace, no simplecov gem, no COVERAGE env wiring in spec_helper.rb. This predates CAT-2763 (introduced in e9a10ce) and was only now exposed because CI never got past the earlier bundle install/rubocop failures to reach this step. gem_ci.yml's Codecov-upload step is gated on ruby_version == '3.1', which this repo's matrix (3.2/4.0) never satisfies anyway, so building out real coverage collection would exercise a step that can never run. Point rake_task at the existing "spec" task instead, matching what nightly.yml already does by default. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3b55e6c..ad71b08 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,7 +32,7 @@ jobs: uses: "puppetlabs/cat-github-actions/.github/workflows/gem_ci.yml@main" secrets: "inherit" with: - rake_task: "spec:coverage" + rake_task: "spec" ruby_version: ${{ matrix.ruby_version }} puppet_gem_version: ${{ matrix.puppet_gem_version }} runs_on: ${{ matrix.runs_on }} From 8f2005a5579af4132b7047544883b7108688f089 Mon Sep 17 00:00:00 2001 From: Gavin Didrichsen Date: Thu, 20 Aug 2026 10:31:22 +0100 Subject: [PATCH 8/8] Update gemspec email/authors. Signed-off-by: Gavin Didrichsen --- puppet-syntax.gemspec | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/puppet-syntax.gemspec b/puppet-syntax.gemspec index 638da02..7533a6e 100644 --- a/puppet-syntax.gemspec +++ b/puppet-syntax.gemspec @@ -7,11 +7,12 @@ require 'puppet-syntax/version' Gem::Specification.new do |spec| spec.name = 'puppet-syntax' spec.version = PuppetSyntax::VERSION - spec.authors = ['DevX team at Puppet by Perforce'] + spec.email = ['modules-team@puppet.com'] + spec.authors = ['Puppet, Inc.'] spec.summary = 'Syntax checks for Puppet manifests, templates, and Hiera YAML' spec.homepage = 'https://github.com/puppetlabs/puppetlabs-syntax/' spec.license = 'MIT' - spec.description = <<-DESC + spec.description = <<-DESC Syntax checks for Puppet manifests and templates. DESC spec.files = `git ls-files`.split($/)