From 4f65d05c6be83b8426ad16869e3034f6392d4286 Mon Sep 17 00:00:00 2001 From: Neil Carvalho Date: Mon, 25 May 2026 12:40:45 -0300 Subject: [PATCH] Remove debugging modes They were useful during development, but are useless now. --- README.md | 22 ----- exe/importmap-update | 186 +++++++++++++------------------------------ lib/config.rb | 22 ----- test/config_test.rb | 21 ----- 4 files changed, 55 insertions(+), 196 deletions(-) diff --git a/README.md b/README.md index d0dcba7..15d2888 100644 --- a/README.md +++ b/README.md @@ -109,28 +109,6 @@ PRs are kept in priority order major → minor → patch. PRs. The block is schema-versioned so future format changes can't cause this action to mistreat newer PRs. -## Debugging - -The CLI supports three offline modes useful during config development: - -```bash -# Show the resolved config (with defaults applied). -./exe/importmap-update --print-config - -# Capture `bin/importmap outdated` and `bin/importmap audit` output, -# then see what the planner would do without hitting GitHub. -./exe/importmap-update --print-plan \ - --outdated-file /tmp/outdated.txt \ - --audit-file /tmp/audit.txt - -# Same but also runs the reconciler against a YAML file of mocked -# existing PRs — useful for verifying close/force-push behavior. -./exe/importmap-update --print-actions \ - --outdated-file /tmp/outdated.txt \ - --audit-file /tmp/audit.txt \ - --existing-prs /tmp/existing.yml -``` - ## Development ```bash diff --git a/exe/importmap-update b/exe/importmap-update index 5e434db..3ee7d97 100755 --- a/exe/importmap-update +++ b/exe/importmap-update @@ -2,16 +2,7 @@ # frozen_string_literal: true # -# Entry point for the importmap-update. -# -# Modes: -# --print-config Print the resolved config and exit. -# --print-plan Run parsers + planner against captured -# outdated/audit output. -# --print-actions Run planner + reconciler. Existing PRs come -# from a YAML file (offline) or from gh. -# (default) Full run: parse outdated/audit, plan, -# reconcile against live gh, execute. +# Entry point for the importmap-update action. # # Environment variables consumed by the action: # INPUT_CONFIG_FILE Path to the YAML config (default @@ -42,20 +33,14 @@ options = { config_path: ENV.fetch("INPUT_CONFIG_FILE", ".github/importmap-updates.yml"), outdated_file: nil, audit_file: nil, - existing_prs_file: nil, dry_run: %w[true 1 yes].include?(ENV["IMPORTMAP_DRY_RUN"].to_s.downcase) } -action = :run OptionParser.new do |opts| opts.banner = "Usage: importmap-update [options]" opts.on("-c", "--config PATH", "Path to config YAML") { |p| options[:config_path] = p } - opts.on("--print-config", "Print resolved config and exit") { action = :print_config } - opts.on("--print-plan", "Run planner; needs --outdated-file --audit-file") { action = :print_plan } - opts.on("--print-actions", "Run planner + reconciler; needs --outdated-file --audit-file [--existing-prs]") { action = :print_actions } opts.on("--outdated-file PATH", "Captured `bin/importmap outdated` output") { |p| options[:outdated_file] = p } opts.on("--audit-file PATH", "Captured `bin/importmap audit` output") { |p| options[:audit_file] = p } - opts.on("--existing-prs PATH", "YAML file listing current PRs (offline)") { |p| options[:existing_prs_file] = p } opts.on("--dry-run", "Do not perform side effects; log what would happen") { options[:dry_run] = true } opts.on("-h", "--help") { puts opts @@ -70,127 +55,66 @@ rescue Importmap::Update::Config::ConfigError => e exit 2 end -# ---- helpers shared by debug and run modes ---- - -def require_files!(opts, *keys) - missing = keys.select { |k| opts[k].nil? } - return if missing.empty? +missing = [:outdated_file, :audit_file].select { |k| options[k].nil? } +unless missing.empty? warn "Missing required option(s): #{missing.map { |k| "--#{k.to_s.tr("_", "-")}" }.join(", ")}" exit 2 end -def build_plan(opts, config) - outdated_output = File.read(opts[:outdated_file]) - audit_output = File.read(opts[:audit_file]) - outdated = Importmap::Update::Parsers::OutdatedParser.parse(outdated_output) - vulnerabilities = Importmap::Update::Parsers::AuditParser.parse(audit_output) - Importmap::Update::Planner.new( - outdated: outdated, vulnerabilities: vulnerabilities, config: config - ).call +repo = ENV["GITHUB_REPOSITORY"] +if repo.nil? || repo.empty? + warn "GITHUB_REPOSITORY is not set; refusing to run." + exit 2 end -def load_existing_prs_from_file(path) - return [] if path.nil? || !File.exist?(path) - raw = YAML.safe_load_file(path, permitted_classes: [], aliases: false) || [] - raw.map { |h| - Importmap::Update::Reconciler::ExistingPR.new( - number: h["number"], branch: h["branch"], title: h["title"], body: h["body"].to_s - ) - } +token = ENV["GITHUB_TOKEN"] || ENV["GH_TOKEN"] +if token.nil? || token.empty? + warn "GITHUB_TOKEN (or GH_TOKEN) is not set; refusing to run." + exit 2 end -# ---- modes ---- - -case action -when :print_config - puts config.to_yaml - -when :print_plan - require_files!(options, :outdated_file, :audit_file) - plan = build_plan(options, config) - puts({ - "pr_specs" => plan.pr_specs.map { |s| - { - "kind" => s.kind.to_s, "branch" => s.branch, "title" => s.title, - "packages" => s.packages.map { |p| - h = {"name" => p.name, "from" => p.from, "to" => p.to, "semver_kind" => p.semver_kind.to_s} - h["severity"] = p.advisory[:severity] if p.advisory - h - } - } - }, - "warnings" => plan.warnings - }.to_yaml) - -when :print_actions - require_files!(options, :outdated_file, :audit_file) - plan = build_plan(options, config) - existing_prs = load_existing_prs_from_file(options[:existing_prs_file]) - result = Importmap::Update::Reconciler.new(plan: plan, existing_prs: existing_prs).call - puts({ - "actions" => result.actions.map { |a| - h = {"type" => a.type.to_s} - h["branch"] = (a.pr_spec || a.existing_pr).branch - h["existing_pr_number"] = a.existing_pr.number if a.existing_pr - h["title"] = a.pr_spec.title if a.pr_spec - h["reason"] = a.reason if a.reason - h - }, - "ignored" => result.ignored.map { |pr| {"number" => pr.number, "branch" => pr.branch} }, - "warnings" => plan.warnings - }.to_yaml) - -when :run - require_files!(options, :outdated_file, :audit_file) - - repo = ENV["GITHUB_REPOSITORY"] - if repo.nil? || repo.empty? - warn "GITHUB_REPOSITORY is not set; refusing to run." - exit 2 - end - - token = ENV["GITHUB_TOKEN"] || ENV["GH_TOKEN"] - if token.nil? || token.empty? - warn "GITHUB_TOKEN (or GH_TOKEN) is not set; refusing to run." - exit 2 - end - - rails_root = ENV.fetch("RAILS_ROOT", ".") - runner = Importmap::Update::Commands::ShellRunner.new(cwd: rails_root) - gh = Importmap::Update::GitHubClient.new(repo: repo, token: token) - git = Importmap::Update::GitClient.new( - repo: Git.open(rails_root), - author_name: ENV.fetch("IMPORTMAP_AUTHOR_NAME", "github-actions[bot]"), - author_email: ENV.fetch("IMPORTMAP_AUTHOR_EMAIL", "github-actions[bot]@users.noreply.github.com") - ) - - plan = build_plan(options, config) - existing_prs = gh.list_open_prs(branch_prefix: config.branch_prefix) - reconciled = Importmap::Update::Reconciler.new(plan: plan, existing_prs: existing_prs).call - - executor = Importmap::Update::Executor.new( - gh: gh, git: git, runner: runner, - base_branch: ENV.fetch("IMPORTMAP_BASE_BRANCH", "main"), - commit_message_prefix: config.commit_message.prefix, - labels: config.labels, - dry_run: options[:dry_run] - ) - report = executor.call(reconciled.actions) - - # Compact, easy-to-read run summary on stderr; the Actions log captures this. - warn "===== importmap-update summary =====" - warn "Dry run: #{options[:dry_run]}" - warn "Plan warnings:" - plan.warnings.each { |w| warn " - #{w}" } - warn "Reconciler ignored #{reconciled.ignored.size} foreign PR(s)." - warn "Actions:" - report.outcomes.each do |o| - desc = [o.branch || "?", "PR##{o.pr_number}"].compact.join(" ") - warn " - #{o.type} [#{o.status}] #{desc} #{"\u2014 " + o.detail if o.detail}" - end - report.warnings.each { |w| warn " ! #{w}" } - - # Exit non-zero only if any non-skipped outcome failed; in dry run all are - # skipped, which is a successful run. - exit(report.outcomes.any?(&:failed?) ? 1 : 0) +rails_root = ENV.fetch("RAILS_ROOT", ".") +runner = Importmap::Update::Commands::ShellRunner.new(cwd: rails_root) +gh = Importmap::Update::GitHubClient.new(repo: repo, token: token) +git = Importmap::Update::GitClient.new( + repo: Git.open(rails_root), + author_name: ENV.fetch("IMPORTMAP_AUTHOR_NAME", "github-actions[bot]"), + author_email: ENV.fetch("IMPORTMAP_AUTHOR_EMAIL", "github-actions[bot]@users.noreply.github.com") +) + +outdated_output = File.read(options[:outdated_file]) +audit_output = File.read(options[:audit_file]) +outdated = Importmap::Update::Parsers::OutdatedParser.parse(outdated_output) +vulnerabilities = Importmap::Update::Parsers::AuditParser.parse(audit_output) +plan = Importmap::Update::Planner.new( + outdated: outdated, vulnerabilities: vulnerabilities, config: config +).call + +existing_prs = gh.list_open_prs(branch_prefix: config.branch_prefix) +reconciled = Importmap::Update::Reconciler.new(plan: plan, existing_prs: existing_prs).call + +executor = Importmap::Update::Executor.new( + gh: gh, git: git, runner: runner, + base_branch: ENV.fetch("IMPORTMAP_BASE_BRANCH", "main"), + commit_message_prefix: config.commit_message.prefix, + labels: config.labels, + dry_run: options[:dry_run] +) +report = executor.call(reconciled.actions) + +# Compact, easy-to-read run summary on stderr; the Actions log captures this. +warn "===== importmap-update summary =====" +warn "Dry run: #{options[:dry_run]}" +warn "Plan warnings:" +plan.warnings.each { |w| warn " - #{w}" } +warn "Reconciler ignored #{reconciled.ignored.size} foreign PR(s)." +warn "Actions:" +report.outcomes.each do |o| + desc = [o.branch || "?", "PR##{o.pr_number}"].compact.join(" ") + warn " - #{o.type} [#{o.status}] #{desc} #{"— " + o.detail if o.detail}" end +report.warnings.each { |w| warn " ! #{w}" } + +# Exit non-zero only if any non-skipped outcome failed; in dry run all are +# skipped, which is a successful run. +exit(report.outcomes.any?(&:failed?) ? 1 : 0) diff --git a/lib/config.rb b/lib/config.rb index eb9dd26..3d62c8e 100644 --- a/lib/config.rb +++ b/lib/config.rb @@ -60,14 +60,6 @@ def self.default new(DEFAULTS) end - # ---- inspection / debug ---- - - # Render as YAML for `--print-config` debugging. Symbols are emitted - # as plain strings so the output is copy-pasteable into a real config. - def to_yaml - stringify(to_h).to_yaml - end - def to_h { version: version, @@ -233,20 +225,6 @@ def self.deep_merge(base, override) end end end - - # Symbols → strings for YAML output (so `--print-config` is copy-pasteable). - def stringify(value) - case value - when Hash - value.each_with_object({}) { |(k, v), acc| acc[k.to_s] = stringify(v) } - when Array - value.map { |v| stringify(v) } - when Symbol - value.to_s - else - value - end - end end end end diff --git a/test/config_test.rb b/test/config_test.rb index e55f842..79cd41b 100644 --- a/test/config_test.rb +++ b/test/config_test.rb @@ -198,27 +198,6 @@ def test_rejects_branch_prefix_with_spaces assert_includes err.message, "branch_prefix" end - # ---- to_yaml / --print-config ---- - - def test_to_yaml_round_trips_to_an_equivalent_config - # The user should be able to run `--print-config`, paste the output into - # their repo, and have it load back to an identical Config. - original = Config.default - Tempfile.create(["config", ".yml"]) do |f| - f.write(original.to_yaml) - f.flush - reloaded = Config.load(f.path) - assert_equal original.to_h, reloaded.to_h - end - end - - def test_to_yaml_emits_symbols_as_plain_strings - yaml = Config.default.to_yaml - # Strategy values should appear as `individual`, not `:individual`. - refute_match(/:\s*:individual/, yaml) - assert_match(/strategy: (grouped|individual)/, yaml) - end - def test_constructor_is_private # Force callers through Config.load / Config.default so validation always runs. assert_raises(NoMethodError) { Config.new({}) }