releasinator 0.2.1 → 0.3.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: d262a01d868cfb7a782dd4c1dca96e2879ccabab
4
- data.tar.gz: 2ba14042c8eccd4485b4c85fd4a8aa49e7eb0136
3
+ metadata.gz: 142b9a1c28929a601de19ffd0cbf8a9676c787b7
4
+ data.tar.gz: 2db14160b8162e371cb3490e972c8c0a7d2f3782
5
5
  SHA512:
6
- metadata.gz: b97d926007fb5f9f79cdd777c0720124063df22ec0bbcd5c359758e5a3e544b0792050e0f1e86c7d6d224fc3ec3be23501503a77171d939deee950f454ea33a1
7
- data.tar.gz: 38473a258a6207a7288ffe02eae724165990e3284ba3bd7bc4ba2a0f506ef22f8f16886b52dd85f65a9e36e2733803e8f5f083e5a65e818258d76958b7a337f9
6
+ metadata.gz: e8e8f8e57c25244bc78e3bdbe23df9c33e1c54c2f64f2139cce64aecb9a3697df99245c8ef06cf71429f9af35dc41fe81d6cfcc581156c8dc3b1f8ab1ef88805
7
+ data.tar.gz: ad2d9f1ed2ad1487443486446d3c85dc5cb368a64884aacf67779e8d4c51bb8e57f0f3c9ff1cf5a8722f47957a9c58955c94310c003d6d9a89a7993da51b6f68
data/.releasinator.rb CHANGED
@@ -15,14 +15,30 @@ def validate_version_match()
15
15
  Printer.success("Ruby gem spec version #{spec_version} matches latest changelog version.")
16
16
  end
17
17
 
18
+ def validate_ruby_gem_credentials()
19
+ file_path = Dir.home() + "/.gem/credentials"
20
+ if !File.exist?(file_path)
21
+ Printer.fail("Please create " + file_path.bold + " with the proper credentials.")
22
+ abort()
23
+ else
24
+ Printer.success(file_path.bold + " found.")
25
+ end
26
+ end
27
+
18
28
  configatron.custom_validation_methods = [
19
- method(:validate_version_match)
29
+ method(:validate_version_match),
30
+ method(:validate_ruby_gem_credentials)
20
31
  ]
21
32
 
22
33
 
23
34
  def build_method
24
35
  # run tests first
25
- CommandProcessor.command("ruby test/ts_allTests.rb")
36
+ # detect non-zero failures/errors, since ruby command won't exit properly if unit tests called exit/abort
37
+ output = CommandProcessor.command("ruby test/ts_allTests.rb | cat", live_output=true)
38
+ if !output.match /assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications$/
39
+ Printer.fail("There were unit test failures.")
40
+ abort()
41
+ end
26
42
 
27
43
  output_dir = "build"
28
44
  CommandProcessor.command("gem build releasinator.gemspec")
data/CHANGELOG.md CHANGED
@@ -1,6 +1,15 @@
1
1
  Releasinator release notes
2
2
  ==========================
3
3
 
4
+ 0.3.0
5
+ -----
6
+ * Add new `validate:releasinator_version` task.
7
+ * Add new `@validator.validate_in_path(executable)` function for use by any config.
8
+ * Fix version validation to be more permissive when a suffix is provided.
9
+ * Add missing dependencies on `validate:changelog`.
10
+ * Remove default rake task, so that releasing is explicit with the `release` task.
11
+ * Clean up and correct some file validations.
12
+ * Fix some bugs.
4
13
 
5
14
  0.2.1
6
15
  -----
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- releasinator (0.2.1)
4
+ releasinator (0.3.0)
5
5
  colorize (~> 0.7)
6
6
  configatron (~> 4.5)
