diff --git a/lib/ghb/workflow/workflow.rb b/lib/ghb/workflow/workflow.rb index 5801133..6d69dd7 100644 --- a/lib/ghb/workflow/workflow.rb +++ b/lib/ghb/workflow/workflow.rb @@ -8,6 +8,9 @@ module GHB class Workflow + GITHUB_ENV_VAR_REGEX = /\$\{GITHUB_([A-Z_]+)\}/ + private_constant :GITHUB_ENV_VAR_REGEX + def initialize(name) @name = name @run_name = nil @@ -114,13 +117,8 @@ def read(file) def write(file, header: '') FileUtils.mkdir_p(File.dirname(file)) - content = header + to_h.deep_stringify_keys.to_yaml({ line_width: -1 }) - - # Convert old-style ${GITHUB_*} patterns to new-style ${{github.*}} - content.gsub!(/\$\{GITHUB_([A-Z_]+)\}/) do |_match| - var_name = ::Regexp.last_match(1).downcase - "${{github.#{var_name}}}" - end + data = rewrite_github_refs(to_h.deep_stringify_keys) + content = header + data.to_yaml({ line_width: -1 }) # Convert secrets.GITHUB_TOKEN to secrets.GH_PAT for higher rate limits content.gsub!('${{secrets.GITHUB_TOKEN}}', '${{secrets.GH_PAT}}') unless file.include?('auto-merge') @@ -140,5 +138,25 @@ def to_h hash[:jobs] = @jobs.transform_values(&:to_h) hash end + + private + + # Rewrite ${GITHUB_*} -> ${{github.*}} in YAML values, but skip shell `run:` + # bodies - there ${GITHUB_*} is the runner-exported env-var form and + # ${{github.*}} is opaque to shellcheck (SC2193). + def rewrite_github_refs(node) + case node + when Hash + node.each_with_object({}) do |(key, value), acc| + acc[key] = key.to_s == 'run' ? value : rewrite_github_refs(value) + end + when Array + node.map { |item| rewrite_github_refs(item) } + when String + node.gsub(GITHUB_ENV_VAR_REGEX) { "${{github.#{::Regexp.last_match(1).downcase}}}" } + else + node + end + end end end diff --git a/spec/ghb/workflow/workflow_spec.rb b/spec/ghb/workflow/workflow_spec.rb index 61aa805..d9b786f 100644 --- a/spec/ghb/workflow/workflow_spec.rb +++ b/spec/ghb/workflow/workflow_spec.rb @@ -295,6 +295,21 @@ end end + it 'preserves ${GITHUB_*} inside shell run: blocks' do # rubocop:disable RSpec/ExampleLength,RSpec/MultipleExpectations + workflow.do_job(:build) do + do_runs_on('ubuntu-latest') + do_step('echo sha') do + do_run('echo "$GITHUB_SHA ${GITHUB_REF}"') + end + end + workflow.write(temp_file) + + expect(File).to(have_received(:write)) do |_, content| + expect(content).to(include('${GITHUB_REF}')) + expect(content).not_to(include('${{github.ref}}')) + end + end + it 'converts secrets.GITHUB_TOKEN to secrets.GH_PAT' do # rubocop:disable RSpec/ExampleLength,RSpec/MultipleExpectations workflow.do_env({ TOKEN: '${{secrets.GITHUB_TOKEN}}' }) workflow.write(temp_file)