mirror of
https://gh.wpcy.net/https://github.com/discourse/discourse.git
synced 2026-04-30 21:10:03 +08:00
206 lines
5.4 KiB
Ruby
206 lines
5.4 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require "open3"
|
|
require_relative "release_utils/version"
|
|
|
|
module ReleaseUtils
|
|
PRIMARY_RELEASE_TAG = "release"
|
|
RELEASE_TAGS = [PRIMARY_RELEASE_TAG, "beta", "latest-release"].freeze
|
|
PRIMARY_ESR_TAG = "esr"
|
|
ESR_TAGS = [PRIMARY_ESR_TAG, "stable"].freeze
|
|
PR_LABEL = "release"
|
|
|
|
def self.dry_run?
|
|
!!ENV["DRY_RUN"]
|
|
end
|
|
|
|
def self.test_mode?
|
|
ENV["RUNNING_RELEASE_IN_RSPEC_TESTS"] == "1"
|
|
end
|
|
|
|
def self.read_versions_json
|
|
ReleaseUtils.with_clean_worktree("main") { JSON.parse(File.read("versions.json")) }
|
|
end
|
|
|
|
def self.released_versions
|
|
read_versions_json
|
|
.select { |_version, info| info["released"] }
|
|
.keys
|
|
.sort_by { |v| Gem::Version.new(v) }
|
|
end
|
|
|
|
def self.released_esrs
|
|
read_versions_json
|
|
.select { |_version, info| info["released"] && info["esr"] }
|
|
.keys
|
|
.sort_by { |v| Gem::Version.new(v) }
|
|
end
|
|
|
|
def self.read_version_rb
|
|
File.read("lib/version.rb")
|
|
end
|
|
|
|
def self.write_version(version)
|
|
File.write("lib/version.rb", read_version_rb.sub(/STRING = ".*"/, "STRING = \"#{version}\""))
|
|
end
|
|
|
|
def self.commit_version_bump(version, message)
|
|
write_version(version)
|
|
git "add", "lib/version.rb"
|
|
git "commit", "-m", message
|
|
end
|
|
|
|
def self.update_versions_json(new_version)
|
|
today_date = DateTime.now.utc.strftime("%Y-%m-%d")
|
|
|
|
version_year, version_month = new_version.split(".").map(&:to_i)
|
|
esr = [1, 7].include?(version_month)
|
|
|
|
support_period = esr ? 8.months : 2.months
|
|
support_end_date = (Date.new(version_year, version_month, 1) + support_period).strftime("%Y-%m")
|
|
|
|
new_version_info = {
|
|
new_version => {
|
|
developmentStartDate: today_date,
|
|
releaseDate: "#{version_year}-#{version_month.to_s.rjust(2, "0")}",
|
|
supportEndDate: support_end_date,
|
|
released: false,
|
|
esr: esr,
|
|
supported: true,
|
|
},
|
|
}
|
|
|
|
data = JSON.parse(File.read("versions.json"))
|
|
data.transform_values! do |v|
|
|
if !v["released"]
|
|
v["released"] = true
|
|
v["releaseDate"] = today_date
|
|
end
|
|
|
|
if v["supported"] &&
|
|
Date.parse(v["supportEndDate"] + "-01") < Date.new(version_year, version_month, 1)
|
|
v["supported"] = false
|
|
v["supportEndDate"] = today_date
|
|
end
|
|
|
|
v
|
|
end
|
|
|
|
File.write("versions.json", JSON.pretty_generate({ **new_version_info, **data }) + "\n")
|
|
end
|
|
|
|
def self.git(*args, allow_failure: false, silent: false)
|
|
puts "> git #{args.inspect}" unless silent
|
|
stdout, stderr, status = Open3.capture3({ "LEFTHOOK" => "0" }, "git", *args)
|
|
if !status.success? && !allow_failure
|
|
raise "Command failed: git #{args.inspect}\n#{stdout.indent(2)}\n#{stderr.indent(2)}"
|
|
end
|
|
stdout
|
|
end
|
|
|
|
def self.gh(*args, capture: false, input: nil)
|
|
puts "> gh #{args.inspect}"
|
|
return true if test_mode? && !capture
|
|
|
|
if capture
|
|
stdout_text, stderr_text, status = Open3.capture3("gh", *args, stdin_data: input)
|
|
|
|
return stdout_text if status.success?
|
|
|
|
raise <<~MESSAGE
|
|
gh failed with status #{status.exitstatus}
|
|
Command: gh #{args.join(" ")}
|
|
STDOUT:
|
|
#{stdout_text}
|
|
STDERR:
|
|
#{stderr_text}
|
|
MESSAGE
|
|
else
|
|
system "gh", *args
|
|
end
|
|
end
|
|
|
|
def self.supported_version_info
|
|
read_versions_json.select { |_version, info| info["supported"] }
|
|
end
|
|
|
|
def self.ref_exists?(ref)
|
|
git "rev-parse", "--verify", ref
|
|
true
|
|
rescue StandardError
|
|
false
|
|
end
|
|
|
|
def self.confirm(msg)
|
|
return true if test_mode?
|
|
TTY::Prompt.new.yes?(msg)
|
|
end
|
|
|
|
def self.confirm_or_abort(msg)
|
|
return if test_mode?
|
|
raise "Aborted" unless confirm(msg)
|
|
end
|
|
|
|
def self.merge_pr(base:, branch:)
|
|
if dry_run?
|
|
puts "[DRY RUN] Skipping merge of #{branch}"
|
|
return
|
|
end
|
|
|
|
loop do
|
|
confirm_or_abort "Ready to merge #{branch}?"
|
|
|
|
if test_mode?
|
|
git "push", "origin", "HEAD:#{base}"
|
|
break
|
|
else
|
|
if !gh("pr", "ready", branch) # remove draft status
|
|
puts "Failed to mark PR as ready-for-review... trying to merge anyway"
|
|
end
|
|
success = gh("pr", "merge", branch, "--rebase", "--delete-branch")
|
|
break if success
|
|
puts "Merge failed. Maybe the PR isn't approved yet, or there's a conflict."
|
|
end
|
|
end
|
|
|
|
puts "Merge successful"
|
|
end
|
|
|
|
def self.make_pr(base:, branch:, title: nil, body: nil, draft: false)
|
|
title ||= git("log", "-1", branch, "--pretty=%s").strip
|
|
body ||= git("log", "-1", branch, "--pretty=%b").strip
|
|
|
|
title_and_body = ["--title", title, "--body", body]
|
|
draft_flag = draft ? ["--draft"] : []
|
|
|
|
success =
|
|
gh(
|
|
"pr",
|
|
"create",
|
|
"--base",
|
|
base,
|
|
"--head",
|
|
branch,
|
|
*title_and_body,
|
|
*draft_flag,
|
|
"--label",
|
|
PR_LABEL,
|
|
) || gh("pr", "edit", branch, *title_and_body, "--add-label", PR_LABEL)
|
|
|
|
raise "Failed to create or update PR" unless success
|
|
end
|
|
|
|
def self.with_clean_worktree(origin_branch)
|
|
git "fetch", "origin", origin_branch
|
|
path = "#{Rails.root}/tmp/version-bump-worktree-#{SecureRandom.hex}"
|
|
begin
|
|
FileUtils.mkdir_p(path)
|
|
git "worktree", "add", "--detach", path, "origin/#{origin_branch}"
|
|
Dir.chdir(path) { yield } # rubocop:disable Discourse/NoChdir
|
|
ensure
|
|
puts "Cleaning up temporary worktree..."
|
|
git "worktree", "remove", "--force", path, silent: true, allow_failure: true
|
|
FileUtils.rm_rf(path)
|
|
end
|
|
end
|
|
end
|