releasinator 0.6.4 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0c78ceb11194e8ecddadf40d89435327f707f334
4
- data.tar.gz: a6c2516d0665a72ca1f853a6e0c4b8904408fffc
3
+ metadata.gz: ae6a23ab3be34c267540720c00929120c025ee98
4
+ data.tar.gz: 8a2dd6a07ea6d57d9921058a667e29e9dec14ece
5
5
  SHA512:
6
- metadata.gz: 5c9b905b61fc83b79081e36c2630974a9c68642352b4d346a8b1d2d762aac922f6af844f83e63193dfd079567822e9596b9bfcff926e59fb5d8b5d0be3c40acb
7
- data.tar.gz: 3b2959b1a4d07be4a9e160f74e95e9d9ec65a119df4904495d54de9efeb5c397e19bfbc20f57de3ebec23affde865b02e25d293ae4f1484444eb96a95c31a53d
6
+ metadata.gz: dc20169b9f864a5a41dd9907a286c212d7ab30fb1fbb0f39537f6d9be020a7f21f10b00b3ccb9be29cf3edf595d3d438ed47d2e90bee75a9a83f8f157b562cbc
7
+ data.tar.gz: 89a413e33460ae2f6c31dd85de46974ed0063353a876f7bbe66348517adab4c13fecb0f44f2f83fd706325ec8a196596ed764419bb1390232057ed251de6ca1f
data/.releasinator.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require 'releasinator/version'
2
+
1
3
  configatron.product_name = "Releasinator"
2
4
 
3
5
  # List of items to confirm from the person releasing. Required, but empty list is ok.
@@ -5,7 +7,6 @@ configatron.prerelease_checklist_items = [
5
7
  ]
6
8
 
7
9
  def validate_version_match()
8
- require 'releasinator/version'
9
10
  spec_version = Releasinator::VERSION
10
11
  if spec_version != @current_release.version
11
12
  Printer.fail("Ruby gem spec version #{spec_version} does not match changelog version #{@current_release.version}.")
@@ -49,6 +50,17 @@ end
49
50
  # The command that builds the project. Required.
50
51
  configatron.build_method = method(:build_method)
51
52
 
53
+ def update_version_method(version, semver_type)
54
+ version_file = "lib/releasinator/version.rb"
55
+
56
+ text = File.read(version_file)
57
+ replace = text.gsub(Releasinator::VERSION, version)
58
+ File.open(version_file, "w") {|file| file.puts replace}
59
+ end
60
+
61
+ # The command that populates a new version to all relevant files. Required.
62
+ configatron.update_version_method = method(:update_version_method)
63
+
52
64
  def publish_to_package_manager(version)
53
65
  output_dir = "build"
54
66
  Dir.chdir(output_dir) do
data/CHANGELOG.md CHANGED
@@ -1,6 +1,10 @@
1
1
  Releasinator release notes
2
2
  ==========================
3
3
 
4
+ 0.7.0
5
+ -----
6
+ * Optionally update version and CHANGELOG.md inline.
7
+
4
8
  0.6.4
5
9
  -----
6
10
  * Remove unused 'fileutils' dependency.
data/README.md CHANGED
@@ -100,6 +100,8 @@ A [default config file](lib/default_config.rb) is created when running any relea
100
100
  5. `configatron.doc_build_method`: The method that builds the docs.
101
101
  6. `configatron.doc_target_dir`: The directory where to run all git commands when publishing the docs. If not specified, the default is `.`. Generally useful if the docs are only applicable on a downstream release, rather than on the source itself.
102
102
  7. `configatron.doc_files_to_copy`: List of CopyFile objects for copying built docs into a targeted location. Please see documentation on the `CopyFile` class.
103
+ 8. `configatron.update_version_method`: Takes a new version, and populates to all relevant files. Only called if not first release, and updating version in-flow (see below).
104
+ 9. `configatron.update_version_commit_message`: The commit message used after updating the CHANGELOG and version. Only used if not first release, and updating version in-flow (see below).
103
105
 
