|
| 1 | +## Releasing a new version of octofacts |
| 2 | +## |
| 3 | +## 1. Update `.version` with new version number |
| 4 | +## 2. Run `script/bootstrap` to update Gemfile.lock |
| 5 | +## 3. Commit changes, PR, and merge to master |
| 6 | +## 4. Check out master branch locally |
| 7 | +## 5. Run `bundle exec rake gem:release` |
| 8 | + |
| 9 | +require "fileutils" |
| 10 | +require "open3" |
| 11 | +require "shellwords" |
| 12 | + |
| 13 | +module Octofacts |
| 14 | + # A class to contain methods and constants for cleaner code |
| 15 | + class Gem |
| 16 | + BASEDIR = File.expand_path("..", File.dirname(__FILE__)).freeze |
| 17 | + GEMS = ["octofacts", "octofacts-updater"].freeze |
| 18 | + PKGDIR = File.join(BASEDIR, "pkg").freeze |
| 19 | + |
| 20 | + # Verify that Gemfile.lock matches .version and that it's committed, since `bundle exec ...` will |
| 21 | + # update the file for us. |
| 22 | + def self.verify_gemfile_version! |
| 23 | + bundler = Bundler::LockfileParser.new(Bundler.read_file(File.expand_path("../Gemfile.lock", File.dirname(__FILE__)))) |
| 24 | + gems = bundler.specs.select { |specs| GEMS.include?(specs.name) } |
| 25 | + GEMS.each do |gem| |
| 26 | + this_gem = gems.detect { |g| g.name == gem } |
| 27 | + unless this_gem |
| 28 | + raise "Did not find #{gem} in Gemfile.lock" |
| 29 | + end |
| 30 | + unless this_gem.version.to_s == version |
| 31 | + raise "Gem #{gem} is version #{this_gem.version}, not #{version}" |
| 32 | + end |
| 33 | + end |
| 34 | + |
| 35 | + puts "Ensuring that all changes are committed." |
| 36 | + exec_command("git diff-index --quiet HEAD --") |
| 37 | + puts "OK: All gems on #{version} and no uncommitted changes here." |
| 38 | + end |
| 39 | + |
| 40 | + # Read the version number from the .version file in the root of the project. |
| 41 | + def self.version |
| 42 | + @version ||= File.read(File.expand_path("../.version", File.dirname(__FILE__))).strip |
| 43 | + end |
| 44 | + |
| 45 | + # Determine what branch we are on |
| 46 | + def self.branch |
| 47 | + exec_command("git rev-parse --abbrev-ref HEAD").strip |
| 48 | + end |
| 49 | + |
| 50 | + # Build the gem and put it into the 'pkg' directory |
| 51 | + def self.build |
| 52 | + Dir.mkdir PKGDIR unless File.directory?(PKGDIR) |
| 53 | + GEMS.each do |gem| |
| 54 | + begin |
| 55 | + output_file = File.join(BASEDIR, "#{gem}-#{version}.gem") |
| 56 | + target_file = File.join(PKGDIR, "#{gem}-#{version}.gem") |
| 57 | + exec_command("gem build #{gem}.gemspec") |
| 58 | + unless File.file?(output_file) |
| 59 | + raise "gem #{gem} failed to create expected output file" |
| 60 | + end |
| 61 | + FileUtils.mv output_file, target_file |
| 62 | + puts "Generated #{target_file}" |
| 63 | + ensure |
| 64 | + # Clean up the *.gem generated in the main directory if it's still there |
| 65 | + FileUtils.rm(output_file) if File.file?(output_file) |
| 66 | + end |
| 67 | + end |
| 68 | + end |
| 69 | + |
| 70 | + # Push the gem to rubygems |
| 71 | + def self.push |
| 72 | + GEMS.each do |gem| |
| 73 | + target_file = File.join(PKGDIR, "#{gem}-#{version}.gem") |
| 74 | + unless File.file?(target_file) |
| 75 | + raise "Cannot push: #{target_file} does not exist" |
| 76 | + end |
| 77 | + end |
| 78 | + GEMS.each do |gem| |
| 79 | + target_file = File.join(PKGDIR, "#{gem}-#{version}.gem") |
| 80 | + exec_command("gem push #{Shellwords.escape(target_file)}") |
| 81 | + end |
| 82 | + end |
| 83 | + |
| 84 | + # Tag the release on GitHub |
| 85 | + def self.tag |
| 86 | + # Make sure we have not released this version before |
| 87 | + exec_command("git fetch -t origin") |
| 88 | + tags = exec_command("git tag -l").split(/\n/) |
| 89 | + raise "There is already a #{version} tag" if tags.include?(version) |
| 90 | + |
| 91 | + # Tag it |
| 92 | + exec_command("git tag #{Shellwords.escape(version)}") |
| 93 | + exec_command("git push origin master") |
| 94 | + exec_command("git push origin #{Shellwords.escape(version)}") |
| 95 | + end |
| 96 | + |
| 97 | + # Yank gem from rubygems |
| 98 | + def self.yank |
| 99 | + GEMS.each do |gem| |
| 100 | + exec_command("gem yank #{gem} -v #{Shellwords.escape(version)}") |
| 101 | + end |
| 102 | + end |
| 103 | + |
| 104 | + # Utility method: Execute command |
| 105 | + def self.exec_command(command) |
| 106 | + STDERR.puts "Command: #{command}" |
| 107 | + output, code = Open3.capture2e(command, chdir: BASEDIR) |
| 108 | + return output if code.exitstatus.zero? |
| 109 | + STDERR.puts "Output:\n#{output}" |
| 110 | + STDERR.puts "Exit code: #{code.exitstatus}" |
| 111 | + exit code.exitstatus |
| 112 | + end |
| 113 | + end |
| 114 | +end |
| 115 | + |
| 116 | +namespace :gem do |
| 117 | + task "build" do |
| 118 | + branch = Octofacts::Gem.branch |
| 119 | + unless branch == "master" |
| 120 | + raise "On a non-master branch #{branch}; use gem:force-build if you really want to do this" |
| 121 | + end |
| 122 | + Octofacts::Gem.build |
| 123 | + end |
| 124 | + |
| 125 | + task "check" do |
| 126 | + Octofacts::Gem.verify_gemfile_version! |
| 127 | + end |
| 128 | + |
| 129 | + task "force-build" do |
| 130 | + branch = Octofacts::Gem.branch |
| 131 | + unless branch == "master" |
| 132 | + warn "WARNING: Force-building from non-master branch #{branch}" |
| 133 | + end |
| 134 | + Octofacts::Gem.build |
| 135 | + end |
| 136 | + |
| 137 | + task "push" do |
| 138 | + Octofacts::Gem.push |
| 139 | + end |
| 140 | + |
| 141 | + task "release" do |
| 142 | + branch = Octofacts::Gem.branch |
| 143 | + unless branch == "master" |
| 144 | + raise "On a non-master branch #{branch}; refusing to release" |
| 145 | + end |
| 146 | + [:check, :build, :tag, :push].each { |t| Rake::Task["gem:#{t}"].invoke } |
| 147 | + end |
| 148 | + |
| 149 | + task "tag" do |
| 150 | + branch = Octofacts::Gem.branch |
| 151 | + raise "On a non-master branch #{branch}; refusing to tag" unless branch == "master" |
| 152 | + Octofacts::Gem.tag |
| 153 | + end |
| 154 | + |
| 155 | + task "yank" do |
| 156 | + Octofacts::Gem.yank |
| 157 | + end |
| 158 | +end |
0 commit comments