7
7
  json (~> 1.8)
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Releasinator
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/releasinator.svg)](https://badge.fury.io/rb/releasinator)
4
+
3
5
  ## Problem
4
6
 
5
7
  When automating an SDK release process, many teams and languages have different ideas. The release process is a hurdle that makes it hard for new project members to ramp up. One shouldn't have to read a `release_process.md` to release an open source SDK.
@@ -21,8 +23,64 @@ The releasinator corrects this by enforcing standard must-have release files, be
21
23
 
22
24
  ### Usage
23
25
 
24
- 1. `cd` to directory of the repo you'd like to release
25
- 2. Run `rake -t -f <path to releasinator>/Rakefile`
26
+ 1. Add releasinator dependency to `Gemfile` or `.gemspec`
27
+ 2. add a `Rakefile` with the following contents:
28
+
29
+ ```ruby
30
+ spec = Gem::Specification.find_by_name 'releasinator'
31
+ load "#{spec.gem_dir}/lib/tasks/releasinator.rake"
32
+ ```
33
+
34
+ 3. Run `rake <command>` to use the newly added rake tasks
35
+
36
+ ### Tasks
37
+
38
+ ```
39
+ release
40
+
41
+ --> config
42
+
43
+ --> validate:all
44
+ --> validate:git_version
45
+ --> validate:gitignore
46
+ --> validate:git
47
+ --> validate:branch
48
+ --> validate:submodules
49
+ --> validate:readme
50
+ --> validate:changelog
51
+ --> validate:license
52
+ --> validate:contributing
53
+ --> validate:issue_template
54
+ --> validate:github_permissions_local
55
+ --> validate:github_permissions_downstream[downstream_repo_index]
56
+ --> validate:custom
57
+
58
+ --> local:build
59
+ --> local:checklist
60
+ --> local:confirm
61
+ --> local:prepare
62
+ --> local:tag
63
+
64
+ --> pm:all
65
+ --> pm:publish
66
+ --> pm:wait
67
+
68
+ --> downstream:all
69
+ --> downstream:reset[downstream_repo_index]
70
+ --> downstream:prepare[downstream_repo_index]
71
+ --> downstream:build[downstream_repo_index]
72
+ --> downstream:package[downstream_repo_index]
73
+ --> downstream:push[downstream_repo_index]
74
+
75
+ --> local:push
76
+
77
+ --> docs:all
78
+ --> docs:build
79
+ --> docs:package
80
+ --> docs:push
81
+
82
+ ```
83
+
26
84
 
27
85
  ## Conventions
28
86
 
@@ -60,10 +118,6 @@ The releasinator enforces certain conventions. If a filename doesn't exactly ma
60
118
  * Compiling with the right version of the platform.
61
119
  * Upstream library dependencies are the latest available.
62
120
 
63
- #### Package manager credential management
64
- 1. Validate permissions to publish to package manager
65
- 2. If permissions are needed, retrieve them from the secret credential repo, and run any subsequent repo steps.
66
-
67
121
  #### Performs internal-only tasks:
68
122
 
69
123
  1. ✓ Confirm user has completed all manual tasks (aka pre-release checklist).
@@ -102,6 +156,10 @@ The releasinator enforces certain conventions. If a filename doesn't exactly ma
102
156
  11. TODO add those same release notes within the release notes of the downstream repo.
103
157
  11. Assemble a draft of news, and publish (where possible, i.e. send email, tweet, whatever).
104
158
 
159
+ #### Package manager credential management (TODO)
160
+ 1. Validate permissions to publish to package manager
161
+ 2. If permissions are needed, retrieve them from the secret credential repo, and run any subsequent repo steps.
162
+
105
163
  ## Contributing
106
164
 
107
165
  Please read our [contributing guidelines](CONTRIBUTING.md) prior to submitting a Pull Request.
@@ -6,6 +6,7 @@ module Releasinator
6
6
  def self.command(command, live_output=false)
7
7
  puts Time.now.utc.iso8601 + ": " + "#{Dir.pwd}".bold + " exec:" + " #{command}".bold
8
8
  if live_output
9
+
9
10
  puts "...with live output (forked process)".bold
10
11
 
11
12
  return_code = nil
@@ -18,7 +19,11 @@ module Releasinator
18
19
  end
19
20
  end
20
21
  io.close
21
- r.each_line{|l| puts l.strip.white}
22
+ output = ""
23
+ r.each_line do |line|
24
+ puts line.strip.white
25
+ output << line
26
+ end
22
27
 
23
28
  Process.wait(pid)
24
29
  fork_exitstatus = $?.exitstatus
@@ -33,9 +38,8 @@ module Releasinator
33
38
  Printer.fail("Process failed with exitstatus:#{exitstatus}")
34
39
  abort()
35
40
  end
36
-
37
- output
38
41
  end
42
+ output
39
43
  end
40
44
 
41
45
  # waits for the input command to return non-empty output.
data/lib/config_hash.rb CHANGED
@@ -40,5 +40,11 @@ module Releasinator
40
40
 
41
41
  puts "loaded config:" + self.to_s if verbose
42
42
  end
43
+
44
+ def use_git_flow()
45
+ return self[:use_git_flow] if self.has_key? :use_git_flow
46
+ false
47
+ end
48
+
43
49
  end
44
50
  end
data/lib/downstream.rb ADDED
@@ -0,0 +1,201 @@
1
+ require_relative 'command_processor'
2
+ require_relative 'downstream_repo'
3
+ require_relative 'git_util'
4
+ require_relative 'printer'
5
+ require_relative 'publisher'
6
+
7
+ module Releasinator
8
+ class Downstream
9
+
10
+ def initialize(releasinator_config, validator, current_release)
11
+ @releasinator_config = releasinator_config
12
+ @validator = validator
13
+ @current_release = current_release
14
+ end
15
+
16
+ def validate_github_permissions(args)
17
+ if @releasinator_config.has_key?(:downstream_repos)
18
+ get_downstream_repos(args[:downstream_repo_index]).each do |downstream_repo, index|
19
+ @validator.validate_github_permissions(downstream_repo.url)
20
+ end
21
+ else
22
+ Printer.success("Not validating permissions of downstream repos. None found.")
23
+ end
24
+ end
25
+
26
+ def reset(args)
27
+ if @releasinator_config.has_key?(:downstream_repos)
28
+ get_downstream_repos(args[:downstream_repo_index]).each do |downstream_repo, index|
29
+ puts "resetting downstream_repo[#{index}]: #{downstream_repo.url}" if @releasinator_config[:verbose]
30
+ Dir.mkdir(DOWNSTREAM_REPOS) unless File.exist?(DOWNSTREAM_REPOS)
31
+ Dir.chdir(DOWNSTREAM_REPOS) do
32
+ CommandProcessor.command("git clone --origin origin #{downstream_repo.url} #{downstream_repo.name}") unless File.exist?(downstream_repo.name)
33
+
34
+ Dir.chdir(downstream_repo.name) do
35
+ GitUtil.reset_repo(downstream_repo.branch)
36
+
37
+ if downstream_repo.options.has_key? :new_branch_name
38
+ new_branch_name = get_new_branch_name(downstream_repo.options[:new_branch_name], @current_release.version)
39
+ GitUtil.delete_branch new_branch_name
40
+ end
41
+ end
42
+ end
43
+ end
44
+ Printer.success("Done resetting downstream repos.")
45
+ else
46
+ Printer.success("Not resetting downstream repos. None found.")
47
+ end
48
+ end
49
+
50
+ def prepare(args)
51
+ if @releasinator_config.has_key?(:downstream_repos)
52
+ get_downstream_repos(args[:downstream_repo_index]).each do |downstream_repo, index|
53
+ puts "preparing downstream_repo[#{index}]: #{downstream_repo.url}" if @releasinator_config[:verbose]
54
+
55
+ root_dir = Dir.pwd.strip
56
+ copy_from_dir = root_dir + "/" + get_base_dir()
57
+
58
+ Dir.chdir(DOWNSTREAM_REPOS) do
59
+ Dir.chdir(downstream_repo.name) do
60
+
61
+ if downstream_repo.options.has_key? :new_branch_name
62
+ new_branch_name = get_new_branch_name(downstream_repo.options[:new_branch_name], @current_release.version)
63
+ CommandProcessor.command("git checkout -b #{new_branch_name}")
64
+ end
65
+
66
+ if downstream_repo.full_file_sync
67
+ # remove old everything
68
+ CommandProcessor.command("rm -rf *")
69
+
70
+ # update all sdk files
71
+ CommandProcessor.command("rsync -av --exclude='#{DOWNSTREAM_REPOS}' --exclude='.git/' #{copy_from_dir}/* .")
72
+ CommandProcessor.command("rsync -av --exclude='#{DOWNSTREAM_REPOS}' --exclude='.git/' #{copy_from_dir}/.[!.]* .")
73
+ end
74
+
75
+ # copy custom files
76
+ if downstream_repo.options.has_key? :files_to_copy
77
+ downstream_repo.options[:files_to_copy].each do |copy_file|
78
+ copy_the_file(root_dir, copy_file, @current_release.version)
79
+ end
80
+ end
81
+
82
+ if downstream_repo.options.has_key? :post_copy_methods
83
+ downstream_repo.options[:post_copy_methods].each do |method|
84
+ method.call(@current_release.version)
85
+ end
86
+ end
87
+
88
+ if GitUtil.is_clean_git?
89
+ Printer.fail("Nothing changed in #{downstream_repo.name}!")
90
+ abort()
91
+ end
92
+ # add everything to git and commit
93
+ CommandProcessor.command("git add .")
94
+ CommandProcessor.command("git add -u .")
95
+ if downstream_repo.options.has_key? :new_branch_name
96
+ commit_message = "Update #{@releasinator_config[:product_name]} to #{@current_release.version}"
97
+ else
98
+ commit_message = "Release #{@current_release.version}"
99
+ end
100
+ CommandProcessor.command("git commit -am \"#{commit_message}\"")
101
+ end
102
+ end
103
+ end
104
+ Printer.success("Done preparing downstream repos.")
105
+ else
106
+ Printer.success("Not preparing downstream repos. None found.")
107
+ end
108
+ end
109
+
110
+ def build(args)
111
+ if @releasinator_config.has_key?(:downstream_repos)
112
+ get_downstream_repos(args[:downstream_repo_index]).each do |downstream_repo, index|
113
+ puts "building downstream_repo[#{index}]: #{downstream_repo.url}" if @releasinator_config[:verbose]
114
+ Dir.chdir(DOWNSTREAM_REPOS) do
115
+ Dir.chdir(downstream_repo.name) do
116
+ # build any files to verify release
117
+ if downstream_repo.options.has_key? :build_methods
118
+ downstream_repo.options[:build_methods].each do |method|
119
+ method.call
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
125
+ Printer.success("Done building downstream repos.")
126
+ else
127
+ Printer.success("Not building downstream repos. None found.")
128
+ end
129
+ end
130
+
131
+ def package(args)
132
+ if @releasinator_config.has_key?(:downstream_repos)
133
+ get_downstream_repos(args[:downstream_repo_index]).each do |downstream_repo, index|
134
+ puts "packaging downstream_repo[#{index}]: #{downstream_repo.url}" if @releasinator_config[:verbose]
135
+ Dir.chdir(DOWNSTREAM_REPOS) do
136
+ Dir.chdir(downstream_repo.name) do
137
+ # don't tag those where new branches are created
138
+ GitUtil.tag(@current_release.version, @current_release.changelog) unless downstream_repo.options.has_key? :new_branch_name
139
+ end
140
+ end
141
+ end
142
+ Printer.success("Done packaging downstream repos.")
143
+ else
144
+ Printer.success("Not packaging downstream repos. None found.")
145
+ end
146
+ end
147
+
148
+ def push(args)
149
+ if @releasinator_config.has_key?(:downstream_repos)
150
+ get_downstream_repos(args[:downstream_repo_index]).each do |downstream_repo, index|
151
+ puts "pushing downstream_repo[#{index}]: #{downstream_repo.url}" if @releasinator_config[:verbose]
152
+ Dir.chdir(DOWNSTREAM_REPOS) do
153
+ Dir.chdir(downstream_repo.name) do
154
+ if downstream_repo.options.has_key? :new_branch_name
155
+ new_branch_name = get_new_branch_name(downstream_repo.options[:new_branch_name], @current_release.version)
156
+ CommandProcessor.command("git push -u origin #{new_branch_name}")
157
+ Publisher.new(@releasinator_config).publish_pull_request(downstream_repo.url, @current_release, @releasinator_config[:product_name], downstream_repo.branch, new_branch_name)
158
+ else
159
+ GitUtil.push_branch("master")
160
+ GitUtil.push_tag(@current_release.version)
161
+ Publisher.new(@releasinator_config).publish_draft(downstream_repo.url, @current_release) unless ! downstream_repo.release_to_github
162
+ end
163
+ end
164
+ end
165
+ end
166
+ Printer.success("Done pushing downstream repos.")
167
+ else
168
+ Printer.success("Not pushing downstream repos. None found.")
169
+ end
170
+ end
171
+
172
+ private
173
+
174
+ def get_downstream_repos(downstream_repo_index)
175
+ repos_to_iterate_over = {}
176
+ if downstream_repo_index
177
+ index = Integer(downstream_repo_index) rescue false
178
+ if !index
179
+ Printer.fail("downstream_repo_index:#{downstream_repo_index} not a valid integer")
180
+ abort()
181
+ end
182
+ downstream_repo_max_index = @releasinator_config[:downstream_repos].size - 1
183
+ if index < 0
184
+ Printer.fail("Index out of bounds downstream_repo_index: #{index} < 0")
185
+ abort()
186
+ end
187
+ if index > downstream_repo_max_index
188
+ Printer.fail("Index out of bounds downstream_repo_index: #{index} >= #{downstream_repo_max_index}")
189
+ abort()
190
+ end
191
+ # keep original index for printing
192
+ repos_to_iterate_over[@releasinator_config[:downstream_repos][index]] = index
193
+ else
194
+ @releasinator_config[:downstream_repos].each_with_index do |downstream_repo, index|
195
+ repos_to_iterate_over[downstream_repo] = index
196
+ end
197
+ end
198
+ repos_to_iterate_over
199
+ end
200
+ end
201
+ end
@@ -14,5 +14,15 @@ module Releasinator
14
14
  # :post_copy_methods
15
15
  # :build_methods
16
16
  end
17
+
18
+ def full_file_sync
19
+ return @options[:full_file_sync] if @options.has_key? :full_file_sync
20
+ false
21
+ end
22
+
23
+ def release_to_github
24
+ return @options[:release_to_github] if @options.has_key? :release_to_github
25
+ false
26
+ end
17
27
  end
18
28
  end
data/lib/git_util.rb CHANGED
@@ -2,6 +2,30 @@ require_relative 'command_processor'
2
2
 
3
3
  module Releasinator
4
4
  class GitUtil
5
+ def self.reset_repo(branch_name)
6
+ # resets the repo to a clean state
7
+ checkout(branch_name)
8
+ fetch()
9
+ CommandProcessor.command("git reset --hard origin/#{branch_name}")
10
+ CommandProcessor.command("git clean -x -d -f")
11
+ end
12
+
13
+ def self.fetch()
14
+ CommandProcessor.command("git fetch origin --prune --recurse-submodules -j9")
15
+ end
16
+
17
+ def self.push_branch(branch_name)
18
+ checkout(branch_name)
19
+ fetch()
20
+ # always merge to include any extra commits added during release process
21
+ CommandProcessor.command("git merge origin/#{branch_name} --no-edit")
22
+ CommandProcessor.command("git push origin #{branch_name}")
23
+ end
24
+
25
+ def self.push_tag(tag_name)
26
+ CommandProcessor.command("git push origin #{tag_name}")
27
+ end
28
+
5
29
  def self.is_clean_git?
6
30
  any_changes = CommandProcessor.command("git status --porcelain")
7
31
  '' == any_changes
@@ -56,6 +80,15 @@ module Releasinator
56
80
  end
57
81
  end
58
82
 
83
+
84
+ def self.get_local_branch_sha1(branch_name)
85
+ CommandProcessor.command("git rev-parse --verify #{branch_name}").strip
86
+ end
87
+
88
+ def self.get_remote_branch_sha1(branch_name)
89
+ CommandProcessor.command("git rev-parse --verify origin/#{branch_name}").strip
90
+ end
91
+
59
92
  def self.tag(new_tag, changelog)
60
93
  confirm_tag_overwrite(new_tag)
61
94
  puts "tagging with changelog: \n\n#{changelog}\n".yellow
@@ -1,3 +1,3 @@
1
1
  module Releasinator
2
- VERSION = "0.2.1"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -7,6 +7,7 @@ require_relative '../command_processor'
7
7
  require_relative '../config_hash'
8
8
  require_relative '../copy_file'
9
9
  require_relative '../current_release'
10
+ require_relative '../downstream'
10
11
  require_relative '../downstream_repo'
11
12
  require_relative '../publisher'
12
13
  require_relative '../validator'
@@ -23,21 +24,6 @@ def get_base_dir
23
24
  end
24
25
  end
25
26
 
26
- def use_git_flow()
27
- return @releasinator_config[:use_git_flow] if @releasinator_config.has_key? :use_git_flow
28
- false
29
- end
30
-
31
- def full_file_sync(options)
32
- return options[:full_file_sync] if options.has_key? :full_file_sync
33
- false
34
- end
35
-
36
- def release_to_github(options)
37
- return options[:release_to_github] if options.has_key? :release_to_github
38
- false
39
- end
40
-
41
27
  desc "read and validate the config, adding one if not found"
42
28
  task :config do
43
29
  @releasinator_config = ConfigHash.new(verbose == true, Rake.application.options.trace == true)
@@ -46,8 +32,17 @@ task :config do
46
32
  end
47
33
 
48
34
  namespace :validate do
35
+ desc "validate the presence, formatting, and semver sequence of CHANGELOG.md"
36
+ task :changelog => :config do
37
+ @current_release = @validator.validate_changelog(get_base_dir(), DOWNSTREAM_REPOS)
38
+ @downstream = Downstream.new(@releasinator_config, @validator, @current_release)
39
+ end
49
40
 
50
41
  desc "validate that git is the correct version"
42
+ task :releasinator_version => :config do
43
+ @validator.validate_releasinator_version
44
+ end
45
+
51
46
  task :git_version => :config do
52
47
  @validator.validate_git_version
53
48
  end
@@ -59,46 +54,39 @@ namespace :validate do
59
54
 
60
55
  desc "validate current branch matches the latest on the server and follows naming conventions"
61
56
  task :branch => [:config, :changelog] do
62
- current_branch = GitUtil.get_current_branch()
63
- @validator.validate_matches_branch(current_branch)
64
- if use_git_flow()
65
- expected_release_branch = "release/#{@current_release.version}"
66
- abort("git flow expects the current branch to be either 'develop' or 'release/#{@current_release.version}'. Current branch is '#{current_branch}'".red) unless current_branch == expected_release_branch || current_branch == "develop"
67
- else
68
- abort("non-git flow expects releases to come from the master branch. Current branch is '#{current_branch}'".red) unless current_branch == "master"
69
- end
57
+ @validator.validate_branches(@current_release.version)
70
58
  end
71
59
 
72
- desc "validate the presence README.md, renaming a similar file if found"
60
+ desc "validate the presence of README.md, renaming a similar file if found"
73
61
  task :readme => :config do
74
- @validator.validate_exist(get_base_dir(), "README.md", DOWNSTREAM_REPOS)
62
+ @validator.validate_exist(".", "README.md", DOWNSTREAM_REPOS)
63
+ @validator.validate_exist(get_base_dir(), ".gitignore", DOWNSTREAM_REPOS) if '.' != get_base_dir()
75
64
  end
76
65
 
77
- desc "validate the presence LICENSE, renaming a similar file if found - also validates that its referenced from README.md"
66
+ desc "validate the presence of LICENSE, renaming a similar file if found - also validates that its referenced from README.md"
78
67
  task :license => :config do
79
68
  @validator.validate_exist(get_base_dir(), "LICENSE", DOWNSTREAM_REPOS)
80
69
  @validator.validate_referenced_in_readme(get_base_dir(), "LICENSE")
81
70
  end
82
71
 
83
- desc "validate the presence CONTRIBUTING.md, renaming a similar file if found - also validates that its referenced from README.md"
72
+ desc "validate the presence of CONTRIBUTING.md, renaming a similar file if found - also validates that its referenced from README.md"
84
73
  task :contributing => :config do
85
74
  @validator.validate_exist(get_base_dir(), "CONTRIBUTING.md", DOWNSTREAM_REPOS)
86
75
  @validator.validate_referenced_in_readme(get_base_dir(), "CONTRIBUTING.md")
87
76
  end
88
77
 
89
- desc "validate the presence .github/ISSUE_TEMPLATE.md"
78
+ desc "validate the presence of .github/ISSUE_TEMPLATE.md"
90
79
  task :issue_template => :config do
91
80
  @validator.validate_exist(get_base_dir(), ".github/ISSUE_TEMPLATE.md", DOWNSTREAM_REPOS)
92
81
  end
93
82
 
94
- desc "validate the presence, formatting, and semver sequence of CHANGELOG.md"
95
- task :changelog => :config do
96
- @current_release = @validator.validate_changelog(get_base_dir(), DOWNSTREAM_REPOS)
97
- end
98
-
99
- desc "validate the presence of .gitignore, adding it and any appropriate releasinator lines if necessary"
83
+ desc "validate the presence of .gitignore, adding any appropriate releasinator lines if necessary"
100
84
  task :gitignore => :config do
101
- @validator.validate_gitignore("#{DOWNSTREAM_REPOS}/", @releasinator_config.has_key?(:downstream_repos))
85
+ @validator.validate_exist('.', ".gitignore", DOWNSTREAM_REPOS)
86
+ @validator.validate_exist(get_base_dir(), ".gitignore", DOWNSTREAM_REPOS) if '.' != get_base_dir()
87
+ if @releasinator_config.has_key?(:downstream_repos)
88
+ @validator.validate_gitignore_contents("#{DOWNSTREAM_REPOS}/")
89
+ end
102
90
  end
103
91
 
104
92
  desc "validate all submodules are on the latest origin/master versions"
@@ -113,13 +101,7 @@ namespace :validate do
113
101
 
114
102
  desc "validate the current user can push to downstream repos"
115
103
  task :github_permissions_downstream, [:downstream_repo_index] => [:config] do |t, args|
116
- if @releasinator_config.has_key?(:downstream_repos)
117
- get_downstream_repos(args[:downstream_repo_index]).each do |downstream_repo, index|
118
- @validator.validate_github_permissions(downstream_repo.url)
119
- end
120
- else
121
- Printer.success("Not validating permissions of downstream repos. None found.")
122
- end
104
+ @downstream.validate_github_permissions(args)
123
105
  end
124
106
 
125
107
  desc "run any configatron.custom_validation_methods"
@@ -149,46 +131,45 @@ namespace :validate do
149
131
  :issue_template,
150
132
  :github_permissions_local,
151
133
  :github_permissions_downstream,
134
+ :releasinator_version,
152
135
  :custom
153
136
  ] do
