0
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-08-06 13:08:40 +08:00
discourse/spec/script/upcoming_changes_status_report_spec.rb
Martin Brennan 67d2410919
DEV: Fix UC status report when reverted files encountered (#41821)
Fixes the following error when generating the upcoming changes status
report
on a weekly basis in GitHub actions:

```
/__w/discourse/discourse/lib/upcoming_changes/status_report.rb:56:in 'UpcomingChanges::StatusReport::Git#capture': git show 45535887f1231b40f8a21b8c154e58cef8670063:plugins/discourse-workflows/config/settings.yml failed: fatal: path 'plugins/discourse-workflows/config/settings.yml' exists on disk, but not in '45535887f1' (RuntimeError)
	from /__w/discourse/discourse/lib/upcoming_changes/status_report.rb:47:in 'UpcomingChanges::StatusReport::Git#show_file'
	from /__w/discourse/discourse/lib/upcoming_changes/status_report.rb:227:in 'UpcomingChanges::StatusReport::GitHistory#statuses_at'
	from /__w/discourse/discourse/lib/upcoming_changes/status_report.rb:215:in 'block in UpcomingChanges::StatusReport::GitHistory#add_history_for_settings_file'
	from /__w/discourse/discourse/lib/upcoming_changes/status_report.rb:214:in 'Array#reverse_each'
	from /__w/discourse/discourse/lib/upcoming_changes/status_report.rb:214:in 'UpcomingChanges::StatusReport::GitHistory#add_history_for_settings_file'
	from /__w/discourse/discourse/lib/upcoming_changes/status_report.rb:205:in 'block in UpcomingChanges::StatusReport::GitHistory#by_change'
	from /__w/discourse/discourse/lib/upcoming_changes/status_report.rb:202:in 'Hash#each'
	from /__w/discourse/discourse/lib/upcoming_changes/status_report.rb:202:in 'Enumerable#each_with_object'
	from /__w/discourse/discourse/lib/upcoming_changes/status_report.rb:202:in 'UpcomingChanges::StatusReport::GitHistory#by_change'
	from /__w/discourse/discourse/lib/upcoming_changes/status_report.rb:422:in 'UpcomingChanges::StatusReport#history_by_change'
	from /__w/discourse/discourse/lib/upcoming_changes/status_report.rb:363:in 'block in UpcomingChanges::StatusReport#report'
	from /__w/discourse/discourse/lib/upcoming_changes/status_report.rb:362:in 'Hash#each'
	from /__w/discourse/discourse/lib/upcoming_changes/status_report.rb:362:in 'Enumerable#map'
	from /__w/discourse/discourse/lib/upcoming_changes/status_report.rb:362:in 'UpcomingChanges::StatusReport#report'
	from /__w/discourse/discourse/lib/upcoming_changes/status_report.rb:499:in 'UpcomingChanges::StatusReport::CLI.run'
	from /__w/discourse/discourse/script/upcoming_changes_status_report:9:in '<main>'
```

This happened because we were trying to run `git sho` on a file that was
reverted in a commit. Now, we check whether the file exists for the
specific commit hash before running git show, and continue with the
next valid commit for the file.
2026-07-20 16:37:35 +10:00

254 lines
8.4 KiB
Ruby
Vendored

# frozen_string_literal: true
require "fileutils"
require "open3"
require "tmpdir"
RSpec.describe UpcomingChanges::StatusReport do
subject(:report) do
described_class.new(repo_path:, stale_after_days: 14, now: Time.iso8601("2026-05-26T00:00:00Z"))
end
let(:repo_path) { Dir.mktmpdir("upcoming-changes-status-report") }
let(:commit_shas) { {} }
before do
git("init")
git("config", "user.name", "Discourse CI")
git("config", "user.email", "ci@ci.invalid")
write_settings(
"experimental_change" => "experimental",
"alpha_change" => "alpha",
"beta_change" => "beta",
"recent_change" => "alpha",
"stable_change" => "stable",
"conceptual_change" => "conceptual",
"permanent_change" => "permanent",
"never_change" => "never",
)
write_plugin_settings("plugin_alpha_change" => "alpha")
commit_shas[:original] = commit(
"FEATURE: Add upcoming changes (#123)",
date: "2026-04-01T12:00:00Z",
author_name: "Alice Example",
author_email: "alice@example.com",
)
remove_plugin_settings
commit_shas[:plugin_removed] = commit(
"DEV: Remove plugin settings",
date: "2026-04-15T12:00:00Z",
author_name: "Alice Example",
author_email: "alice@example.com",
)
write_plugin_settings("plugin_alpha_change" => "alpha")
commit_shas[:plugin_restored] = commit(
"DEV: Restore plugin settings",
date: "2026-04-20T12:00:00Z",
author_name: "Alice Example",
author_email: "alice@example.com",
)
write_settings(
"experimental_change" => "experimental",
"alpha_change" => "alpha",
"beta_change" => "beta",
"recent_change" => "beta",
"stable_change" => "stable",
"conceptual_change" => "conceptual",
"permanent_change" => "permanent",
"never_change" => "never",
)
write_plugin_settings("plugin_alpha_change" => "alpha")
commit_shas[:recent] = commit(
"DEV: Bump recent upcoming change (#456)",
date: "2026-05-20T12:00:00Z",
author_name: "Bob Example",
author_email: "bob@example.com",
)
end
after { FileUtils.remove_entry(repo_path) }
describe "#report" do
it "reports eligibility and git metadata", :aggregate_failures do
records = report.report.index_by { |record| record.fetch(:name) }
expect(records.fetch("experimental_change")).to include(
settings_path: "config/site_settings.yml",
current_status: "experimental",
next_status: "alpha",
eligible: true,
eligibility_reason: "status_unchanged_for_14_days",
last_status_change_commit: commit_shas[:original],
original_commit: commit_shas[:original],
original_author_name: "Alice Example",
original_author_email: "alice@example.com",
original_pr_number: "123",
branch: "dev/upcoming-change-status-bump/experimental_change",
title: "FEATURE: Bump experimental_change upcoming change to alpha",
pr_label: "upcoming-change",
)
expect(records.fetch("experimental_change").fetch(:pr_body)).to include(
"<!-- upcoming-change-status-pr:experimental_change -->",
"This automated PR moves `experimental_change` from `experimental` to `alpha`",
"Original PR: #123",
)
expect(records.fetch("alpha_change")).to include(
current_status: "alpha",
next_status: "beta",
eligible: true,
)
expect(records.fetch("beta_change")).to include(
current_status: "beta",
next_status: "stable",
eligible: true,
)
recent_change = records.fetch("recent_change")
expect(recent_change).to include(
current_status: "beta",
next_status: "stable",
eligible: false,
eligibility_reason: "status_changed_recently",
last_status_change_commit: commit_shas[:recent],
days_since_status_change: 5,
)
expect(Time.iso8601(recent_change.fetch(:last_status_change_date))).to eq(
Time.iso8601("2026-05-20T12:00:00Z"),
)
expect(records.fetch("stable_change")).to include(
current_status: "stable",
next_status: nil,
eligible: false,
eligibility_reason: "terminal_status",
)
expect(records.fetch("conceptual_change")).to include(
current_status: "conceptual",
eligible: false,
eligibility_reason: "terminal_status",
)
expect(records.fetch("permanent_change")).to include(
current_status: "permanent",
eligible: false,
eligibility_reason: "terminal_status",
)
expect(records.fetch("never_change")).to include(
current_status: "never",
eligible: false,
eligibility_reason: "terminal_status",
)
expect(records.fetch("plugin_alpha_change")).to include(
settings_path: "plugins/chat/config/settings.yml",
current_status: "alpha",
next_status: "beta",
eligible: true,
original_commit: commit_shas[:original],
last_status_change_commit: commit_shas[:original],
)
end
end
describe "#apply" do
it "updates only the target setting file" do
report.apply("plugin_alpha_change")
metadata =
described_class::MetadataLoader.from_file(
File.join(repo_path, "plugins/chat/config/settings.yml"),
strict: true,
)
expect(metadata.transform_values { |value| value[:status].to_s }).to include(
plugin_alpha_change: "beta",
)
expect(File.read(File.join(repo_path, "config/site_settings.yml"))).to include(
" alpha_change:\n default: false\n client: true\n hidden: true\n upcoming_change:\n status: alpha\n",
)
end
end
describe described_class::SourceStatusUpdater do
it "raises when the status line cannot be edited" do
settings_file = File.join(repo_path, "config/site_settings.yml")
File.write(settings_file, <<~YAML)
experimental:
malformed_change:
upcoming_change:
status: "alpha beta"
YAML
updater = described_class.new(settings_file:)
expect {
updater.update!(change_name: "malformed_change", next_status: "beta")
}.to raise_error(RuntimeError, /Could not parse status line/)
end
end
def git(*args, env: {})
stdout, stderr, status = Open3.capture3(env, "git", "-C", repo_path, *args)
raise "git #{args.join(" ")} failed: #{stderr}" if !status.success?
stdout
end
def commit(message, date:, author_name:, author_email:)
git("add", ".")
git(
"commit",
"-m",
message,
env: {
"GIT_AUTHOR_DATE" => date,
"GIT_COMMITTER_DATE" => date,
"GIT_AUTHOR_NAME" => author_name,
"GIT_AUTHOR_EMAIL" => author_email,
"GIT_COMMITTER_NAME" => "Discourse CI",
"GIT_COMMITTER_EMAIL" => "ci@ci.invalid",
},
)
git("rev-parse", "HEAD").strip
end
def write_settings(statuses)
FileUtils.mkdir_p(File.join(repo_path, "config"))
File.write(File.join(repo_path, "config/site_settings.yml"), "experimental:\n")
File.open(File.join(repo_path, "config/site_settings.yml"), "a") do |file|
statuses.each do |name, status|
file.write(" #{name}:\n")
file.write(" default: false\n")
file.write(" client: true\n")
file.write(" hidden: true\n")
file.write(" upcoming_change:\n")
file.write(" status: #{status}\n")
file.write(" impact: \"feature,all_members\"\n")
file.write(" learn_more_url: \"https://meta.discourse.org/t/-/123\"\n")
end
end
end
def write_plugin_settings(statuses)
FileUtils.mkdir_p(File.join(repo_path, "plugins/chat/config"))
File.write(File.join(repo_path, "plugins/chat/config/settings.yml"), "chat:\n")
File.open(File.join(repo_path, "plugins/chat/config/settings.yml"), "a") do |file|
statuses.each do |name, status|
file.write(" #{name}:\n")
file.write(" default: false\n")
file.write(" client: true\n")
file.write(" hidden: true\n")
file.write(" upcoming_change:\n")
file.write(" status: #{status}\n")
file.write(" impact: \"feature,all_members\"\n")
file.write(" learn_more_url: \"https://meta.discourse.org/t/-/123\"\n")
end
end
end
def remove_plugin_settings
FileUtils.rm_rf(File.join(repo_path, "plugins/chat"))
end
end