From e93b33423b374a72ccce5921733cf2465f2afd5d Mon Sep 17 00:00:00 2001 From: Olivier Halligon Date: Mon, 17 Aug 2020 20:42:00 +0200 Subject: [PATCH] Release 0.14.0 (#300) * Introduce script to automate release * Rakefile levelup * Version 0.14.0 * Fix newline at end of podspec.json * Ensure we start and end on master branch And that we pull latest master before tagging * CRLF at EOF * Remove [:version] param from `release:finish` task It can be guessed from the current podspec version * Fix create_release task * Ensure we run rake via bundle exec Co-authored-by: David Jennes Co-authored-by: David Jennes --- CHANGELOG.md | 2 +- Gemfile | 7 +++ Gemfile.lock | 105 +++++++++++++++++++++++++++++++++++++++++ Rakefile | 10 ++++ Stencil.podspec.json | 9 ++-- docs/conf.py | 4 +- docs/installation.rst | 4 +- rakelib/changelog.rake | 34 +++++++++++++ rakelib/github.rake | 52 ++++++++++++++++++++ rakelib/pod.rake | 21 +++++++++ rakelib/release.rake | 67 ++++++++++++++++++++++++++ rakelib/utils.rake | 28 +++++++++++ 12 files changed, 335 insertions(+), 8 deletions(-) create mode 100644 Gemfile create mode 100644 Gemfile.lock create mode 100755 Rakefile create mode 100644 rakelib/changelog.rake create mode 100644 rakelib/github.rake create mode 100644 rakelib/pod.rake create mode 100644 rakelib/release.rake create mode 100644 rakelib/utils.rake diff --git a/CHANGELOG.md b/CHANGELOG.md index a879fc0..1b34dd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Stencil Changelog -## Master +## 0.14.0 ### Breaking diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..3fd311f --- /dev/null +++ b/Gemfile @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +gem "octokit" +gem "cocoapods" +gem "rake" diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..189b5e8 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,105 @@ +GEM + remote: https://rubygems.org/ + specs: + CFPropertyList (3.0.2) + activesupport (4.2.11.3) + i18n (~> 0.7) + minitest (~> 5.1) + thread_safe (~> 0.3, >= 0.3.4) + tzinfo (~> 1.1) + addressable (2.7.0) + public_suffix (>= 2.0.2, < 5.0) + algoliasearch (1.27.3) + httpclient (~> 2.8, >= 2.8.3) + json (>= 1.5.1) + atomos (0.1.3) + claide (1.0.3) + cocoapods (1.9.3) + activesupport (>= 4.0.2, < 5) + claide (>= 1.0.2, < 2.0) + cocoapods-core (= 1.9.3) + cocoapods-deintegrate (>= 1.0.3, < 2.0) + cocoapods-downloader (>= 1.2.2, < 2.0) + cocoapods-plugins (>= 1.0.0, < 2.0) + cocoapods-search (>= 1.0.0, < 2.0) + cocoapods-stats (>= 1.0.0, < 2.0) + cocoapods-trunk (>= 1.4.0, < 2.0) + cocoapods-try (>= 1.1.0, < 2.0) + colored2 (~> 3.1) + escape (~> 0.0.4) + fourflusher (>= 2.3.0, < 3.0) + gh_inspector (~> 1.0) + molinillo (~> 0.6.6) + nap (~> 1.0) + ruby-macho (~> 1.4) + xcodeproj (>= 1.14.0, < 2.0) + cocoapods-core (1.9.3) + activesupport (>= 4.0.2, < 6) + algoliasearch (~> 1.0) + concurrent-ruby (~> 1.1) + fuzzy_match (~> 2.0.4) + nap (~> 1.0) + netrc (~> 0.11) + typhoeus (~> 1.0) + cocoapods-deintegrate (1.0.4) + cocoapods-downloader (1.4.0) + cocoapods-plugins (1.0.0) + nap + cocoapods-search (1.0.0) + cocoapods-stats (1.1.0) + cocoapods-trunk (1.5.0) + nap (>= 0.8, < 2.0) + netrc (~> 0.11) + cocoapods-try (1.2.0) + colored2 (3.1.2) + concurrent-ruby (1.1.6) + escape (0.0.4) + ethon (0.12.0) + ffi (>= 1.3.0) + faraday (1.0.1) + multipart-post (>= 1.2, < 3) + ffi (1.13.1) + fourflusher (2.3.1) + fuzzy_match (2.0.4) + gh_inspector (1.1.3) + httpclient (2.8.3) + i18n (0.9.5) + concurrent-ruby (~> 1.0) + json (2.3.1) + minitest (5.14.1) + molinillo (0.6.6) + multipart-post (2.1.1) + nanaimo (0.3.0) + nap (1.1.0) + netrc (0.11.0) + octokit (4.18.0) + faraday (>= 0.9) + sawyer (~> 0.8.0, >= 0.5.3) + public_suffix (4.0.5) + rake (13.0.1) + ruby-macho (1.4.0) + sawyer (0.8.2) + addressable (>= 2.3.5) + faraday (> 0.8, < 2.0) + thread_safe (0.3.6) + typhoeus (1.4.0) + ethon (>= 0.9.0) + tzinfo (1.2.7) + thread_safe (~> 0.1) + xcodeproj (1.17.1) + CFPropertyList (>= 2.3.3, < 4.0) + atomos (~> 0.1.3) + claide (>= 1.0.2, < 2.0) + colored2 (~> 3.1) + nanaimo (~> 0.3.0) + +PLATFORMS + ruby + +DEPENDENCIES + cocoapods + octokit + rake + +BUNDLED WITH + 2.1.4 diff --git a/Rakefile b/Rakefile new file mode 100755 index 0000000..3a2a392 --- /dev/null +++ b/Rakefile @@ -0,0 +1,10 @@ + +PODSPEC_FILE = 'Stencil.podspec.json' +CHANGELOG_FILE = 'CHANGELOG.md' + +if ENV['BUNDLE_GEMFILE'].nil? + puts "\u{274C} Please use bundle exec" + exit 1 +end + +task :default => 'release:new' diff --git a/Stencil.podspec.json b/Stencil.podspec.json index 8d72d4c..876fbf9 100644 --- a/Stencil.podspec.json +++ b/Stencil.podspec.json @@ -1,6 +1,6 @@ { "name": "Stencil", - "version": "0.13.1", + "version": "0.14.0", "summary": "Stencil is a simple and powerful template language for Swift.", "homepage": "https://stencil.fuller.li", "license": { @@ -13,7 +13,7 @@ "social_media_url": "https://twitter.com/kylefuller", "source": { "git": "https://github.com/stencilproject/Stencil.git", - "tag": "0.13.1" + "tag": "0.14.0" }, "source_files": [ "Sources/*.swift" @@ -24,7 +24,10 @@ "tvos": "9.0" }, "cocoapods_version": ">= 1.7.0", - "swift_versions": ["4.2", "5.0"], + "swift_versions": [ + "4.2", + "5.0" + ], "requires_arc": true, "dependencies": { "PathKit": [ diff --git a/docs/conf.py b/docs/conf.py index aefffdf..16532c9 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -58,9 +58,9 @@ author = 'Kyle Fuller' # built documents. # # The short X.Y version. -version = '0.13.1' +version = '0.14.0' # The full version, including alpha/beta/rc tags. -release = '0.13.1' +release = '0.14.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/installation.rst b/docs/installation.rst index 75c71e5..8903729 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -26,7 +26,7 @@ If you're using CocoaPods, you can add Stencil to your ``Podfile`` and then run .. code-block:: ruby - pod 'Stencil', '~> 0.13.1' + pod 'Stencil', '~> 0.14.0' Carthage -------- @@ -37,7 +37,7 @@ Carthage .. code-block:: text - github "stencilproject/Stencil" ~> 0.13.1 + github "stencilproject/Stencil" ~> 0.14.0 2) Checkout your dependencies, generate the Stencil Xcode project, and then use Carthage to build Stencil: diff --git a/rakelib/changelog.rake b/rakelib/changelog.rake new file mode 100644 index 0000000..9a9f80d --- /dev/null +++ b/rakelib/changelog.rake @@ -0,0 +1,34 @@ +NEW_CHANGELOG_SECTION = "## Master\n" + ['Breaking', 'Enhancements', 'Deprecations', 'Bug Fixes', 'Internal Changes'].map do |s| + <<~MARKDOWN + + ### #{s} + + _None_ + MARKDOWN +end.join + +def changelog_first_section + content = [] + section_count = 0 + File.foreach(CHANGELOG_FILE) do |line| + section_count += 1 if line.start_with?('## ') + break if section_count > 1 + content.append(line) if section_count == 1 + end + content[1..].join +end + +namespace :changelog do + # rake changelog:reset + desc "Add a new empty section at the top of the changelog and git push it" + task :reset do + header "Reset CHANGELOG" + content = File.read(CHANGELOG_FILE) + new_content = NEW_CHANGELOG_SECTION + "\n" + content + File.write(CHANGELOG_FILE, new_content) + + sh("git", "add", CHANGELOG_FILE) + sh("git", "commit", "-m", "Reset CHANGELOG") + sh("git", "push") + end +end diff --git a/rakelib/github.rake b/rakelib/github.rake new file mode 100644 index 0000000..db0160f --- /dev/null +++ b/rakelib/github.rake @@ -0,0 +1,52 @@ +require 'octokit' + +def repo_slug + url_parts = `git remote get-url origin`.chomp.split(%r{/|:}) + last_two_parts = url_parts[-2..-1].join('/') + last_two_parts.gsub(/\.git$/, '') +end + +def github_client + Octokit::Client.new(:netrc => true) +end + +namespace :github do + # rake github:create_release_pr[version] + task :create_release_pr, [:version] do |_, args| + version = args[:version] + branch = release_branch(version) + + title = "Release #{version}" + body = <<~BODY + This PR prepares the release for version #{version}. + + Once the PR is merged into master, run `bundle exec rake release:finish` to tag and push to trunk. + BODY + + header "Opening PR" + res = github_client.create_pull_request(repo_slug, "master", branch, title, body) + info "Pull request created: #{res['html_url']}" + end + + # rake github:tag + task :tag do + tag = current_pod_version + sh("git", "tag", tag) + sh("git", "push", origin, tag) + end + + # rake github:create_release + task :create_release do + tag_name = current_pod_version + title = tag_name + body = changelog_first_section() + res = github_client.create_release(repo_slug, tag_name, name: title, body: body) + info "GitHub Release created: #{res['html_url']}" + end + + # rake github:pull_master + task :pull_master do + sh("git", "switch", "master") + sh("git", "pull") + end +end diff --git a/rakelib/pod.rake b/rakelib/pod.rake new file mode 100644 index 0000000..4d68c77 --- /dev/null +++ b/rakelib/pod.rake @@ -0,0 +1,21 @@ +require 'json' + +def current_pod_version + JSON.parse(File.read(PODSPEC_FILE))['version'] +end + +namespace :pod do + # rake pod:lint + desc "Lint the podspec" + task :lint do + header "Linting podspec" + sh("pod", "lib", "lint", PODSPEC_FILE) + end + + # rake pod:push + desc "Push the podspec to trunk" + task :push do + header "Pushing podspec to trunk" + sh("pod", "trunk", "push", PODSPEC_FILE) + end +end diff --git a/rakelib/release.rake b/rakelib/release.rake new file mode 100644 index 0000000..8c69a28 --- /dev/null +++ b/rakelib/release.rake @@ -0,0 +1,67 @@ +require 'json' + +namespace :release do + + # rake release:new + desc "Ask for a version number and prepare a release PR for that version" + task :new do + info "Current version is: #{current_pod_version}" + print "What version do you want to release? " + new_version = STDIN.gets.chomp + + Rake::Task['release:start'].invoke(new_version) + end + + # rake release:start[version] + desc "Start a release by creating a PR with the required changes to bump the version" + task :start, [:version] => ['release:create_branch', 'release:update_files', 'pod:lint', 'release:push_branch', 'github:create_release_pr', 'github:pull_master'] + + # rake release:finish[version] + desc "Finish a release after the PR has been merged, by tagging master and pushing to trunk" + task :finish => ['github:pull_master', 'github:tag', 'pod:push', 'github:create_release', 'changelog:reset'] + + + ### Helper tasks ### + + # rake release:create_branch[version] + task :create_branch, [:version] do |_, args| + branch = release_branch(args[:version]) + + header "Creating release branch" + sh("git", "checkout", "-b", branch) + end + + # rake release:update_files[version] + task :update_files, [:version] do |_, args| + version = args[:version] + + header "Updating files for version #{version}" + + podspec = JSON.parse(File.read(PODSPEC_FILE)) + podspec['version'] = version + podspec['source']['tag'] = version + File.write(PODSPEC_FILE, JSON.pretty_generate(podspec) + "\n") + + replace(CHANGELOG_FILE, '## Master' => "\#\# #{version}") + replace("docs/conf.py", + /^version = .*/ => %Q(version = '#{version}'), + /^release = .*/ => %Q(release = '#{version}') + ) + replace("docs/installation.rst", + /pod 'Stencil', '.*'/ => %Q(pod 'Stencil', '~> #{version}'), + /github "stencilproject\/Stencil" ~> .*/ => %Q(github "stencilproject/Stencil" ~> #{version}) + ) + + ## Commit Changes + sh("git", "add", PODSPEC_FILE, CHANGELOG_FILE, "docs/*") + sh("git", "commit", "-m", "Version #{version}") + end + + # rake release:push_branch[version] + task :push_branch, [:version] do |_, args| + branch = release_branch(args[:version]) + + header "Pushing #{branch} to origin" + sh("git", "push", "-u", "origin", branch) + end +end diff --git a/rakelib/utils.rake b/rakelib/utils.rake new file mode 100644 index 0000000..6d4f7d9 --- /dev/null +++ b/rakelib/utils.rake @@ -0,0 +1,28 @@ +def colorize(string, *codes) + if `tput colors`.chomp.to_i >= 8 + code = codes.join(';') + puts "\e[#{code}m" + string + "\e[0m" + else + puts string + end +end + +def header(title) + puts colorize("==> #{title}...", 1, 32) # bold, green +end + +def info(string) + puts colorize(string, 34) # blue +end + +def release_branch(version) + "release/#{version}" +end + +def replace(file, replacements) + content = File.read(file) + replacements.each do |match, replacement| + content.gsub!(match, replacement) + end + File.write(file, content) +end