154
137
  Printer.success("All validations passed.")
155
138
  end
156
139
  end
157
140
 
158
- task :default => :release
159
-
160
141
  desc "release all"
161
142
  task :release => [:"validate:all",:"local:build",:"pm:all",:"downstream:all",:"local:push",:"docs:all"] do
162
143
  Printer.success("Done releasing.")
163
144
  end
164
145
 
165
- desc "iterate over the prerelease_checklist_items, asking the user if each is done"
166
- task :prerelease_checklist => :config do
167
- @releasinator_config[:prerelease_checklist_items].each do |prerelease_item|
168
- Printer.check_proceed("#{prerelease_item}", "Then no release for you!")
169
- end
170
- end
171
-
172
146
  namespace :local do
173
147
  desc "ask user whether to proceed with release"
174
- task :confirm do
148
+ task :confirm => [:config, :"validate:changelog"] do
175
149
  Printer.check_proceed("You're about to release #{@current_release.version}!", "Then no release for you!")
176
150
  end
177
151
 
178
152
  desc "change branch for git flow, if using git flow"
179
153
  task :prepare => [:config, :"validate:changelog"] do
180
- if use_git_flow()
154
+ if @releasinator_config.use_git_flow()
181
155
  CommandProcessor.command("git checkout -b release/#{@current_release.version} develop") unless GitUtil.get_current_branch() != "develop"
