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 +4 -4
- data/.releasinator.rb +13 -1
- data/CHANGELOG.md +4 -0
- data/README.md +17 -0
- data/lib/changelog/updater.rb +101 -0
- data/lib/default_config.rb +12 -0
- data/lib/git_util.rb +48 -0
- data/lib/printer.rb +20 -0
- data/lib/releasinator/version.rb +1 -1
- data/lib/tasks/releasinator.rake +63 -9
- data/releasinator.gemspec +1 -0
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ae6a23ab3be34c267540720c00929120c025ee98
|
4
|
+
data.tar.gz: 8a2dd6a07ea6d57d9921058a667e29e9dec14ece
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
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
|
data/lib/default_config.rb
CHANGED
@@ -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
|
data/lib/releasinator/version.rb
CHANGED
data/lib/tasks/releasinator.rake
CHANGED
@@ -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
|
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"
|
155
|
-
|
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
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
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.
|
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-
|
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
|