releasinator 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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