182
156
  end
183
157
  end
184
158
 
185
159
  desc "tag the local repo"
186
- task :tag => :config do
160
+ task :tag => [:config, :"validate:changelog"] do
187
161
  GitUtil.tag(@current_release.version, @current_release.changelog)
188
162
  end
189
163
 
164
+ desc "iterate over the prerelease_checklist_items, asking the user if each is done"
165
+ task :checklist => [:config] do
166
+ @releasinator_config[:prerelease_checklist_items].each do |prerelease_item|
167
+ Printer.check_proceed("#{prerelease_item}", "Then no release for you!")
168
+ end
169
+ end
170
+
190
171
  desc "build the local repo"
191
- task :build => [:config, :"validate:changelog", :prerelease_checklist, :confirm, :prepare, :tag] do
172
+ task :build => [:config, :"validate:changelog", :checklist, :confirm, :prepare, :tag] do
192
173
  puts "building #{@current_release.version}" if @releasinator_config[:verbose]
193
174
  @releasinator_config[:build_method].call
194
175
  if @releasinator_config.has_key? :post_build_methods
@@ -199,17 +180,23 @@ namespace :local do
199
180
  end
200
181
 
201
182
  desc "run the git flow branch magic (if configured) and push local to remote"
202
- task :push => :config do
203
- if use_git_flow()
183
+ task :push => [:config, :"validate:changelog"] do
184
+ if @releasinator_config.use_git_flow()
204
185
  CommandProcessor.command("git checkout master")