104
106
  #### Appending existing tasks in the releasinator lifecycle:
105
107
 
@@ -110,6 +112,7 @@ task :"validate:changelog" do
110
112
  puts "validating changelog complete, let's dance!".red
111
113
  end
112
114
  ```
115
+
113
116
  This will append the task `validate:changelog`, running the code block after the official releasinator task contents have run. See [this blog post](http://www.dan-manges.com/blog/modifying-rake-tasks) for a detailed description of how this mechanism works. You may append a task more than once.
114
117
 
115
118
  ## Conventions
@@ -125,6 +128,20 @@ The releasinator enforces certain conventions. If a filename closely matches th
125
128
  3. Releases MUST not skip versions (such as `1.0.0 -> 1.0.2`, or `1.0.2 -> 1.1.1`). That is, if you skip or botch a release, it MUST be documented as an official release with a minimal description suggesting that this release is not actually available, or was skipped. This is for the benefit future maintainers of the repo to know that certain releases were not actually published or are not to be used (for whatever reason). In addition, external developers benefit when they look to upgrade versions and want to know what changes were made since a particular release. They can rest at ease knowing that there are no unaccounted-for releases.
126
129
  5. `.gitignore` and `.DS_store`: While this file is Mac-specific, many repos contain this entry in their `.gitignore` files because it is quite common for developers to have their global `.gitignore` configured incorrectly. Therefore, the authors of this project have made the decision to force this entry in all `.gitignore` files as a gesture of goodwill to all these new git users.
127
130
 
131
+ ## Inline updating of version and `CHANGELOG.md`
132
+
133
+ For your first release, the releasinator expects that the `CHANGELOG.md` contains an initial entry and that the initial version is placed as appropriate in relevant files.
134
+
135
+ For subsequent releases, the releasinator can handle updating the `CHANGELOG.md` and version numbers as part of the release flow. If you'd like to update your `CHANGELOG.md` and version manually, ensure that your release's `CHANGELOG.md` entry and version updates in any relevant files are pushed to your remote prior to releasing.
136
+
137
+ The releasinator will ask during the release flow whether or not to update the `CHANGELOG.md` and versions. If so, it:
138
+
139
+ 1. Checks the `CHANGELOG.md` for the most recently released version.
140
+ 2. Prompts for the type of release (major, minor, or patch).
141
+ 3. Increments the version number accordingly, and passes it to `configatron.update_version_method`, for you to update relevant files.
142
+ 4. Opens an editor and prompts you to provide a `CHANGELOG.md` entry.
143
+ 5. Updates the `CHANGELOG.md` accordingly.
144
+
128
145
  ## Behind the Scenes
129
146
 
130
147
  #### Validations (in no particular order)
@@ -0,0 +1,101 @@
1
+ require 'tmpdir'
2
+
3
+ require_relative '../command_processor'
4
+ require_relative '../printer'
5
+ require_relative '../git_util'
6
+
7
+ module Releasinator
8
+ module Changelog
9
+ class Updater
10
+
11
+ def self.bump_version(version=GitUtil.tags.last)
12
+ loop do
13
+ term = Printer.ask("What type of release is this? (major, minor, patch)")
14
+
15
+ case term
16
+ when "major", "minor", "patch"
17
+ current_version = Semantic::Version.new(version).increment!(term.to_sym).to_s
18
+ yield(current_version, term)
19
+
20
+ return current_version
21
+ else
22
+ Printer.fail("release type must be one of: [major, minor, patch]")
23
+ end
24
+ end
25
+ end
26
+
27
+ def self.prompt_for_change_log(version, semver_type)
28
+ new_changes = Dir.mktmpdir do |dir|
29
+ tmp_cl = "#{dir}/tmp-changelog-release#{Time.now.to_i}.md"
30
+ last_version = GitUtil.tags.last
31
+ tmp_change_log = "\n\n# Please enter a bulleted CHANGELOG list summarizing the changes for #{semver_type} version #{version}."
32
+ tmp_change_log += "\n# Lines starting with '# ' will be ignored."
33
+ tmp_change_log += "\n#"
34
+ tmp_change_log += "\n# Changes since #{last_version}:"
35
+ tmp_change_log += "\n#"
36
+ tmp_change_log += "\n# "
37
+ tmp_change_log += GitUtil.commits(from_tag=last_version).reverse.join("\n# ")
38
+ tmp_change_log += "\n#"
39
+ tmp_change_log += "\n"
40
+ File.foreach("CHANGELOG.md") do |line|
41
+ tmp_change_log += "# #{line}"
42
+ end
43
+ File.open(tmp_cl, "w") {|file| file.puts tmp_change_log }
44
+
45
+ editor = ENV["EDITOR"]
46
+ if editor == nil
47
+ Printer.fail("Value of $EDITOR environment variable must be set in order to edit CHANGELOG")
48
+ abort()
49
+ elsif "" == CommandProcessor.command("which #{editor} | cat")
50
+ Printer.fail("Value of $EDITOR (#{editor}) not found on path")
51
+ abort()
52
+ end
53
+
54
+ system("$EDITOR #{tmp_cl}")
55
+
56
+ new_changes = ""
57
+ File.foreach(tmp_cl) do |line|
58
+ if !line.start_with?("# ") && !line.start_with?("#\n")
59
+ new_changes += line
60
+ end
61
+ end
62
+
63
+ new_changes
64
+ end
65
+
66
+ self.update_changelog(new_changes, version)
67
+ end
68
+
69
+ private
70
+ def self.update_changelog(new_changes, version)
71
+ changelog = "CHANGELOG.md"
72
+
73
+ in_header = true
74
+ header = ""
75
+ old_changes = ""
76
+ File.open(changelog, "r") do |file|
77
+ file.each_line do |line|
78
+ if in_header
79
+ in_header = !/\d+\.\d+\.\d+$/.match(line)
80
+ end
81
+
82
+ if in_header
83
+ header += line
84
+ else
85
+ old_changes += line
86
+ end
87
+ end
88
+ end
89
+
90
+ h2 = old_changes.start_with? "##"
91
+ if h2
92
+ new_changes = "## #{version}\n" + new_changes
93
+ else
94
+ new_changes = "#{version}\n-----\n" + new_changes
95
+ end
96
+
97
+ File.open(changelog, "w") {|file| file.puts(header + new_changes.chomp + "\n" + old_changes.chomp) }
98
+ end
99
+ end
100
+ end
101
+ end
@@ -14,6 +14,18 @@ end
14
14
  # The command that builds the project. Required.
15
15
  configatron.build_method = method(:build_method)
16
16
 
17
+ def update_version_method(version, semver_type)
18
+ # semver_regex = /^version = "\d+.\d+.\d+"$/
19
+ # contents = File.read("setup.py")
20
+ # contents = contents.gsub(semver_regex, "version = \"\#{version}\"")
21
+ # File.open("setup.py", "w") do |f|
22
+ # f << contents
23
+ # end
24
+ end
25
+
26
+ # The command that populates a new version to all relevant files. Recommended.
27
+ # configatron.update_version_method = method(:update_version_method)
28
+
17
29
  def publish_to_package_manager(version)
18
30
  abort("please implement publish_to_package_manager method")
19
31
  end
data/lib/git_util.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require_relative 'command_processor'
2
+ require 'semantic'
2
3
 
3
4
  module Releasinator
4
5
  class GitUtil
@@ -152,5 +153,52 @@ module Releasinator
152
153
  end
153
154
  end
154
155
  end
156
+
157
+ def self.tags(remote=false)
158
+ if remote
159
+ CommandProcessor.command("git ls-remote --tags").split("\n")
160
+ .map { |tag| tag.split()[1].strip.gsub("refs/tags/", "") }
161
+ .keep_if { |tag| !tag.include? "{}" }
162
+ else
163
+ CommandProcessor.command("git tag --list").split("\n")
164
+ end
165
+ end
166
+
167
+ def self.tagged_versions(remote=false)
168
+ version_reg = Semantic::Version::SemVerRegexp
169
+ tags = self.tags(remote)
170
+
171
+ tags.map { |tag|
172
+ if tag.start_with? "v"
173
+ tag = tag[1..tag.size]
174
+ end
175
+
176
+ if tag =~ version_reg
177
+ tag
178
+ end
179
+ }.compact
180
+ end
181
+
182
+ def self.stage(files=".")
183
+ CommandProcessor.command("git add #{files}")
184
+ end
185
+
186
+ def self.commit(message)
187
+ CommandProcessor.command("git commit -m'#{message}'")
188
+ end
189
+
190
+ def self.reset_head(hard=false)
191
+ CommandProcessor.command("git reset#{' --hard' if hard} HEAD")
192
+ end
193
+
194
+ def self.commits(from_tag=nil, to_tag="HEAD")
195
+ rev = ""
196
+ if from_tag
197
+ rev = "#{from_tag}..#{to_tag}"
198
+ end
199
+
200
+ # Format: [short hash] [date] [commit message] ([author])
201
+ CommandProcessor.command("git log #{rev} --pretty=format:'%h %ad%x20%s%x20%x28%an%x29' --date=short").split("\n").reverse!
202
+ end
155
203
  end
156
204
  end
data/lib/printer.rb CHANGED
@@ -17,5 +17,25 @@ module Releasinator
17
17
  abort()
18
18
  end
19
19
  end
20
+
21
+ def self.ask(msg)
22
+ puts msg.yellow
23
+ $stdin.gets.chomp.downcase
24
+ end
25
+
26
+ def self.ask_binary(msg)
27
+ answer = self.ask("#{msg} (Y/n)")
28
+
29
+ while answer != "y" && answer != "n" do
30
+ puts "Please answer y or n".red
31
+ answer = self.ask("#{msg} (Y/n)")
32
+ end
33
+
34
+ return answer == "y"
35
+ end
36
+
37
+ def self.comment(msg)
38
+ puts msg.yellow
39
+ end
20
40
  end
21
41
  end
@@ -1,3 +1,3 @@
1
1
  module Releasinator
2
- VERSION = "0.6.4"
2
+ VERSION = "0.7.0"
3
3
  end
@@ -11,7 +11,8 @@ require_relative '../downstream'
11
11
  require_relative '../downstream_repo'
12
12
  require_relative '../publisher'
13
13
  require_relative '../validator'
14
- require_relative '../changelog/importer.rb'
14
+ require_relative '../changelog/importer'
15
+ require_relative '../changelog/updater'
15
16
 
16
17
  include Releasinator
17
18
 
@@ -28,7 +29,7 @@ end
28
29
 
29
30
  namespace :validate do
30
31
  desc "validate the presence, formatting, and semver sequence of CHANGELOG.md"
31
- task :changelog => :config do
32
+ task :changelog => [:config, :git] do
32
33
  @current_release = @validator.validate_changelog(DOWNSTREAM_REPOS)
33
34
  @current_release.freeze
34
35
  @downstream = Downstream.new(@releasinator_config, @validator, @current_release)
@@ -150,9 +151,63 @@ namespace :validate do
150
151
  end
151
152
  end
152
153
 
154
+ desc "Update release version and CHANGELOG"
155
+ task :update_version_and_changelog do
156
+ begin
157
+ Changelog::Updater.bump_version do |version, semver_type|
158
+ @releasinator_config[:update_version_method].call(version, semver_type)
159
+ Changelog::Updater.prompt_for_change_log(version, semver_type)
160
+
161
+ GitUtil.stage
162
+ if @releasinator_config.has_key? :update_version_commit_message
163
+ GitUtil.commit(@releasinator_config[:update_version_commit_message])
164
+ else
165
+ GitUtil.commit("Update version and CHANGELOG.md for #{version}")
166
+ end
167
+ end
168
+ rescue Exception => e
169
+ GitUtil.reset_head(true)
170
+ Printer.fail("Failed to update version: #{e}")
171
+ abort()
172
+ end
173
+ end
174
+
153
175
  desc "release all"
154
- task :release => [:"validate:all",:"local:build",:"pm:all",:"downstream:all",:"local:push",:"docs:all"] do
155
- Printer.success("Done releasing.")
176
+ task :release => [:"validate:all"] do
177
+ last_tag = GitUtil.tagged_versions(true).last
178
+
179
+ if !last_tag.nil? # If last tag is nil, at this point, there must be changelog entry, but this is the first releasinator release, proceed.
180
+ last_tag = Semantic::Version.new(last_tag)
181
+ commits_since_tag = GitUtil.commits(last_tag)
182
+ if commits_since_tag.size > 0 # There are new commits to be released
183
+ if @current_release.version > last_tag # CHANGELOG.md version is ahead of last tag. The releaser has already updated the changelog, and we've valdidated it
184
+ 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?")
185
+ Printer.fail("Update your version and CHANGELOG.md and re-run rake release.")
186
+ abort()
187
+ end
188
+ elsif @releasinator_config.has_key? :update_version_method
189
+ 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?")
190
+ Rake::Task[:update_version_and_changelog].invoke
191
+ Rake::Task[:"validate:changelog"].reenable
192
+ Rake::Task[:"validate:changelog"].invoke
193
+ else
194
+ Printer.fail("Update your version and CHANGELOG.md and re-run rake release.")
195
+ abort()
196
+ end
197
+ else
198
+ 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.")
199
+ abort()
200
+ end
201
+ elsif !Printer.ask_binary("There are no new commits since last tagged version '#{last_tag}'. Are you sure you want to release?")
202
+ abort()
203
+ end
204
+ end
205
+
206
+ [:"local:build",:"pm:all",:"downstream:all",:"local:push",:"docs:all"].each do |task|
207
+ Rake::Task[task].invoke
208
+ end
209
+
210
+ Printer.success("Done releasing #{@current_release.version}")
156
211
  end
157
212
 
158
213
  namespace :import do
@@ -348,9 +403,8 @@ namespace :docs do
348
403
  end
349
404
 
350
405
  def replace_string(filepath, string_to_replace, new_string)
351
- IO.write(filepath,
352
- File.open(filepath) do |file|
353
- file.read.gsub(string_to_replace, new_string)
354
- end
355
- )
406
+ text = File.read(filepath)
407
+ new_contents = text.gsub(string_to_replace, new_string)
408
+
409
+ File.open(filepath, "w") {|file| file.puts new_contents }
356
410
  end
data/releasinator.gemspec CHANGED
@@ -29,6 +29,7 @@ Gem::Specification.new do |spec|
29
29
  spec.add_development_dependency "bundler", "~> 1.11"
30
30
  spec.add_development_dependency "rake", "~> 11.1"
31
31
  spec.add_development_dependency "test-unit", "~> 3.1"
32
+ spec.add_development_dependency "mocha", "~> 1.3.0"
32
33
 
33
34
  spec.add_dependency "configatron", "~> 4.5"
34
35
  spec.add_dependency "colorize", "~> 0.7"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: releasinator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.4
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - PayPal
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-08-16 00:00:00.000000000 Z
11
+ date: 2017-08-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.1'
55
+ - !ruby/object:Gem::Dependency
56
+ name: mocha
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 1.3.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 1.3.0
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: configatron
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -153,6 +167,7 @@ files:
153
167
  - README.md
154
168
  - Rakefile
155
169
  - lib/changelog/importer.rb
170
+ - lib/changelog/updater.rb
156
171
  - lib/changelog/validator.rb
157
172
  - lib/command_processor.rb
158
173
  - lib/config_hash.rb