aboutsummaryrefslogtreecommitdiffstats
path: root/tasks/release.rb
diff options
context:
space:
mode:
Diffstat (limited to 'tasks/release.rb')
-rw-r--r--tasks/release.rb197
1 files changed, 156 insertions, 41 deletions
diff --git a/tasks/release.rb b/tasks/release.rb
index 7c8b884d44..1e83814bae 100644
--- a/tasks/release.rb
+++ b/tasks/release.rb
@@ -1,12 +1,27 @@
-FRAMEWORKS = %w( activesupport activemodel activerecord actionview actionpack activejob actionmailer railties )
+# frozen_string_literal: true
-root = File.expand_path('../../', __FILE__)
+FRAMEWORKS = %w( activesupport activemodel activerecord actionview actionpack activejob actionmailer actioncable activestorage railties )
+FRAMEWORK_NAMES = Hash.new { |h, k| k.split(/(?<=active|action)/).map(&:capitalize).join(" ") }
+
+root = File.expand_path("..", __dir__)
version = File.read("#{root}/RAILS_VERSION").strip
tag = "v#{version}"
directory "pkg"
-(FRAMEWORKS + ['rails']).each do |framework|
+# This "npm-ifies" the current version number
+# With npm, versions such as "5.0.0.rc1" or "5.0.0.beta1.1" are not compliant with its
+# versioning system, so they must be transformed to "5.0.0-rc1" and "5.0.0-beta1-1" respectively.
+
+# "5.0.1" --> "5.0.1"
+# "5.0.1.1" --> "5.0.1-1" *
+# "5.0.0.rc1" --> "5.0.0-rc1"
+#
+# * This makes it a prerelease. That's bad, but we haven't come up with
+# a better solution at the moment.
+npm_version = version.gsub(/\./).with_index { |s, i| i >= 2 ? "-" : s }
+
+(FRAMEWORKS + ["rails"]).each do |framework|
namespace framework do
gem = "pkg/#{framework}-#{version}.gem"
gemspec = "#{framework}.gemspec"
@@ -27,7 +42,7 @@ directory "pkg"
file = Dir[glob].first
ruby = File.read(file)
- major, minor, tiny, pre = version.split('.')
+ major, minor, tiny, pre = version.split(".", 4)
pre = pre ? pre.inspect : "nil"
ruby.gsub!(/^(\s*)MAJOR(\s*)= .*?$/, "\\1MAJOR = #{major}")
@@ -42,56 +57,75 @@ directory "pkg"
ruby.gsub!(/^(\s*)PRE(\s*)= .*?$/, "\\1PRE = #{pre}")
raise "Could not insert PRE in #{file}" unless $1
- File.open(file, 'w') { |f| f.write ruby }
+ File.open(file, "w") { |f| f.write ruby }
+
+ require "json"
+ if File.exist?("#{framework}/package.json") && JSON.parse(File.read("#{framework}/package.json"))["version"] != npm_version
+ Dir.chdir("#{framework}") do
+ if sh "which npm"
+ sh "npm version #{npm_version} --no-git-tag-version"
+ else
+ raise "You must have npm installed to release Rails."
+ end
+ end
+ end
end
task gem => %w(update_versions pkg) do
cmd = ""
- cmd << "cd #{framework} && " unless framework == "rails"
- cmd << "gem build #{gemspec} && mv #{framework}-#{version}.gem #{root}/pkg/"
+ cmd += "cd #{framework} && " unless framework == "rails"
+ cmd += "bundle exec rake package && " unless framework == "rails"
+ cmd += "gem build #{gemspec} && mv #{framework}-#{version}.gem #{root}/pkg/"
sh cmd
end
- task :build => [:clean, gem]
- task :install => :build do
- sh "gem install #{gem}"
+ task build: [:clean, gem]
+ task install: :build do
+ sh "gem install --pre #{gem}"
end
- task :prep_release => [:ensure_clean_state, :build]
-
- task :push => :build do
+ task push: :build do
sh "gem push #{gem}"
+
+ if File.exist?("#{framework}/package.json")
+ Dir.chdir("#{framework}") do
+ npm_tag = /[a-z]/.match?(version) ? "pre" : "latest"
+ sh "npm publish --tag #{npm_tag}"
+ end
+ end
end
end
end
namespace :changelog do
task :header do
- (FRAMEWORKS + ['guides']).each do |fw|
- require 'date'
- fname = File.join fw, 'CHANGELOG.md'
+ (FRAMEWORKS + ["guides"]).each do |fw|
+ require "date"
+ fname = File.join fw, "CHANGELOG.md"
+ current_contents = File.read(fname)
- header = "## Rails #{version} (#{Date.today.strftime('%B %d, %Y')}) ##\n\n* No changes.\n\n\n"
- contents = header + File.read(fname)
- File.open(fname, 'wb') { |f| f.write contents }
+ header = "## Rails #{version} (#{Date.today.strftime('%B %d, %Y')}) ##\n\n"
+ header += "* No changes.\n\n\n" if current_contents.start_with?("##")
+ contents = header + current_contents
+ File.write(fname, contents)
end
end
task :release_date do
- (FRAMEWORKS + ['guides']).each do |fw|
- require 'date'
+ (FRAMEWORKS + ["guides"]).each do |fw|
+ require "date"
replace = "## Rails #{version} (#{Date.today.strftime('%B %d, %Y')}) ##\n"
- fname = File.join fw, 'CHANGELOG.md'
+ fname = File.join fw, "CHANGELOG.md"
contents = File.read(fname).sub(/^(## Rails .*)\n/, replace)
- File.open(fname, 'wb') { |f| f.write contents }
+ File.write(fname, contents)
end
end
task :release_summary do
- (FRAMEWORKS + ['guides']).each do |fw|
+ (FRAMEWORKS + ["guides"]).each do |fw|
puts "## #{fw}"
- fname = File.join fw, 'CHANGELOG.md'
+ fname = File.join fw, "CHANGELOG.md"
contents = File.readlines fname
contents.shift
changes = []
@@ -103,41 +137,122 @@ namespace :changelog do
end
namespace :all do
- task :build => FRAMEWORKS.map { |f| "#{f}:build" } + ['rails:build']
- task :update_versions => FRAMEWORKS.map { |f| "#{f}:update_versions" } + ['rails:update_versions']
- task :install => FRAMEWORKS.map { |f| "#{f}:install" } + ['rails:install']
- task :push => FRAMEWORKS.map { |f| "#{f}:push" } + ['rails:push']
+ task build: FRAMEWORKS.map { |f| "#{f}:build" } + ["rails:build"]
+ task update_versions: FRAMEWORKS.map { |f| "#{f}:update_versions" } + ["rails:update_versions"]
+ task install: FRAMEWORKS.map { |f| "#{f}:install" } + ["rails:install"]
+ task push: FRAMEWORKS.map { |f| "#{f}:push" } + ["rails:push"]
task :ensure_clean_state do
- unless `git status -s | grep -v 'RAILS_VERSION\\|CHANGELOG\\|Gemfile.lock'`.strip.empty?
+ unless `git status -s | grep -v 'RAILS_VERSION\\|CHANGELOG\\|Gemfile.lock\\|package.json\\|version.rb\\|tasks/release.rb'`.strip.empty?
abort "[ABORTING] `git status` reports a dirty tree. Make sure all changes are committed"
end
- unless ENV['SKIP_TAG'] || `git tag | grep '^#{tag}$'`.strip.empty?
+ unless ENV["SKIP_TAG"] || `git tag | grep '^#{tag}$'`.strip.empty?
abort "[ABORTING] `git tag` shows that #{tag} already exists. Has this version already\n"\
" been released? Git tagging can be skipped by setting SKIP_TAG=1"
end
end
+ task verify: :install do
+ app_name = "pkg/verify-#{version}-#{Time.now.to_i}"
+ sh "rails _#{version}_ new #{app_name} --skip-bundle" # Generate with the right version.
+ cd app_name
+
+ # Replace the generated gemfile entry with the exact version.
+ File.write("Gemfile", File.read("Gemfile").sub(/^gem 'rails.*/, "gem 'rails', '#{version}'"))
+ sh "bundle"
+
+ sh "rails generate scaffold user name admin:boolean && rails db:migrate"
+
+ puts "Booting a Rails server. Verify the release by:"
+ puts
+ puts "- Seeing the correct release number on the root page"
+ puts "- Viewing /users"
+ puts "- Creating a user"
+ puts "- Updating a user (e.g. disable the admin flag)"
+ puts "- Deleting a user on /users"
+ puts "- Whatever else you want."
+ begin
+ sh "rails server"
+ rescue Interrupt
+ # Server passes along interrupt. Prevent halting verify task.
+ end
+ end
+
task :bundle do
- sh 'bundle check'
+ sh "bundle check"
end
task :commit do
- File.open('pkg/commit_message.txt', 'w') do |f|
- f.puts "# Preparing for #{version} release\n"
- f.puts
- f.puts "# UNCOMMENT THE LINE ABOVE TO APPROVE THIS COMMIT"
- end
+ unless `git status -s`.strip.empty?
+ File.open("pkg/commit_message.txt", "w") do |f|
+ f.puts "# Preparing for #{version} release\n"
+ f.puts
+ f.puts "# UNCOMMENT THE LINE ABOVE TO APPROVE THIS COMMIT"
+ end
- sh "git add . && git commit --verbose --template=pkg/commit_message.txt"
- rm_f "pkg/commit_message.txt"
+ sh "git add . && git commit --verbose --template=pkg/commit_message.txt"
+ rm_f "pkg/commit_message.txt"
+ end
end
task :tag do
- sh "git tag -m '#{tag} release' #{tag}"
+ sh "git tag -s -m '#{tag} release' #{tag}"
sh "git push --tags"
end
- task :release => %w(ensure_clean_state build bundle commit tag push)
+ task prep_release: %w(ensure_clean_state build bundle commit)
+
+ task release: %w(prep_release tag push)
+end
+
+module Announcement
+ class Version
+ def initialize(version)
+ @version, @gem_version = version, Gem::Version.new(version)
+ end
+
+ def to_s
+ @version
+ end
+
+ def previous
+ @gem_version.segments[0, 3].tap { |v| v[2] -= 1 }.join(".")
+ end
+
+ def major_or_security?
+ @gem_version.segments[2].zero? || @gem_version.segments[3].is_a?(Integer)
+ end
+
+ def rc?
+ @version =~ /rc/
+ end
+ end
+end
+
+task :announce do
+ Dir.chdir("pkg/") do
+ versions = ENV["VERSIONS"] ? ENV["VERSIONS"].split(",") : [ version ]
+ versions = versions.sort.map { |v| Announcement::Version.new(v) }
+
+ raise "Only valid for patch releases" if versions.any?(&:major_or_security?)
+
+ if versions.any?(&:rc?)
+ require "date"
+ future_date = Date.today + 5
+ future_date += 1 while future_date.saturday? || future_date.sunday?
+
+ github_user = `git config github.user`.chomp
+ end
+
+ require "erb"
+ template = File.read("../tasks/release_announcement_draft.erb")
+
+ match = ERB.version.match(/\Aerb\.rb \[(?<version>[^ ]+) /)
+ if match && match[:version] >= "2.2.0" # Ruby 2.6+
+ puts ERB.new(template, trim_mode: "<>").result(binding)
+ else
+ puts ERB.new(template, nil, "<>").result(binding)
+ end
+ end
end