205
- CommandProcessor.command("git pull")
206
186
  CommandProcessor.command("git merge --no-ff release/#{@current_release.version}")
207
187
  GitUtil.delete_branch "release/#{@current_release.version}"
208
- CommandProcessor.command("git checkout develop")
188
+ # still on master, so let's push it
189
+ end
190
+
191
+ GitUtil.push_branch("master")
192
+
193
+ if @releasinator_config.use_git_flow()
194
+ # switch back to develop to merge and continue development
195
+ GitUtil.checkout("develop")
209
196
  CommandProcessor.command("git merge master")
210
- CommandProcessor.command("git push origin develop --tags")
197
+ GitUtil.push_branch("develop")
211
198
  end
212
- CommandProcessor.command("git push origin master --tags")
199
+ GitUtil.push_tag(@current_release.version)
213
200
  if @releasinator_config[:release_to_github]
214
201
  Publisher.new(@releasinator_config).publish_draft(GitUtil.repo_url, @current_release)
215
202
  end
@@ -232,7 +219,7 @@ namespace :pm do
232
219
  end
233
220
 
234
221
  def copy_the_file(root_dir, copy_file, version=nil)
235
- Dir.mkdir(copy_file.target_dir) unless File.exists?(copy_file.target_dir)
222
+ Dir.mkdir(copy_file.target_dir) unless File.exist?(copy_file.target_dir)
236
223
  # use __VERSION__ to auto-substitute the version in any input param
237
224
  source_file_name = copy_file.source_file.gsub("__VERSION__", "#{version}")
238
225
  target_dir_name = copy_file.target_dir.gsub("__VERSION__", "#{version}")
@@ -244,41 +231,6 @@ def get_new_branch_name(new_branch_name, version)
244
231
  new_branch_name.gsub("__VERSION__", "#{version}")
245
232
  end
246
233
 
247
- def reset_repo(branch_name)
248
- # resets the repo to a clean state
249
- GitUtil.checkout(branch_name)
250
- CommandProcessor.command("git fetch origin --prune --recurse-submodules -j9")
251
- CommandProcessor.command("git reset --hard origin/#{branch_name}")
252
- CommandProcessor.command("git clean -x -d -f")
253
- end
254
-
255
- def get_downstream_repos(downstream_repo_index)
256
- repos_to_iterate_over = {}
257
- if downstream_repo_index
258
- index = Integer(downstream_repo_index) rescue false
259
- if !index
260
- Printer.fail("downstream_repo_index:#{downstream_repo_index} not a valid integer")
261
- abort()
262
- end
263
- downstream_repo_max_index = @releasinator_config[:downstream_repos].size - 1
264
- if index < 0
265
- Printer.fail("Index out of bounds downstream_repo_index: #{index} < 0")
266
- abort()
267
- end
268
- if index > downstream_repo_max_index
269
- Printer.fail("Index out of bounds downstream_repo_index: #{index} >= #{downstream_repo_max_index}")
270
- abort()
271
- end
272
- # keep original index for printing
273
- repos_to_iterate_over[@releasinator_config[:downstream_repos][index]] = index
274
- else
275
- @releasinator_config[:downstream_repos].each_with_index do |downstream_repo, index|
276
- repos_to_iterate_over[downstream_repo] = index
277
- end
278
- end
279
- repos_to_iterate_over
280
- end
281
-
282
234
  namespace :downstream do
283
235
  desc "build, package, and push all downstream repos"
284
236
  task :all => [:reset,:prepare,:build,:package,:push] do
@@ -287,152 +239,27 @@ namespace :downstream do
287
239
 
288
240
  desc "reset the downstream repos to their starting state"
289
241
  task :reset, [:downstream_repo_index] => [:config, :"validate:changelog"] do |t, args|
290
- if @releasinator_config.has_key?(:downstream_repos)
291
- get_downstream_repos(args[:downstream_repo_index]).each do |downstream_repo, index|
292
- puts "resetting downstream_repo[#{index}]: #{downstream_repo.url}" if @releasinator_config[:verbose]
293
- Dir.mkdir(DOWNSTREAM_REPOS) unless File.exists?(DOWNSTREAM_REPOS)
294
- Dir.chdir(DOWNSTREAM_REPOS) do
295
- CommandProcessor.command("git clone --origin origin #{downstream_repo.url} #{downstream_repo.name}") unless File.exists?(downstream_repo.name)
296
-
297
- Dir.chdir(downstream_repo.name) do
298
- reset_repo(downstream_repo.branch)
299
-
300
- if downstream_repo.options.has_key? :new_branch_name
301
- new_branch_name = get_new_branch_name(downstream_repo.options[:new_branch_name], @current_release.version)
302
- GitUtil.delete_branch new_branch_name
303
- end
304
- end
305
- end
306
- end
307
- Printer.success("Done resetting downstream repos.")
308
- else
309
- Printer.success("Not resetting downstream repos. None found.")
310
- end
242
+ @downstream.reset(args)
311
243
  end
312
244
 
313
245
  desc "prepare downstream release, copying files from base_docs_dir and any other configured files"
314
246
  task :prepare, [:downstream_repo_index] => [:config, :"validate:changelog", :reset] do |t, args|
315
- if @releasinator_config.has_key?(:downstream_repos)
316
- get_downstream_repos(args[:downstream_repo_index]).each do |downstream_repo, index|
317
- puts "preparing downstream_repo[#{index}]: #{downstream_repo.url}" if @releasinator_config[:verbose]
318
-
319
- root_dir = Dir.pwd.strip
320
- copy_from_dir = root_dir + "/" + get_base_dir()
321
-
322
- Dir.chdir(DOWNSTREAM_REPOS) do
323
- Dir.chdir(downstream_repo.name) do
324
-
325
- if downstream_repo.options.has_key? :new_branch_name
326
- new_branch_name = get_new_branch_name(downstream_repo.options[:new_branch_name], @current_release.version)
327
- CommandProcessor.command("git checkout -b #{new_branch_name}")
328
- end
329
-
330
- if full_file_sync(downstream_repo.options)
331
- # remove old everything
332
- CommandProcessor.command("rm -rf *")
333
-
334
- # update all sdk files
335
- CommandProcessor.command("rsync -av --exclude='#{DOWNSTREAM_REPOS}' --exclude='.git/' #{copy_from_dir}/* .")
336
- CommandProcessor.command("rsync -av --exclude='#{DOWNSTREAM_REPOS}' --exclude='.git/' #{copy_from_dir}/.[!.]* .")
337
- end
338
-
339
- # copy custom files
340
- if downstream_repo.options.has_key? :files_to_copy
341
- downstream_repo.options[:files_to_copy].each do |copy_file|
342
- copy_the_file(root_dir, copy_file, @current_release.version)
343
- end
344
- end
345
-
346
- if downstream_repo.options.has_key? :post_copy_methods
347
- downstream_repo.options[:post_copy_methods].each do |method|
348
- method.call(@current_release.version)
349
- end
350
- end
351
-
352
- if GitUtil.is_clean_git?
353
- Printer.fail("Nothing changed in #{downstream_repo.name}!")
354
- abort()
355
- end
356
- # add everything to git and commit
357
- CommandProcessor.command("git add .")
358
- CommandProcessor.command("git add -u .")
359
- if downstream_repo.options.has_key? :new_branch_name
360
- commit_message = "Update #{@releasinator_config[:product_name]} to #{@current_release.version}"
361
- else
362
- commit_message = "Release #{@current_release.version}"
363
- end
364
- CommandProcessor.command("git commit -am \"#{commit_message}\"")
365
- end
366
- end
367
- end
368
- Printer.success("Done preparing downstream repos.")
369
- else
370
- Printer.success("Not preparing downstream repos. None found.")
371
- end
247
+ @downstream.prepare(args)
372
248
  end
