From 935645382b8de766486bcc30eaa6e413e996ba98 Mon Sep 17 00:00:00 2001 From: Steven Pritchard Date: Sat, 24 Oct 2026 20:45:22 +0000 Subject: [PATCH] Reference ruby/setup-ruby by a release tag `ruby/setup-ruby@v1` follows a branch in that repository, not a tag. Renovate can't resolve a branch to a commit, so it can neither pin `@v1` to a digest nor hold it back with the shared preset's minimumReleaseAge. Reference the v1.334.0 release tag (the first that supports Ruby 4.0.9) in every workflow template and in puppetsync's own CI, and drop the stale digest comment on the pr_tests templates. merge_gha_workflows preserves each repo's `uses:` values, so a template change alone wouldn't reach existing repos. Add a `refresh_identities` option (task, plan and session config) that makes the template's value win for the named identities, including inside `preserve_blocks`, while every other Renovate-managed value is preserved as usual. The 20261024-setup-ruby-release-tag session uses it for the pupmod pr_tests.yml and tag_deploy.yml workflows. Extend github>simp/renovate-config in puppetsync's renovate.json, as the downstream repos do, so the templates get the same release-age wait and action pinning as the workflows synced from them. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci.yml | 2 +- .../20261024-setup-ruby-release-tag.yaml | 64 ++++++++++ dist/puppetsync/plans/init.pp | 10 +- .../puppetsync/tasks/merge_gha_workflows.json | 4 + dist/puppetsync/tasks/merge_gha_workflows.rb | 32 ++++- .../_github/workflows/tag_deploy_rubygem.yml | 4 +- .../tag_deploy_rubygem__github-only.yml | 2 +- .../tag_deploy_rubygem__github-rpms.yml | 2 +- .../_github/workflows/pr_tests.simp-simp.yml | 16 +-- .../pupmod/_github/workflows/pr_tests.yml | 16 +-- .../pupmod/_github/workflows/tag_deploy.yml | 4 +- renovate.json | 1 + spec/tasks/merge_gha_workflows_spec.rb | 109 +++++++++++++++++- 13 files changed, 234 insertions(+), 32 deletions(-) create mode 100644 data/sync/configs/20261024-setup-ruby-release-tag.yaml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9e92ee6..7bdd0c0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-26.04 steps: - uses: actions/checkout@v7 - - uses: ruby/setup-ruby@v1 + - uses: ruby/setup-ruby@v1.334.0 with: # Match the Ruby bundled with openbolt, which runs the tasks in production ruby-version: '3.2.11' diff --git a/data/sync/configs/20261024-setup-ruby-release-tag.yaml b/data/sync/configs/20261024-setup-ruby-release-tag.yaml new file mode 100644 index 0000000..3608393 --- /dev/null +++ b/data/sync/configs/20261024-setup-ruby-release-tag.yaml @@ -0,0 +1,64 @@ +--- +# Reference ruby/setup-ruby by a release tag instead of `@v1`. +# +# In ruby/setup-ruby, `v1` is a branch, not a tag. Renovate can't resolve +# a branch to a commit, so `@v1` can be neither pinned to a digest nor held +# back by the shared preset's minimumReleaseAge. A release tag gives +# Renovate a version to pin, age and update; the shared ruby.json preset +# groups those updates with the Ruby patch updates. +# +# merge_github_workflows normally preserves each repo's `uses:` values, so +# `refresh_identities` makes the template's ruby/setup-ruby ref win for +# this session only. Every other Renovate-managed value is preserved as +# usual. +# +# Run this after simp/renovate-config#9 merges. Without it, every +# setup-ruby release opens its own immediate PR in every repo, instead of +# joining the grouped Ruby PR after a 2-day wait. +puppetsync::plan_config: + puppetsync: + permitted_project_types: + - pupmod + - pupmod_skeleton + plans: + sync: + github_api_delay_seconds: 1 + merge_github_workflows: + files: + - pr_tests.yml + - tag_deploy.yml + preserve_blocks: + - jobs.acceptance + refresh_identities: + - ruby/setup-ruby + stages: + - install_gems + - checkout_git_feature_branch_in_each_repo + - merge_github_workflows + - git_commit_changes + - ensure_github_fork + - ensure_git_remote + - git_push_to_remote + - ensure_github_pr + + approve_github_prs: + stages: + - approve_github_pr_for_each_repo + + merge_github_prs: + stages: + - merge_github_pr_for_each_repo + + git: + feature_branch: puppetsync/20261024-setup-ruby-release-tag + commit_message: | + [puppetsync] Reference ruby/setup-ruby by a release tag + + `ruby/setup-ruby@v1` follows a branch, which Renovate can't pin or + hold back with a minimum release age. Reference the v1.334.0 release + tag instead, so Renovate pins it and proposes setup-ruby updates + together with the Ruby updates that need them. + + github: + pr_user: silug # This should be the account that *submitted* the PRs + approval_message: ':+1: :ghost:' diff --git a/dist/puppetsync/plans/init.pp b/dist/puppetsync/plans/init.pp index 4b90ce4..c703a8f 100644 --- a/dist/puppetsync/plans/init.pp +++ b/dist/puppetsync/plans/init.pp @@ -344,10 +344,14 @@ default => Hash({ 'path' => "${wf_dir}/${f}", 'template' => file::read($template_path) }), } }.filter |$wf| { $wf =~ NotUndef } + # Optional: identities (e.g. 'ruby/setup-ruby') whose values come from + # the template this session, overriding the repo's preserved value. + # puppetsync.plans.sync.merge_github_workflows.refresh_identities Hash({ - 'workflows' => $workflows, - 'preserve_keys' => $opts.dig('merge_github_workflows', 'preserve_keys'), - 'preserve_blocks' => $opts.dig('merge_github_workflows', 'preserve_blocks'), + 'workflows' => $workflows, + 'preserve_keys' => $opts.dig('merge_github_workflows', 'preserve_keys'), + 'preserve_blocks' => $opts.dig('merge_github_workflows', 'preserve_blocks'), + 'refresh_identities' => $opts.dig('merge_github_workflows', 'refresh_identities'), }) } } diff --git a/dist/puppetsync/tasks/merge_gha_workflows.json b/dist/puppetsync/tasks/merge_gha_workflows.json index 15b558a..9c97da3 100644 --- a/dist/puppetsync/tasks/merge_gha_workflows.json +++ b/dist/puppetsync/tasks/merge_gha_workflows.json @@ -10,6 +10,10 @@ "description": "YAML mapping keys whose scalar values are Renovate-managed and must be preserved from the existing file (default: uses, image, container, ruby-version, runs-on)", "type": "Optional[Array[String[1]]]" }, + "refresh_identities": { + "description": "Identities whose values come from the template even when the repo has one (e.g. ['ruby/setup-ruby'] to move every ruby/setup-ruby ref to the template's): for a session that deliberately changes a preserved value without resetting the repo's other Renovate-managed values", + "type": "Optional[Array[String[1]]]" + }, "preserve_blocks": { "description": "Dot-paths of mapping blocks the EXISTING file is authoritative for (e.g. ['jobs.acceptance']): its block replaces the template's, is appended when the template lacks it, and the template's block is removed when the existing file has none", "type": "Optional[Array[String[1]]]" diff --git a/dist/puppetsync/tasks/merge_gha_workflows.rb b/dist/puppetsync/tasks/merge_gha_workflows.rb index 3d82bf2..d991a4b 100644 --- a/dist/puppetsync/tasks/merge_gha_workflows.rb +++ b/dist/puppetsync/tasks/merge_gha_workflows.rb @@ -32,6 +32,14 @@ # other job's `needs:`, since GitHub rejects a workflow whose `needs:` # names a job that doesn't exist (the template's aggregate check job # needs every job, acceptance included). +# +# `refresh_identities` names identities (e.g. 'ruby/setup-ruby') whose values +# come from the template even when the repo already has one, including inside +# `preserve_blocks`: the way a session deliberately changes a preserved value, +# such as moving an action from a branch ref to a release tag, without +# touching the repo's other Renovate-managed values. An occurrence the +# template has no counterpart for (e.g. in a repo-owned block) takes the +# template's first value for that identity. require 'fileutils' require 'json' @@ -184,7 +192,19 @@ def prune_needs(template, job_names) end end -def merge_workflow(template, existing_text, keys, blocks) +# The template's values for a refreshed identity at this [path, key, +# identity], or its first value for the identity anywhere when this context +# only exists in the repo (a preserved block the template lacks) +def template_refresh(template_values, id) + list = template_values.fetch(id, []) + return list unless list.empty? + + template_values.select { |(_path, _key, ident), _values| ident == id[2] }.values.flatten.first(1) +end + +def merge_workflow(template, existing_text, keys, blocks, refresh = []) + # Read before preserve_blocks splices repo content into the template + template_values = refresh.empty? ? {} : existing_values(template, keys) template, preserved_blocks, removed_blocks = preserve_existing_blocks(template, existing_text, blocks) removed_jobs = removed_blocks.filter_map { |path| path.delete_prefix('jobs.') if path.match?(/\Ajobs\.[^.]+\z/) } template = prune_needs(template, removed_jobs) @@ -197,12 +217,13 @@ def merge_workflow(template, existing_text, keys, blocks) # differences (distinct versions per job, trailing comments) survive # verbatim. Template entries with no counterpart in the existing file # (a genuinely new or restored step) keep the template's value. + # Refreshed identities take the template's values instead. counters = Hash.new(0) preserved_scalars(Psych.parse(template), keys).each do |path, key, node| next unless node.start_line == node.end_line id = [path, key, identity(node.value)] - list = existing[id] + list = refresh.include?(id[2]) ? template_refresh(template_values, id) : existing[id] value = list[counters[id]] || list.last counters[id] += 1 next if value.nil? @@ -217,7 +238,7 @@ def merge_workflow(template, existing_text, keys, blocks) [lines.join("\n"), updated, preserved_blocks] end -def merge_gha_workflows(workflows, keys, blocks) +def merge_gha_workflows(workflows, keys, blocks, refresh = []) results = {} workflows.each do |wf| path = wf.fetch('path') @@ -232,7 +253,7 @@ def merge_gha_workflows(workflows, keys, blocks) end existing_text = File.read(path) - merged, updated, preserved_blocks = merge_workflow(template, existing_text, keys, blocks) + merged, updated, preserved_blocks = merge_workflow(template, existing_text, keys, blocks, refresh) if merged == existing_text results[path] = { 'changed' => false } @@ -252,5 +273,6 @@ def merge_gha_workflows(workflows, keys, blocks) raise('No workflows given') unless workflows.is_a?(Array) keys = params.fetch('preserve_keys', nil) || DEFAULT_PRESERVE_KEYS blocks = params.fetch('preserve_blocks', nil) || [] +refresh = params.fetch('refresh_identities', nil) || [] -puts JSON.generate(merge_gha_workflows(workflows, keys, blocks)) +puts JSON.generate(merge_gha_workflows(workflows, keys, blocks, refresh)) diff --git a/modules/profile/files/_github/workflows/tag_deploy_rubygem.yml b/modules/profile/files/_github/workflows/tag_deploy_rubygem.yml index 517ad8b..91f9fe5 100644 --- a/modules/profile/files/_github/workflows/tag_deploy_rubygem.yml +++ b/modules/profile/files/_github/workflows/tag_deploy_rubygem.yml @@ -90,7 +90,7 @@ jobs: echo "build_command=$GEM_BUILD_COMMAND" | tee -a "$GITHUB_OUTPUT" echo "pkg_dir=$GEM_PKG_DIR" | tee -a "$GITHUB_OUTPUT" echo "release_command=$GEM_RELEASE_COMMAND" | tee -a "$GITHUB_OUTPUT" - - uses: ruby/setup-ruby@v1 + - uses: ruby/setup-ruby@v1.334.0 with: ruby-version: 2.7 bundler-cache: true @@ -182,7 +182,7 @@ jobs: with: ref: ${{ github.ref }} clean: true - - uses: ruby/setup-ruby@v1 + - uses: ruby/setup-ruby@v1.334.0 with: ruby-version: 2.7 bundler-cache: true diff --git a/modules/profile/files/_github/workflows/tag_deploy_rubygem__github-only.yml b/modules/profile/files/_github/workflows/tag_deploy_rubygem__github-only.yml index cb30155..4818154 100644 --- a/modules/profile/files/_github/workflows/tag_deploy_rubygem__github-only.yml +++ b/modules/profile/files/_github/workflows/tag_deploy_rubygem__github-only.yml @@ -77,7 +77,7 @@ jobs: echo "build_command=$GEM_BUILD_COMMAND" | tee -a "$GITHUB_OUTPUT" echo "pkg_dir=$GEM_PKG_DIR" | tee -a "$GITHUB_OUTPUT" echo "release_command=$GEM_RELEASE_COMMAND" | tee -a "$GITHUB_OUTPUT" - - uses: ruby/setup-ruby@v1 + - uses: ruby/setup-ruby@v1.334.0 with: ruby-version: 2.7 bundler-cache: true diff --git a/modules/profile/files/_github/workflows/tag_deploy_rubygem__github-rpms.yml b/modules/profile/files/_github/workflows/tag_deploy_rubygem__github-rpms.yml index a505820..b1afef2 100644 --- a/modules/profile/files/_github/workflows/tag_deploy_rubygem__github-rpms.yml +++ b/modules/profile/files/_github/workflows/tag_deploy_rubygem__github-rpms.yml @@ -91,7 +91,7 @@ jobs: echo "build_command=$GEM_BUILD_COMMAND" | tee -a "$GITHUB_OUTPUT" echo "pkg_dir=$GEM_PKG_DIR" | tee -a "$GITHUB_OUTPUT" echo "release_command=$GEM_RELEASE_COMMAND" | tee -a "$GITHUB_OUTPUT" - - uses: ruby/setup-ruby@v1 + - uses: ruby/setup-ruby@v1.334.0 with: ruby-version: 2.7 bundler-cache: true diff --git a/modules/profile/files/pupmod/_github/workflows/pr_tests.simp-simp.yml b/modules/profile/files/pupmod/_github/workflows/pr_tests.simp-simp.yml index 3102366..0204306 100644 --- a/modules/profile/files/pupmod/_github/workflows/pr_tests.simp-simp.yml +++ b/modules/profile/files/pupmod/_github/workflows/pr_tests.simp-simp.yml @@ -23,7 +23,7 @@ jobs: steps: - uses: actions/checkout@v7 - name: 'Install Ruby' - uses: ruby/setup-ruby@v1 # ruby/setup-ruby@ec106b438a1ff6ff109590de34ddc62c540232e0 + uses: ruby/setup-ruby@v1.334.0 with: ruby-version: '4.0.9' bundler-cache: true @@ -35,7 +35,7 @@ jobs: steps: - uses: actions/checkout@v7 - name: 'Install Ruby' - uses: ruby/setup-ruby@v1 + uses: ruby/setup-ruby@v1.334.0 with: ruby-version: '4.0.9' bundler-cache: true @@ -48,7 +48,7 @@ jobs: steps: - uses: actions/checkout@v7 - name: 'Install Ruby' - uses: ruby/setup-ruby@v1 + uses: ruby/setup-ruby@v1.334.0 with: ruby-version: '4.0.9' bundler-cache: true @@ -62,7 +62,7 @@ jobs: steps: - uses: actions/checkout@v7 - name: 'Install Ruby' - uses: ruby/setup-ruby@v1 + uses: ruby/setup-ruby@v1.334.0 with: ruby-version: '4.0.9' bundler-cache: true @@ -75,7 +75,7 @@ jobs: steps: - uses: actions/checkout@v7 - name: 'Install Ruby' - uses: ruby/setup-ruby@v1 + uses: ruby/setup-ruby@v1.334.0 with: ruby-version: '4.0.9' bundler-cache: true @@ -88,7 +88,7 @@ jobs: steps: - uses: actions/checkout@v7 - name: 'Install Ruby' - uses: ruby/setup-ruby@v1 + uses: ruby/setup-ruby@v1.334.0 with: ruby-version: '4.0.9' bundler-cache: true @@ -141,7 +141,7 @@ jobs: steps: - uses: actions/checkout@v7 - name: 'Install Ruby ${{matrix.puppet.ruby_version}}' - uses: ruby/setup-ruby@v1 + uses: ruby/setup-ruby@v1.334.0 with: ruby-version: ${{matrix.puppet.ruby_version}} bundler-cache: true @@ -193,7 +193,7 @@ jobs: - name: checkout repo uses: actions/checkout@v7 - name: 'Install Ruby' - uses: ruby/setup-ruby@v1 + uses: ruby/setup-ruby@v1.334.0 with: ruby-version: '4.0.9' - name: bundle install diff --git a/modules/profile/files/pupmod/_github/workflows/pr_tests.yml b/modules/profile/files/pupmod/_github/workflows/pr_tests.yml index 678173b..a005f5f 100644 --- a/modules/profile/files/pupmod/_github/workflows/pr_tests.yml +++ b/modules/profile/files/pupmod/_github/workflows/pr_tests.yml @@ -23,7 +23,7 @@ jobs: steps: - uses: actions/checkout@v7 - name: 'Install Ruby' - uses: ruby/setup-ruby@v1 # ruby/setup-ruby@ec106b438a1ff6ff109590de34ddc62c540232e0 + uses: ruby/setup-ruby@v1.334.0 with: ruby-version: '4.0.9' bundler-cache: true @@ -35,7 +35,7 @@ jobs: steps: - uses: actions/checkout@v7 - name: 'Install Ruby' - uses: ruby/setup-ruby@v1 + uses: ruby/setup-ruby@v1.334.0 with: ruby-version: '4.0.9' bundler-cache: true @@ -48,7 +48,7 @@ jobs: steps: - uses: actions/checkout@v7 - name: 'Install Ruby' - uses: ruby/setup-ruby@v1 + uses: ruby/setup-ruby@v1.334.0 with: ruby-version: '4.0.9' bundler-cache: true @@ -62,7 +62,7 @@ jobs: steps: - uses: actions/checkout@v7 - name: 'Install Ruby' - uses: ruby/setup-ruby@v1 + uses: ruby/setup-ruby@v1.334.0 with: ruby-version: '4.0.9' bundler-cache: true @@ -75,7 +75,7 @@ jobs: steps: - uses: actions/checkout@v7 - name: 'Install Ruby' - uses: ruby/setup-ruby@v1 + uses: ruby/setup-ruby@v1.334.0 with: ruby-version: '4.0.9' bundler-cache: true @@ -88,7 +88,7 @@ jobs: steps: - uses: actions/checkout@v7 - name: 'Install Ruby' - uses: ruby/setup-ruby@v1 + uses: ruby/setup-ruby@v1.334.0 with: ruby-version: '4.0.9' bundler-cache: true @@ -126,7 +126,7 @@ jobs: steps: - uses: actions/checkout@v7 - name: 'Install Ruby ${{matrix.puppet.ruby_version}}' - uses: ruby/setup-ruby@v1 + uses: ruby/setup-ruby@v1.334.0 with: ruby-version: ${{matrix.puppet.ruby_version}} bundler-cache: true @@ -173,7 +173,7 @@ jobs: - name: checkout repo uses: actions/checkout@v7 - name: 'Install Ruby' - uses: ruby/setup-ruby@v1 + uses: ruby/setup-ruby@v1.334.0 with: ruby-version: '4.0.9' - name: bundle install diff --git a/modules/profile/files/pupmod/_github/workflows/tag_deploy.yml b/modules/profile/files/pupmod/_github/workflows/tag_deploy.yml index 5d883b7..27d6778 100644 --- a/modules/profile/files/pupmod/_github/workflows/tag_deploy.yml +++ b/modules/profile/files/pupmod/_github/workflows/tag_deploy.yml @@ -53,7 +53,7 @@ jobs: with: ref: ${{ github.ref }} clean: true - - uses: ruby/setup-ruby@v1 + - uses: ruby/setup-ruby@v1.334.0 with: ruby-version: '4.0.9' bundler-cache: true @@ -174,7 +174,7 @@ jobs: with: ref: ${{ github.ref }} clean: true - - uses: ruby/setup-ruby@v1 + - uses: ruby/setup-ruby@v1.334.0 with: ruby-version: '4.0.9' bundler-cache: true diff --git a/renovate.json b/renovate.json index f40b4af..40e99b7 100644 --- a/renovate.json +++ b/renovate.json @@ -2,6 +2,7 @@ "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ "config:recommended", + "github>simp/renovate-config", "github>simp/renovate-config:ruby.json" ], "github-actions": { diff --git a/spec/tasks/merge_gha_workflows_spec.rb b/spec/tasks/merge_gha_workflows_spec.rb index 869f11f..cf1389d 100644 --- a/spec/tasks/merge_gha_workflows_spec.rb +++ b/spec/tasks/merge_gha_workflows_spec.rb @@ -13,9 +13,10 @@ end end - def run_merge(workflows, preserve_blocks: nil) + def run_merge(workflows, preserve_blocks: nil, refresh_identities: nil) params = { 'workflows' => workflows } params['preserve_blocks'] = preserve_blocks if preserve_blocks + params['refresh_identities'] = refresh_identities if refresh_identities run_task('merge_gha_workflows.rb', params) end @@ -251,6 +252,112 @@ def run_merge(workflows, preserve_blocks: nil) expect(File.read(@wf)).to eq(first_pass) end + context 'with refresh_identities' do + let(:template) do + <<~YAML + jobs: + build: + steps: + - uses: actions/checkout@v7 + - uses: ruby/setup-ruby@v1.334.0 + test: + steps: + - uses: ruby/setup-ruby@v1.334.0 + with: + ruby-version: '4.0.9' + YAML + end + + let(:existing) do + <<~YAML + jobs: + build: + steps: + - uses: actions/checkout@3d3c42e0f0d4c8a1e9f6c2b5a7d8e9f0a1b2c3d4 # v7 + - uses: ruby/setup-ruby@v1 # ruby/setup-ruby@ec106b438a1ff6ff109590de34ddc62c540232e0 + test: + steps: + - uses: ruby/setup-ruby@v1 + with: + ruby-version: '4.0.8' + YAML + end + + it "takes the template's value for every occurrence of a refreshed identity" do + File.write(@wf, existing) + + stdout, stderr, status = run_merge([{ 'path' => @wf, 'template' => template }], refresh_identities: ['ruby/setup-ruby']) + + expect(status).to be_success, stderr + expect(JSON.parse(stdout)['files'][@wf]['changed']).to be true + merged = File.read(@wf) + expect(merged.scan('uses: ruby/setup-ruby@v1.334.0').size).to eq(2) + expect(merged).not_to include('ec106b438a') # the repo's trailing comment goes with its value + end + + it "keeps the repo's other Renovate-managed values" do + File.write(@wf, existing) + + _stdout, stderr, status = run_merge([{ 'path' => @wf, 'template' => template }], refresh_identities: ['ruby/setup-ruby']) + + expect(status).to be_success, stderr + merged = File.read(@wf) + expect(merged).to include('actions/checkout@3d3c42e0f0d4c8a1e9f6c2b5a7d8e9f0a1b2c3d4 # v7') + expect(merged).to include("ruby-version: '4.0.8'") + end + + it "keeps the repo's value for that identity without the option" do + File.write(@wf, existing) + + _stdout, stderr, status = run_merge([{ 'path' => @wf, 'template' => template }]) + + expect(status).to be_success, stderr + expect(File.read(@wf)).to eq(existing) + end + + it 'refreshes the identity inside preserved blocks too' do + template_with_acceptance = template + <<~YAML.gsub(/^/, ' ') + acceptance: + steps: + - uses: ruby/setup-ruby@v1.334.0 + YAML + repo_with_blocks = existing + <<~YAML.gsub(/^/, ' ') + acceptance: + steps: + - uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.2.11' + repo_only: + steps: + - uses: ruby/setup-ruby@v1 + YAML + File.write(@wf, repo_with_blocks) + + _stdout, stderr, status = run_merge( + [{ 'path' => @wf, 'template' => template_with_acceptance }], + preserve_blocks: ['jobs.acceptance', 'jobs.repo_only'], + refresh_identities: ['ruby/setup-ruby'], + ) + + expect(status).to be_success, stderr + merged = File.read(@wf) + expect(merged).not_to match(%r{ruby/setup-ruby@v1$}) + expect(merged.scan('uses: ruby/setup-ruby@v1.334.0').size).to eq(4) + expect(merged).to include("ruby-version: '3.2.11'") # the rest of the preserved block is untouched + end + + it 'moves the fleet off the setup-ruby branch ref against the real template' do + fleet = real_template.gsub(/ruby\/setup-ruby@v1\.334\.0/, 'ruby/setup-ruby@v1') + raise 'munge failed' if fleet == real_template + File.write(@wf, fleet) + + _stdout, stderr, status = run_merge([{ 'path' => @wf, 'template' => real_template }], refresh_identities: ['ruby/setup-ruby']) + + expect(status).to be_success, stderr + expect(File.read(@wf)).to eq(real_template) + end + end + context 'with preserve_blocks' do let(:template) do <<~YAML