require 'rubygems' require 'bundler/setup' require 'colorize' require 'json' require 'tempfile' require_relative '../command_processor' require_relative '../config_hash' require_relative '../copy_file' require_relative '../current_release' require_relative '../downstream' require_relative '../downstream_repo' require_relative '../publisher' require_relative '../validator' require_relative '../changelog/importer' require_relative '../changelog/updater'
include Releasinator
DOWNSTREAM_REPOS = “downstream_repos”
desc “read and validate the config, adding one if not found” task :config do
@releasinator_config = ConfigHash.new(verbose == true, Rake.application.options.trace == true) @releasinator_config.freeze @validator = Validator.new(@releasinator_config) @validator.freeze @validator.validate_config
end
namespace :validate do
desc "validate the presence, formatting, and semver sequence of CHANGELOG.md" task :changelog => [:config, :git] do @current_release = @validator.validate_changelog(DOWNSTREAM_REPOS) @current_release.freeze @downstream = Downstream.new(@releasinator_config, @validator, @current_release) @downstream.freeze end desc "validate important text files end in a newline character" task :eof_newlines => :config do @validator.validate_eof_newlines end desc "validate releasinator is up to date" task :releasinator_version => :config do @validator.validate_releasinator_version end desc "validate your path has some useful tools" task :paths => :config do @validator.validate_in_path("wget") @validator.validate_in_path("git") end desc "validate git version is acceptable" task :git_version => :config do @validator.validate_git_version end desc "validate git reports no untracked, unstaged, or uncommitted changes" task :git => :config do @validator.validate_clean_git end desc "validate current branch matches the latest on the server and follows naming conventions" task :branch => [:config, :changelog] do @validator.validate_branches(@current_release.version) end desc "validate the presence of README.md, renaming a similar file if found" task :readme => :config do @validator.validate_exist('.', "README.md", DOWNSTREAM_REPOS) @validator.validate_exist(@releasinator_config.base_dir, "README.md", DOWNSTREAM_REPOS) if '.' != @releasinator_config.base_dir end desc "validate the presence of LICENSE, renaming a similar file if found - also validates that its referenced from README.md" task :license => :config do @validator.validate_exist(@releasinator_config.base_dir, "LICENSE", DOWNSTREAM_REPOS) @validator.validate_referenced_in_readme("LICENSE") end desc "validate the presence of CONTRIBUTING.md, renaming a similar file if found - also validates that its referenced from README.md" task :contributing => :config do @validator.validate_exist(@releasinator_config.base_dir, "CONTRIBUTING.md", DOWNSTREAM_REPOS) @validator.validate_referenced_in_readme("CONTRIBUTING.md") end desc "validate the presence of .github/ISSUE_TEMPLATE.md" task :issue_template => :config do @validator.validate_exist(@releasinator_config.base_dir, ".github/ISSUE_TEMPLATE.md", DOWNSTREAM_REPOS) end desc "validate the presence of .gitignore, adding any appropriate releasinator lines if necessary" task :gitignore => :config do @validator.validate_exist('.', ".gitignore", DOWNSTREAM_REPOS) @validator.validate_exist(@releasinator_config.base_dir, ".gitignore", DOWNSTREAM_REPOS) if '.' != @releasinator_config.base_dir @validator.validate_gitignore_contents(".DS_Store") if @releasinator_config.has_key?(:downstream_repos) @validator.validate_gitignore_contents("#{DOWNSTREAM_REPOS}/") end end desc "validate all submodules are on the latest origin/master versions" task :submodules => :config do @validator.validate_submodules end desc "validate the current user can push to local repo" task :github_permissions_local => [:config] do @validator.validate_github_permissions(GitUtil.repo_url) end desc "validate the current user can push to downstream repos" task :github_permissions_downstream, [:downstream_repo_index] => [:config] do |t, args| @downstream.validate_github_permissions(args) end desc "run any configatron.custom_validation_methods" task :custom => [:config, :changelog] do if @releasinator_config.has_key?(:custom_validation_methods) @releasinator_config[:custom_validation_methods].each do |validate_method| validate_method.call end Printer.success("All configatron.custom_validation_methods succeeded.") else Printer.success("No configatron.custom_validation_methods found.") end end desc "validate all" task :all => [ :paths, :eof_newlines, :git_version, :gitignore, :submodules, :readme, :changelog, :license, :contributing, :issue_template, :github_permissions_local, :github_permissions_downstream, :releasinator_version, :custom, :git, :branch ] do Printer.success("All validations passed.") end
end
desc “Update release version and CHANGELOG” task :update_version_and_changelog do
begin Changelog::Updater.bump_version do |version, semver_type| @releasinator_config[:update_version_method].call(version, semver_type) Changelog::Updater.prompt_for_change_log(version, semver_type) GitUtil.stage if @releasinator_config.has_key? :update_version_commit_message GitUtil.commit(@releasinator_config[:update_version_commit_message]) else GitUtil.commit("Update version and CHANGELOG.md for #{version}") end end rescue Exception => e GitUtil.reset_head(true) Printer.fail("Failed to update version: #{e}") abort() end
end
desc “release all” task :release => [:“validate:all”] do
last_tag_raw = GitUtil.tagged_versions(remote=true, raw_tags=true).last last_tag = last_tag_raw last_tag = last_tag[1..last_tag.size] if last_tag.start_with? "v" if !last_tag_raw.nil? # If last tag is nil, at this point, there must be changelog entry, but this is the first releasinator release, proceed. commits_since_tag = GitUtil.commits(last_tag_raw) last_tag = Semantic::Version.new(last_tag) if commits_since_tag.size > 0 # There are new commits to be released current_version = @current_release.version current_version = current_version[1..current_version.size] if current_version.start_with? "v" if current_version > last_tag # CHANGELOG.md version is ahead of last tag. The releaser has already updated the changelog, and we've validated it if !Printer.ask_binary("The version from CHANGELOG.md '#{@current_release.version}' is greater than the last tagged version '#{last_tag}'. Have you already updated your version and CHANGELOG.md?") Printer.fail("Update your version and CHANGELOG.md and re-run rake release.") abort() end elsif @releasinator_config.has_key? :update_version_method if Printer.ask_binary("It doesn't look like your CHANGELOG.md has been updated. HEAD is #{commits_since_tag.size} commits ahead of tag #{last_tag}. Do you want to update CHANGELOG.md and version now?") Rake::Task[:update_version_and_changelog].invoke Rake::Task[:"validate:changelog"].reenable Rake::Task[:"validate:changelog"].invoke else Printer.fail("Update your version and CHANGELOG.md and re-run rake release.") abort() end else Printer.fail("It doesn't look like your CHANGELOG.md has been updated. HEAD is #{commits_since_tag.size} commits ahead of last tagged version '#{last_tag}'. Please update CHANGELOG.md or implement update_version_method in .releasinator.rb to allow releasinator to perform this step on your behalf. See https://github.com/paypal/releasinator for more details.") abort() end elsif !Printer.ask_binary("There are no new commits since last tagged version '#{last_tag}'. Are you sure you want to release?") abort() end elsif !Printer.ask_binary("This release (#{@current_release.version}) is the first release. Do you want to continue?") abort() end [:"local:build",:"pm:all",:"downstream:all",:"local:push",:"docs:all"].each do |task| Rake::Task[task].invoke end Printer.success("Done releasing #{@current_release.version}")
end
namespace :import do
desc "import a changelog from release notes contained within GitHub releases" task :changelog => [:config] do Changelog::Importer.new(@releasinator_config).import(GitUtil.repo_url) end
end
namespace :local do
desc "ask user whether to proceed with release" task :confirm => [:config, :"validate:changelog"] do Printer.check_proceed("You're about to release #{@current_release.version}!", "Then no release for you!") end desc "change branch for git flow, if using git flow" task :prepare => [:config, :"validate:changelog"] do if @releasinator_config.use_git_flow() CommandProcessor.command("git checkout -b release/#{@current_release.version} develop") unless GitUtil.get_current_branch() != "develop" end end desc "tag the local repo" task :tag => [:config, :"validate:changelog"] do GitUtil.tag(@current_release.version, @current_release.changelog) end desc "iterate over the prerelease_checklist_items, asking the user if each is done" task :checklist => [:config] do @releasinator_config[:prerelease_checklist_items].each do |prerelease_item| Printer.check_proceed("#{prerelease_item}", "Then no release for you!") end end desc "build the local repo" task :build => [:config, :"validate:changelog", :checklist, :confirm, :prepare, :tag] do puts "building #{@current_release.version}" if @releasinator_config[:verbose] @releasinator_config[:build_method].call if @releasinator_config.has_key? :post_build_methods @releasinator_config[:post_build_methods].each do |post_build_method| post_build_method.call(@current_release.version) end end end desc "run the git flow branch magic (if configured) and push local to remote" task :push => [:config, :"validate:changelog"] do if @releasinator_config.use_git_flow() CommandProcessor.command("git checkout master") CommandProcessor.command("git merge --no-ff release/#{@current_release.version}") GitUtil.delete_branch "release/#{@current_release.version}" # still on master, so let's push it end GitUtil.push_branch("master") if @releasinator_config.use_git_flow() # switch back to develop to merge and continue development GitUtil.checkout("develop") CommandProcessor.command("git merge master") GitUtil.push_branch("develop") end GitUtil.push_tag(@current_release.version) if @releasinator_config[:release_to_github] # TODO - check that the tag exists CommandProcessor.command("sleep 5") Publisher.new(@releasinator_config).publish(GitUtil.repo_url, @current_release) end if @releasinator_config.has_key? :post_push_methods @releasinator_config[:post_push_methods].each do |post_push_method| post_push_method.call(@current_release.version) end end end
end
namespace :pm do
desc "publish and wait for package manager" task :all => [:publish, :wait] desc "call configured publish_to_package_manager_method" task :publish => [:config, :"validate:changelog"] do @releasinator_config[:publish_to_package_manager_method].call(@current_release.version) end desc "call configured wait_for_package_manager_method" task :wait => [:config, :"validate:changelog"] do @releasinator_config[:wait_for_package_manager_method].call(@current_release.version) end
end
def copy_the_file(root_dir, copy_file, version=nil)
Dir.mkdir(copy_file.target_dir) unless File.exist?(copy_file.target_dir) # use __VERSION__ to auto-substitute the version in any input param source_file_name = copy_file.source_file.gsub("__VERSION__", "#{version}") target_dir_name = copy_file.target_dir.gsub("__VERSION__", "#{version}") destination_file_name = copy_file.target_name.gsub("__VERSION__", "#{version}") CommandProcessor.command("cp -R #{root_dir}/#{source_file_name} #{target_dir_name}/#{destination_file_name}")
end
def get_new_branch_name(new_branch_name, version)
new_branch_name.gsub("__VERSION__", "#{version}")
end
namespace :downstream do
desc "build, package, and push all downstream repos" task :all => [:reset,:prepare,:build,:package,:push] do Printer.success("Done with all downstream tasks.") end desc "reset the downstream repos to their starting state" task :reset, [:downstream_repo_index] => [:config, :"validate:changelog"] do |t, args| @downstream.reset(args) end desc "prepare downstream release, copying files from base_docs_dir and any other configured files" task :prepare, [:downstream_repo_index] => [:config, :"validate:changelog", :reset] do |t, args| @downstream.prepare(args) end desc "call all build_methods for each downstream repo" task :build, [:downstream_repo_index] => [:config,:"validate:changelog"] do |t, args| @downstream.build(args) end desc "tag all non-branch downstream repos" task :package, [:downstream_repo_index] => [:config,:"validate:changelog"] do |t, args| @downstream.package(args) end desc "push tags and creates draft release, or pushes branch and creates pull request, depending on the presence of new_branch_name" task :push, [:downstream_repo_index] => [:config,:"validate:changelog"] do |t, args| @downstream.push(args) end
end
namespace :docs do
desc "build, copy, and push docs to gh-pages branch" task :all => [:build, :package, :push] desc "build docs" task :build => [:config] do if @releasinator_config.has_key?(:doc_build_method) @releasinator_config[:doc_build_method].call Printer.success("doc_build_method done.") else Printer.success("No doc_build_method found.") end end desc "copy and commit docs to gh-pages branch" task :package => [:config,:"validate:changelog"] do if @releasinator_config.has_key?(:doc_files_to_copy) root_dir = Dir.pwd.strip Dir.chdir(@releasinator_config.doc_target_dir) do current_branch = GitUtil.get_current_branch() GitUtil.init_gh_pages() GitUtil.reset_repo("gh-pages") @releasinator_config[:doc_files_to_copy].each do |copy_file| copy_the_file(root_dir, copy_file) end CommandProcessor.command("git add .") CommandProcessor.command("git commit -m \"Update docs for release #{@current_release.version}\"") # switch back to previous branch CommandProcessor.command("git checkout #{current_branch}") end Printer.success("Doc files copied.") else Printer.success("No doc_files_to_copy found.") end end desc "push gh-pages branch" task :push => [:config] do if @releasinator_config.has_key?(:doc_build_method) Dir.chdir(@releasinator_config.doc_target_dir) do current_branch = GitUtil.get_current_branch() CommandProcessor.command("git checkout gh-pages") GitUtil.push_branch("gh-pages") # switch back to previous branch CommandProcessor.command("git checkout #{current_branch}") end Printer.success("Docs pushed.") else Printer.success("No docs pushed.") end end
end
def replace_string(filepath, string_to_replace, new_string)
text = File.read(filepath) new_contents = text.gsub(string_to_replace, new_string) File.open(filepath, "w") {|file| file.puts new_contents }
end