373
249
 
374
250
  desc "call all build_methods for each downstream repo"
375
- task :build, [:downstream_repo_index] => :config do |t, args|
376
- if @releasinator_config.has_key?(:downstream_repos)
377
- get_downstream_repos(args[:downstream_repo_index]).each do |downstream_repo, index|
378
- puts "building downstream_repo[#{index}]: #{downstream_repo.url}" if @releasinator_config[:verbose]
379
- Dir.chdir(DOWNSTREAM_REPOS) do
380
- Dir.chdir(downstream_repo.name) do
381
- # build any files to verify release
382
- if downstream_repo.options.has_key? :build_methods
383
- downstream_repo.options[:build_methods].each do |method|
384
- method.call
385
- end
386
- end
387
- end
388
- end
389
- end
390
- Printer.success("Done building downstream repos.")
391
- else
392
- Printer.success("Not building downstream repos. None found.")
393
- end
251
+ task :build, [:downstream_repo_index] => [:config,:"validate:changelog"] do |t, args|
252
+ @downstream.build(args)
394
253
  end
395
254
 
396
255
  desc "tag all non-branch downstream repos"
397
256
  task :package, [:downstream_repo_index] => [:config,:"validate:changelog"] do |t, args|
398
- if @releasinator_config.has_key?(:downstream_repos)
399
- get_downstream_repos(args[:downstream_repo_index]).each do |downstream_repo, index|
400
- puts "packaging downstream_repo[#{index}]: #{downstream_repo.url}" if @releasinator_config[:verbose]
401
- Dir.chdir(DOWNSTREAM_REPOS) do
402
- Dir.chdir(downstream_repo.name) do
403
- # don't tag those where new branches are created
404
- GitUtil.tag(@current_release.version, @current_release.changelog) unless downstream_repo.options.has_key? :new_branch_name
405
- end
406
- end
407
- end
408
- Printer.success("Done packaging downstream repos.")
409
- else
410
- Printer.success("Not packaging downstream repos. None found.")
411
- end
257
+ @downstream.package(args)
412
258
  end
413
259
 
414
260
  desc "push tags and creates draft release, or pushes branch and creates pull request, depending on the presence of new_branch_name"
415
261
  task :push, [:downstream_repo_index] => [:config,:"validate:changelog"] do |t, args|
416
- if @releasinator_config.has_key?(:downstream_repos)
417
- get_downstream_repos(args[:downstream_repo_index]).each do |downstream_repo, index|
418
- puts "pushing downstream_repo[#{index}]: #{downstream_repo.url}" if @releasinator_config[:verbose]
419
- Dir.chdir(DOWNSTREAM_REPOS) do
420
- Dir.chdir(downstream_repo.name) do
421
- if downstream_repo.options.has_key? :new_branch_name
422
- new_branch_name = get_new_branch_name(downstream_repo.options[:new_branch_name], @current_release.version)
423
- CommandProcessor.command("git push -u origin #{new_branch_name}")
424
- Publisher.new(@releasinator_config).publish_pull_request(downstream_repo.url, @current_release, @releasinator_config[:product_name], downstream_repo.branch, new_branch_name)
425
- else
426
- CommandProcessor.command("git push origin master --tags")
427
- Publisher.new(@releasinator_config).publish_draft(downstream_repo.url, @current_release) unless ! release_to_github(downstream_repo.options)
428
- end
429
- end
430
- end
431
- end
432
- Printer.success("Done pushing downstream repos.")
433
- else
434
- Printer.success("Not pushing downstream repos. None found.")
435
- end
262
+ @downstream.push(args)
436
263
  end
437
264
  end
438
265
 
@@ -462,7 +289,7 @@ namespace :docs do
462
289
  current_branch = GitUtil.get_current_branch()
463
290
 
464
291
  GitUtil.init_gh_pages()
465
- reset_repo("gh-pages")
292
+ GitUtil.reset_repo("gh-pages")
466
293
  @releasinator_config[:doc_files_to_copy].each do |copy_file|
467
294
  copy_the_file(root_dir, copy_file)
468
295
  end
@@ -488,7 +315,7 @@ namespace :docs do
488
315
  Dir.chdir(doc_target_dir) do
489
316
  current_branch = GitUtil.get_current_branch()
490
317
  CommandProcessor.command("git checkout gh-pages")
491
- CommandProcessor.command("git push origin gh-pages")
318
+ GitUtil.push_branch("gh-pages")
492
319
  # switch back to previous branch
493
320
  CommandProcessor.command("git checkout #{current_branch}")
494
321
  end
data/lib/validator.rb CHANGED
@@ -1,10 +1,13 @@
1
- require 'colorize'
1
+ require 'colorize'
2
2
  require 'fileutils'
3
+ require 'net/http'
4
+ require 'json'
3
5
  require_relative 'command_processor'
4
6
  require_relative 'downstream_repo'
5
7
  require_relative 'github_repo'
6
8
  require_relative 'printer'
7
9
  require_relative 'validator_changelog'
10
+ require_relative 'releasinator/version'
8
11
 
9
12
  module Releasinator
10
13
  class Validator
@@ -13,13 +16,32 @@ module Releasinator
13
16
  @releasinator_config = releasinator_config
14
17
  end
15
18
 
16
- def get_changelog_contents(base_dir)
17
- Dir.chdir(base_dir) do
18
- open('CHANGELOG.md').read
19
+ def validate_releasinator_version
20
+ uri = URI('https://rubygems.org/api/v1/gems/releasinator.json')
21
+ latest_version = JSON.parse(Net::HTTP.get(uri))["version"]
22
+ current_version = Releasinator::VERSION
23
+
24
+ if Gem::Version.new(latest_version) > Gem::Version.new(current_version)
25
+ Printer.fail("Please upgrade to the latest releasinator version:" + latest_version.bold + ". Current version:" + current_version.bold)
26
+ abort()
27
+ elsif Gem::Version.new(latest_version) < Gem::Version.new(current_version)
28
+ Printer.success("Releasinator version: " + current_version.bold + " is newer than one from rubygems.org: " + latest_version.bold + ". You're probably testing a development version.")
29
+ else
30
+ Printer.success("Releasinator version: " + latest_version.bold + " is the latest from rubygems.org.")
31
+ end
32
+ end
33
+
34
+ def validate_in_path(executable)
35
+ if "" == CommandProcessor.command("which #{executable} | cat")
36
+ Printer.fail(executable.bold + " not found on path.")
37
+ abort()
38
+ else
39
+ Printer.success(executable.bold + " found on path.")
19
40
  end
20
41
  end
21
42
 
22
43
  def validate_git_version
44
+ validate_in_path("git")
23
45
  version_output = CommandProcessor.command("git version")
24
46
  # version where the parallel git fetch features were added
25
47
  expected_git_version = "2.8.0"
@@ -34,10 +56,11 @@ module Releasinator
34
56
  end
35
57
 
36
58
  def validate_changelog(base_dir, downstream_dir)
59
+ validate_base_dir base_dir
37
60
  validate_exist(base_dir, "CHANGELOG.md", downstream_dir, ["release_notes.md"])
38
61
 
39
62
  changelog_contents = get_changelog_contents(base_dir)
40
- ValidatorChangelog.new.validate_changelog_contents(changelog_contents)
63
+ ValidatorChangelog.new(@releasinator_config).validate_changelog_contents(changelog_contents)
41
64
  end
42
65
 
43
66
  def validate_is_type(obj, type)
@@ -114,37 +137,22 @@ module Releasinator
114
137
  end
115
138
  end
116
139
 
117
- def validate_gitignore(line, is_downstream_present)
118
- if !File.exist?(".gitignore")
119
- is_git_already_clean = GitUtil.new().is_clean_git?
120
- FileUtils.touch('.gitignore')
140
+ def validate_gitignore_contents(line)
141
+ if !line_match_in_file?(line, ".gitignore")
142
+ is_git_already_clean = GitUtil.is_clean_git?
143
+ File.open('.gitignore', 'a') do |f|
144
+ f.puts "# #{@releasinator_config[:releasinator_name]}"
145
+ f.puts line
146
+ end
147
+
121
148
  if is_git_already_clean
122
- CommandProcessor.command("git add . && git commit -m \"#{@releasinator_config[:releasinator_name]}: add .gitignore\"")
123
- Printer.success("Added .gitignore file.")
149
+ CommandProcessor.command("git add . && git commit -m \"#{@releasinator_config[:releasinator_name]}: add downstream dir to .gitignore\"")
150
+ Printer.success("Added downstream dir to .gitignore file.")
124
151
  else
125
- Printer.fail("Added .gitignore, but there are other changes in the workspace. Please commit and continue.")
152
+ Printer.fail("Added downstream dir to .gitignore file, but there are other changes in the workspace. Please commit and continue.")
126
153
  abort()
127
154
  end
128
155
  end
129
- Printer.success(".gitignore found.")
130
-
131
- if is_downstream_present
132
- if !line_match_in_file?(line, ".gitignore")
133
- is_git_already_clean = GitUtil.new().is_clean_git?
134
- File.open('.gitignore', 'a') do |f|
135
- f.puts "# #{@releasinator_config[:releasinator_name]}"
136
- f.puts line
137
- end
138
-
139
- if is_git_already_clean
140
- CommandProcessor.command("git add . && git commit -m \"#{@releasinator_config[:releasinator_name]}: add downstream dir to .gitignore\"")
141
- Printer.success("Added downstream dir to .gitignore file.")
142
- else
143
- Printer.fail("Added downstream dir to .gitignore file, but there are other changes in the workspace. Please commit and continue.")
144
- abort()
145
- end
146
- end
147
- end
148
156
  end
149
157
 
150
158
  def line_match_in_file?(contains_string, filename)
@@ -160,6 +168,7 @@ module Releasinator
160
168
  end
161
169
 
162
170
  def validate_referenced_in_readme(base_dir, filename)
171
+ validate_base_dir base_dir
163
172
  Dir.chdir(base_dir) do
164
173
  File.open("README.md", "r") do |f|
165
174
  f.each_line do |line|
@@ -175,6 +184,7 @@ module Releasinator
175
184
  end
176
185
 
177
186
  def validate_exist(base_dir, expected_file_name, downstream_dir, alternate_names=[])
187
+ validate_base_dir base_dir
178
188
  Dir.chdir(base_dir) do
179
189
  if !File.exist?(expected_file_name)
180
190
  puts "#{base_dir}/#{expected_file_name} not found. Searching for similar files.".yellow
@@ -199,6 +209,13 @@ module Releasinator
199
209
  end
200
210
  end
201
211
 
212
+ def validate_base_dir(base_dir)
213
+ if !File.exist? base_dir
214
+ Printer.fail("Directory specified by base_docs_dir '#{base_dir}' not found. Please fix the config, or add this directory.")
215
+ abort()
216
+ end
217
+ end
218
+
202
219
  def validate_clean_git
203
220
  untracked_files = GitUtil.untracked_files
204
221
  diff = GitUtil.diff
@@ -231,15 +248,6 @@ module Releasinator
231
248
  abort() if error
232
249
  end
233
250
 
234
- class Submodule
235
- attr_reader :name, :path, :url
236
-
237
- def initialize(name, path, url)
238
- @name=name
239
- @path=path
240
- @url=url
241
- end
242
- end
243
251
 
244
252
  def validate_submodules
245
253
  if File.exist?(".gitmodules")
@@ -265,7 +273,8 @@ module Releasinator
265
273
  Printer.success("Found " + submodules.count.to_s.bold + " submodules in .gitmodules.")
266
274
  submodules.each do |submodule|
267
275
  Dir.chdir(submodule.path) do
268
- validate_matches_branch("master", "Submodule")
276
+ validate_local_matches_remote("master")
277
+ validate_clean_git()
269
278
  end
270
279
  end
271
280
  else
@@ -273,24 +282,37 @@ module Releasinator
273
282
  end
274
283
  end
275
284
 
276
- def validate_matches_branch(branch_name, console_prefix="Root")
277
- current_dir = Dir.pwd
285
+ def validate_branches(version)
286
+ GitUtil.fetch()
287
+ current_branch = GitUtil.get_current_branch()
278
288
 
279
- # Don't fetch the submodules, as they should already be fetched by the initial recursive fetch.
280
- if console_prefix == "Root"
281
- puts "fetching #{current_dir}" if @releasinator_config[:verbose]
289
+ if @releasinator_config.use_git_flow()
290
+ expected_release_branch = "release/#{version}"
291
+
292
+ unless current_branch == expected_release_branch || current_branch == "develop"
293
+ Printer.fail("git flow expects the current branch to be either 'develop' or 'release/#{version}'. Current branch is '#{current_branch}'")
294
+ abort()
295
+ end
282
296
 
283
- # silently fails if it can't connect because sometimes we want to release even if
284
- # corp GitHub is down.
285
- `git fetch --recurse-submodules -j9`
297
+ # validate that master is up to date, because git flow requires this.
298
+ validate_local_matches_remote("master")
299
+ else
300
+ unless current_branch == "master"
301
+ Printer.fail("non-git flow expects releases to come from the master branch. Current branch is '#{current_branch}'")
302
+ abort()
303
+ end
286
304
  end
287
305
 
288
- validate_clean_git()
306
+ validate_local_matches_remote(current_branch)
307
+ end
289
308
 
290
- head_sha1 = `git rev-parse --verify head`.strip
291
- origin_branch_sha1 = `git rev-parse --verify origin/#{branch_name}`.strip
292
- if head_sha1 != origin_branch_sha1
293
- abort_string = "#{console_prefix} #{current_dir} at #{head_sha1}, but origin/#{branch_name} is #{origin_branch_sha1}."\
309
+ private
310
+
311
+ def validate_local_matches_remote(branch_name)
312
+ local_branch_sha1 = GitUtil.get_local_branch_sha1(branch_name)
313
+ origin_branch_sha1 = GitUtil.get_remote_branch_sha1(branch_name)
314
+ if local_branch_sha1 != origin_branch_sha1
315
+ abort_string = "Branches not in sync: #{Dir.pwd} at #{local_branch_sha1}, but origin/#{branch_name} is #{origin_branch_sha1}."\
294
316
  "\nIf you received this error on the root project, you may need to:"\
295
317
  "\n 1. pull the latest changes from the remote,"\
296
318
  "\n 2. push changes up to the remote,"\
@@ -298,7 +320,16 @@ module Releasinator
298
320
  Printer.fail(abort_string)
299
321
  abort()
300
322
  else
301
- Printer.success("#{console_prefix} #{current_dir} matches origin/#{branch_name}.")
323
+ Printer.success("Repo #{Dir.pwd} matches origin/#{branch_name}.")
324
+ end
325
+ end
326
+
327
+ class Submodule
328
+ attr_reader :name, :path, :url
329
+ def initialize(name, path, url)
330
+ @name=name
331
+ @path=path
332
+ @url=url
302
333
  end
303
334
  end
304
335
 
@@ -320,5 +351,12 @@ module Releasinator
320
351
  end
321
352
  false
322
353
  end
354
+
355
+ def get_changelog_contents(base_dir)
356
+ validate_base_dir base_dir
357
+ Dir.chdir(base_dir) do
358
+ open('CHANGELOG.md').read
359
+ end
360
+ end
323
361
  end
324
362
  end
@@ -7,32 +7,51 @@ require_relative 'printer'
7
7
  module Releasinator
8
8
  class ValidatorChangelog
9
9
 
10
- def validate_semver(changelog_hash, prefix="")
10
+ RELEASE_REGEX = /\d+\.\d+\.\d+/
11
+
12
+ def initialize(releasinator_config)
13
+ @releasinator_config = releasinator_config
14
+ end
15
+
16
+ # assume changelog_hash is not empty
17
+ def validate_semver(changelog_hash)
18
+ latest_release, latest_release_changelog = changelog_hash.first
19
+ # extract prefix from first release to use on all subsequent releases
20
+ latest_release_prefix = latest_release.partition(RELEASE_REGEX)[0]
21
+
11
22
  newer_version = nil
12
23
  changelog_hash.each do |key,value|
13
- if !key.start_with? prefix
14
- Printer.fail("version #{key} does not start with prefix '#{prefix}'.")
24
+ prefix, version, suffix = key.partition(RELEASE_REGEX)
25
+ puts "Checking version with prefix:'#{prefix}', version:'#{version}', suffix:'#{suffix}'." if @releasinator_config[:verbose]
26
+ if prefix != latest_release_prefix
27
+ Printer.fail("version #{key} does not start with extracted prefix '#{latest_release_prefix}'.")
15
28
  abort()
16
29
  end
17
- older_version = Semantic::Version.new key[prefix.length..-1]
30
+ older_version = Semantic::Version.new version
18
31
 
19
32
  if nil != newer_version
20
33
  version_comp = newer_version <=> older_version
21
- if version_comp < 1
34
+ if version_comp < 0
22
35
  Printer.fail("Semver releases out of order: #{older_version} should be smaller than #{newer_version}")
23
36
  abort()
24
- end
25
-
26
- error_suffix = "version increment error - comparing #{newer_version} to #{older_version} does not pass semver validation."
27
- # validate the next sequence in semver
28
- if newer_version.major == older_version.major
29
- if newer_version.minor == older_version.minor
30
- check_semver_criteria(newer_version.patch == older_version.patch + 1, "patch #{error_suffix}")
31
- else
32
- check_semver_criteria(newer_version.minor == older_version.minor + 1 && newer_version.patch == 0, "minor #{error_suffix}")
37
+ elsif version_comp == 0
38
+ # this case cannot be found with Vandamme library because 2 versions in a row get overwritten in the underlying hash
39
+ if suffix.empty?
40
+ Printer.fail("2 semver releases in a row without a suffix (like -beta, -rc1, etc...) is not allowed.")
41
+ abort()
33
42
  end
34
43
  else
35
- check_semver_criteria(newer_version.major == older_version.major + 1 && newer_version.minor == 0 && newer_version.patch == 0, "major #{error_suffix}")
44
+ error_suffix = "version increment error - comparing #{newer_version} to #{older_version} does not pass semver validation."
45
+ # validate the next sequence in semver
46
+ if newer_version.major == older_version.major
47
+ if newer_version.minor == older_version.minor
48
+ check_semver_criteria(newer_version.patch == older_version.patch + 1, "patch #{error_suffix}")
49
+ else
50
+ check_semver_criteria(newer_version.minor == older_version.minor + 1 && newer_version.patch == 0, "minor #{error_suffix}")
51
+ end
52
+ else
53
+ check_semver_criteria(newer_version.major == older_version.major + 1 && newer_version.minor == 0 && newer_version.patch == 0, "major #{error_suffix}")
54
+ end
36
55
  end
37
56
  end
38
57
  newer_version = older_version
@@ -46,13 +65,13 @@ module Releasinator
46
65
  end
47
66
  end
48
67
 
49
- def validate_changelog_contents(changelog_contents, prefix="")
68
+ def validate_changelog_contents(changelog_contents)
50
69
  version_header_regexes = [
51
70
  ## h2 using --- separator. Example:
52
71
  # 1.0.0
53
72
  # -----
54
73
  # First release!
55
- '(^\d+\.\d+\.\d+).*\n----.*',
74
+ '(^#{RELEASE_REGEX}).*\n----.*',
56
75
 
57
76
  # h1/h2 header retrieved from https://github.com/tech-angels/vandamme/#format
58
77
  '^#{0,3} ?([\w\d\.-]+\.[\w\d\.-]+[a-zA-Z0-9])(?: \/ (\w+ \d{1,2}(?:st|nd|rd|th)?,\s\d{4}|\d{4}-\d{2}-\d{2}|\w+))?\n?[=-]*'
@@ -73,7 +92,7 @@ module Releasinator
73
92
 
74
93
  Printer.success("Found " + changelog_hash.count.to_s.bold + " release(s) in CHANGELOG.md.")
75
94
 
76
- validate_semver(changelog_hash, prefix)
95
+ validate_semver(changelog_hash)
77
96
 
78
97
  latest_release, latest_release_changelog = changelog_hash.first
79
98
  CurrentRelease.new(latest_release, latest_release_changelog)
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.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - PayPal
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-04-27 00:00:00.000000000 Z
11
+ date: 2016-05-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -158,6 +158,7 @@ files:
158
158
  - lib/copy_file.rb
159
159
  - lib/current_release.rb
160
160
  - lib/default_config.rb
161
+ - lib/downstream.rb
161
162
  - lib/downstream_repo.rb
162
163
  - lib/git_util.rb
163
164
  - lib/github_repo.rb