metasploit-version 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +2 -0
  3. data/.travis.yml +10 -3
  4. data/CHANGELOG.md +6 -0
  5. data/CONTRIBUTING.md +10 -44
  6. data/Gemfile +12 -8
  7. data/RELEASING.md +86 -0
  8. data/Rakefile +5 -4
  9. data/UPGRADING.md +1 -0
  10. data/app/templates/.rspec.tt +2 -0
  11. data/app/templates/CHANGELOG.md.tt +18 -0
  12. data/app/templates/CONTRIBUTING.md.tt +122 -0
  13. data/app/templates/RELEASING.md.tt +99 -0
  14. data/app/templates/Rakefile.tt +7 -0
  15. data/app/templates/UPGRADING.md.tt +1 -0
  16. data/app/templates/lib/versioned/version.rb.tt +64 -0
  17. data/app/templates/spec/lib/versioned/version_spec.rb.tt +3 -0
  18. data/app/templates/spec/lib/versioned_spec.rb.tt +4 -0
  19. data/app/templates/spec/spec_helper.rb.tt +76 -0
  20. data/bin/metasploit-version +12 -0
  21. data/config/cucumber.yml +3 -0
  22. data/features/metasploit-version/install/add_development_dependency.feature +68 -0
  23. data/features/metasploit-version/install/bundle_install.feature +24 -0
  24. data/features/metasploit-version/install/changelog.feature +29 -0
  25. data/features/metasploit-version/install/conflict.feature +45 -0
  26. data/features/metasploit-version/install/contributing.feature +139 -0
  27. data/features/metasploit-version/install/namespace_spec/gem_version_constant.feature +154 -0
  28. data/features/metasploit-version/install/namespace_spec/namespace.feature +33 -0
  29. data/features/metasploit-version/install/namespace_spec/version_constant.feature +179 -0
  30. data/features/metasploit-version/install/rake_spec.feature +27 -0
  31. data/features/metasploit-version/install/releasing/ruby_versions.feature +50 -0
  32. data/features/metasploit-version/install/releasing/versioning.feature +138 -0
  33. data/features/metasploit-version/install/steps/gemspec_steps.rb +19 -0
  34. data/features/metasploit-version/install/upgrading.feature +19 -0
  35. data/features/metasploit-version/install/version.feature +11 -0
  36. data/features/metasploit-version/install/version/namespace.feature +157 -0
  37. data/features/metasploit-version/install/version/prerelease.feature +154 -0
  38. data/features/metasploit-version/install/version_spec/namespace.feature +32 -0
  39. data/features/metasploit-version/install/version_spec/testing/branch_from_master.feature +40 -0
  40. data/features/metasploit-version/install/version_spec/testing/branching_from_branch.feature +161 -0
  41. data/features/metasploit-version/install/version_spec/testing/merge_branch_to_master.feature +97 -0
  42. data/features/{shared/examples/metasploit/version/version_module/prerelease/git/step_definitions → step_definitions}/environment_variable_steps.rb +1 -1
  43. data/features/{shared/examples/metasploit/version/version_module/prerelease/git/step_definitions → step_definitions}/git_steps.rb +11 -3
  44. data/features/support/env.rb +15 -11
  45. data/lib/metasploit/version.rb +1 -0
  46. data/lib/metasploit/version/cli.rb +321 -0
  47. data/lib/metasploit/version/version.rb +2 -2
  48. data/metasploit-version.gemspec +2 -2
  49. data/spec/lib/metasploit/version/branch_spec.rb +1 -3
  50. data/spec/lib/metasploit/version/cli_spec.rb +514 -0
  51. data/spec/lib/metasploit/version/version_spec.rb +2 -4
  52. data/spec/lib/metasploit/version_spec.rb +2 -4
  53. data/spec/spec_helper.rb +63 -1
  54. data/spec/support/shared/examples/metasploit/version/version_module.rb +3 -1
  55. metadata +82 -8
@@ -0,0 +1,97 @@
1
+ Feature: metasploit-version install's 'version_spec.rb' catches errors in version.rb when merging branch to master
2
+
3
+ The version_spec.rb file from metasploit-version will catch errors if the user fails to update the PRERELEASE
4
+ constant in version.rb to when going from a branch to master.
5
+
6
+ Background:
7
+ Given I successfully run `bundle gem branched`
8
+ And I cd to "branched"
9
+ And my git identity is configured
10
+ And I successfully run `git commit --message "bundle gem branched"`
11
+ And I successfully run `git checkout -b feature/MSP-1234/metasploit-version`
12
+ And I unset the environment variable "TRAVIS_BRANCH"
13
+ And I set the environment variables to:
14
+ | variable | value |
15
+ | TRAVIS_PULL_REQUEST | false |
16
+ And I successfully run `metasploit-version install --force --no-bundle-install`
17
+ And I successfully run `rake spec`
18
+ And I successfully run `git add *`
19
+ And I successfully run `git commit --all --message "metasploit-version install"`
20
+ And I successfully run `git checkout master`
21
+ And I successfully run `git merge feature/MSP-1234/metasploit-version`
22
+
23
+ Scenario: Merging branch to master without removing PRERELEASE
24
+ Given the file "lib/branched/version.rb" should contain:
25
+ """
26
+ # The prerelease version, scoped to the {MAJOR}, {MINOR}, and {PATCH} version numbers.
27
+ PRERELEASE = 'metasploit-version'
28
+ """
29
+ When I run `rake spec`
30
+ Then the exit status should not be 0
31
+ And the output should contain:
32
+ """
33
+ expected Branched::Version::PRERELEASE not to be defined on master
34
+ """
35
+ And the output should contain " 1 failure"
36
+
37
+ Scenario: Merging branch to master with removing PRERELEASE
38
+ Given I overwrite "lib/branched/version.rb" with:
39
+ """
40
+ module Branched
41
+ # Holds components of {VERSION} as defined by {http://semver.org/spec/v2.0.0.html semantic versioning v2.0.0}.
42
+ module Version
43
+ #
44
+ # CONSTANTS
45
+ #
46
+
47
+ # The major version number.
48
+ MAJOR = 0
49
+ # The minor version number, scoped to the {MAJOR} version number.
50
+ MINOR = 0
51
+ # The patch version number, scoped to the {MAJOR} and {MINOR} version numbers.
52
+ PATCH = 1
53
+
54
+ #
55
+ # Module Methods
56
+ #
57
+
58
+ # The full version string, including the {ImproperlyBranched::Version::MAJOR},
59
+ # {ImproperlyBranched::Version::MINOR}, {ImproperlyBranched::Version::PATCH}, and optionally, the
60
+ # `ImproperlyBranched::Version::PRERELEASE` in the
61
+ # {http://semver.org/spec/v2.0.0.html semantic versioning v2.0.0} format.
62
+ #
63
+ # @return [String] '{ImproperlyBranched::Version::MAJOR}.{ImproperlyBranched::Version::MINOR}.{ImproperlyBranched::Version::PATCH}' on master.
64
+ # '{ImproperlyBranched::Version::MAJOR}.{ImproperlyBranched::Version::MINOR}.{ImproperlyBranched::Version::PATCH}-PRERELEASE'
65
+ # on any branch other than master.
66
+ def self.full
67
+ version = "#{MAJOR}.#{MINOR}.#{PATCH}"
68
+
69
+ if defined? PRERELEASE
70
+ version = "#{version}-#{PRERELEASE}"
71
+ end
72
+
73
+ version
74
+ end
75
+
76
+ # The full gem version string, including the {ImproperlyBranched::Version::MAJOR},
77
+ # {ImproperlyBranched::Version::MINOR}, {ImproperlyBranched::Version::PATCH}, and optionally, the
78
+ # `ImproperlyBranched::Version::PRERELEASE` in the
79
+ # {http://guides.rubygems.org/specification-reference/#version RubyGems versioning} format.
80
+ #
81
+ # @return [String] '{ImproperlyBranched::Version::MAJOR}.{ImproperlyBranched::Version::MINOR}.{ImproperlyBranched::Version::PATCH}'
82
+ # on master. '{ImproperlyBranched::Version::MAJOR}.{ImproperlyBranched::Version::MINOR}.{ImproperlyBranched::Version::PATCH}.PRERELEASE'
83
+ # on any branch other than master.
84
+ def self.gem
85
+ full.gsub('-', '.pre.')
86
+ end
87
+ end
88
+
89
+ # (see Version.gem)
90
+ GEM_VERSION = Version.gem
91
+
92
+ # (see Version.full)
93
+ VERSION = Version.full
94
+ end
95
+
96
+ """
97
+ Then I successfully run `rake spec`
@@ -5,7 +5,7 @@ Given(/^I unset the environment variable "(.*?)"$/) do |variable|
5
5
  # ENV[variable] = nil and ENV.delete(variable) will not stop the parent process's environment variable from
6
6
  # propagating to processes called by run_simple, so have to fake unsetting with blank values.
7
7
  if RUBY_PLATFORM == 'java'
8
- warn "Faking unsetting environment variable for JRuby by setting to blank string"
8
+ warn 'Faking unsetting environment variable for JRuby by setting to blank string'
9
9
  set_env(variable, '')
10
10
  else
11
11
  set_env(variable, nil)
@@ -1,6 +1,6 @@
1
1
  fail_on_error = true
2
2
 
3
- Given /^a git repository$/ do
3
+ Given(/^a git repository$/) do
4
4
  # git init will fail if account ident it not setup
5
5
  if ENV['TRAVIS'] == 'true'
6
6
  run_simple('git config --global user.email "cucumber@example.com"')
@@ -14,7 +14,7 @@ Given /^a git repository$/ do
14
14
  run_simple('git commit --message "Initial commit"', fail_on_error)
15
15
  end
16
16
 
17
- Given /^(\d+) commits$/ do |commits|
17
+ Given(/^(\d+) commits$/) do |commits|
18
18
  commits = commits.to_i
19
19
 
20
20
  commits.times do |commit|
@@ -25,6 +25,14 @@ Given /^(\d+) commits$/ do |commits|
25
25
  end
26
26
  end
27
27
 
28
- Given /^a git checkout of "(.*?)"$/ do |treeish|
28
+ Given(/^a git checkout of "(.*?)"$/) do |treeish|
29
29
  run_simple("git checkout #{treeish}", fail_on_error)
30
+ end
31
+
32
+ Given(/^my git identity is configured$/) do
33
+ # git commit will fail if account ident is not setup
34
+ if ENV['TRAVIS'] == 'true'
35
+ run_simple('git config --local user.email "cucumber@example.com"')
36
+ run_simple('git config --local user.name "Cucumber"')
37
+ end
30
38
  end
@@ -1,5 +1,7 @@
1
- # Has to be the first file required so that all other files show coverage information
2
- require 'simplecov'
1
+ if RUBY_ENGINE == 'ruby'
2
+ # Has to be the first file required so that all other files show coverage information
3
+ require 'simplecov'
4
+ end
3
5
 
4
6
  #
5
7
  # Standard Library
@@ -15,8 +17,9 @@ require 'aruba/cucumber'
15
17
  # only does jruby customization if actually in JRuby
16
18
  require 'aruba/jruby'
17
19
 
18
- Before do |scenario|
19
- command_name = case scenario
20
+ if defined? SimpleCov
21
+ Before do |scenario|
22
+ command_name = case scenario
20
23
  when Cucumber::Ast::Scenario, Cucumber::Ast::ScenarioOutline
21
24
  "#{scenario.feature.title} #{scenario.name}"
22
25
  when Cucumber::Ast::OutlineTable::ExampleRow
@@ -25,15 +28,16 @@ Before do |scenario|
25
28
  "#{scenario_outline.feature.title} #{scenario_outline.name} #{scenario.name}"
26
29
  else
27
30
  raise TypeError, "Don't know how to extract command name from #{scenario.class}"
28
- end
31
+ end
29
32
 
30
- # Used in simplecov_setup so that each scenario has a different name and their coverage results are merged instead
31
- # of overwriting each other as 'Cucumber Features'
32
- set_env('SIMPLECOV_COMMAND_NAME', command_name)
33
+ # Used in simplecov_setup so that each scenario has a different name and their coverage results are merged instead
34
+ # of overwriting each other as 'Cucumber Features'
35
+ set_env('SIMPLECOV_COMMAND_NAME', command_name)
33
36
 
34
- simplecov_setup_pathname = Pathname.new(__FILE__).expand_path.parent.join('simplecov_setup')
35
- # set environment variable so child processes will merge their coverage data with parent process's coverage data.
36
- set_env('RUBYOPT', "-r#{simplecov_setup_pathname} #{ENV['RUBYOPT']}")
37
+ simplecov_setup_pathname = Pathname.new(__FILE__).expand_path.parent.join('simplecov_setup')
38
+ # set environment variable so child processes will merge their coverage data with parent process's coverage data.
39
+ set_env('RUBYOPT', "-r#{simplecov_setup_pathname} #{ENV['RUBYOPT']}")
40
+ end
37
41
  end
38
42
 
39
43
  Before do
@@ -15,5 +15,6 @@ module Metasploit
15
15
  # Namespace for this gem.
16
16
  module Version
17
17
  autoload :Branch, 'metasploit/version/branch'
18
+ autoload :CLI, 'metasploit/version/cli'
18
19
  end
19
20
  end
@@ -0,0 +1,321 @@
1
+ #
2
+ # Standard library
3
+ #
4
+
5
+ require 'pathname'
6
+
7
+ #
8
+ # Gems
9
+ #
10
+
11
+ require 'thor'
12
+
13
+ #
14
+ # Project
15
+ #
16
+
17
+ require 'metasploit/version'
18
+ require 'metasploit/version/version'
19
+
20
+ # Command-line interface for `metasploit-version`. Used to run commands for managing the semantic version of a project.
21
+ class Metasploit::Version::CLI < Thor
22
+ include Thor::Actions
23
+
24
+ #
25
+ # CONSTANTS
26
+ #
27
+
28
+ # Name of this gem, for use in other projects that call `metasploit-version install`.
29
+ GEM_NAME = 'metasploit-version'
30
+ # Matches pre-existing development dependency on metasploit-version for updating.
31
+ DEVELOPMENT_DEPENDENCY_REGEXP = /spec\.add_development_dependency\s+(?<quote>"|')#{GEM_NAME}\k<quote>/
32
+
33
+ #
34
+ # Class options
35
+ #
36
+
37
+ class_option :force,
38
+ default: false,
39
+ desc: 'Force overwriting conflicting files',
40
+ type: :boolean
41
+ class_option :skip,
42
+ default: false,
43
+ desc: 'Skip conflicting files',
44
+ type: :boolean
45
+
46
+ #
47
+ # Configuration
48
+ #
49
+
50
+ root = Pathname.new(__FILE__).parent.parent.parent.parent
51
+ source_root root.join('app', 'templates')
52
+
53
+ #
54
+ # Commands
55
+ #
56
+
57
+ desc 'install',
58
+ 'Install metasploit-version and sets up files'
59
+ long_desc(
60
+ "Adds 'metasploit-version' as a development dependency in this project's gemspec OR updates the semantic version requirement; " \
61
+ "adds semantic versioning version.rb file."
62
+ )
63
+ option :major,
64
+ banner: 'MAJOR',
65
+ default: 0,
66
+ desc: 'Major version number',
67
+ type: :numeric
68
+ option :minor,
69
+ banner: 'MINOR',
70
+ default: 0,
71
+ desc: 'Minor version number, scoped to MAJOR version number.',
72
+ type: :numeric
73
+ option :patch,
74
+ banner: 'PATCH',
75
+ default: 1,
76
+ desc: 'Patch version number, scoped to MAJOR and MINOR version numbers.',
77
+ type: :numeric
78
+ option :bundle_install,
79
+ default: true,
80
+ desc: '`bundle install` after adding `metasploit-version` as a development dependency so you can ' \
81
+ 'immediately run `rake spec` afterwards. Use `--no-bundle-install` if you want to add other gems to ' \
82
+ 'the gemspec or Gemfile before installing or you\'re just rerunning install to update the templated ' \
83
+ 'files and the dependencies are already in your bundle.',
84
+ type: :boolean
85
+ option :github_owner,
86
+ default: 'rapid7',
87
+ desc: 'The owner of the github repo for this gem. Used to generate links in CONTRIBUTING.md',
88
+ type: :string
89
+ option :ruby_versions,
90
+ default: ['jruby', 'ruby-2.1'],
91
+ desc: 'Ruby versions that the gem should be released for on rubygems.org as part of CONTRIBUTING.md',
92
+ type: :array
93
+ # Adds 'metasploit-version' as a development dependency in this project's gemspec.
94
+ #
95
+ # @return [void]
96
+ def install
97
+ ensure_development_dependency
98
+ template('lib/versioned/version.rb.tt', "lib/#{namespaced_path}/version.rb")
99
+ install_bundle
100
+ template('CHANGELOG.md.tt', 'CHANGELOG.md')
101
+ template('CONTRIBUTING.md.tt', 'CONTRIBUTING.md')
102
+ template('RELEASING.md.tt', 'RELEASING.md')
103
+ template('UPGRADING.md.tt', 'UPGRADING.md')
104
+ setup_rspec
105
+ end
106
+
107
+ private
108
+
109
+ # Capitalizes words by converting the first character of `word` to upper case.
110
+ #
111
+ # @param word [String] a lower case string
112
+ # @return [String]
113
+ def capitalize(word)
114
+ word[0, 1].upcase + word[1 .. -1]
115
+ end
116
+
117
+ # The line injected into the gemspec by {#ensure_development_dependency}
118
+ #
119
+ # @return [String]
120
+ def development_dependency_line
121
+ " spec.add_development_dependency '#{GEM_NAME}', '#{version_requirement}'\n"
122
+ end
123
+
124
+ # Ensures that the {#gemspec_path} contains a development dependency on {GEM_NAME}.
125
+ #
126
+ # Adds `spec.add_development_dependency 'metasploit_version', '~> <semantic version requirement>'` if {#gemspec_path}
127
+ # does not have such an entry. Otherwise, updates the `<semantic version requirement>` to match this version of
128
+ # `metasploit-version`.
129
+ #
130
+ # @return [void]
131
+ # @raise (see #gemspec_path)
132
+ def ensure_development_dependency
133
+ path = gemspec_path
134
+ gem_specification = Gem::Specification.load(path)
135
+
136
+ metasploit_version = gem_specification.dependencies.find { |dependency|
137
+ dependency.name == GEM_NAME
138
+ }
139
+
140
+ lines = []
141
+
142
+ if metasploit_version
143
+ if metasploit_version.requirements_list.include? '>= 0'
144
+ shell.say "Adding #{GEM_NAME} as a development dependency to "
145
+ else
146
+ shell.say "Updating #{GEM_NAME} requirements in "
147
+ end
148
+
149
+ shell.say path
150
+
151
+ File.open(path) do |f|
152
+ f.each_line do |line|
153
+ match = line.match(DEVELOPMENT_DEPENDENCY_REGEXP)
154
+
155
+ if match
156
+ lines << development_dependency_line
157
+ else
158
+ lines << line
159
+ end
160
+ end
161
+ end
162
+ else
163
+ end_index = nil
164
+ lines = []
165
+
166
+ open(path) do |f|
167
+ line_index = 0
168
+
169
+ f.each_line do |line|
170
+ lines << line
171
+
172
+ if line =~ /^\s*end\s*$/
173
+ end_index = line_index
174
+ end
175
+
176
+ line_index += 1
177
+ end
178
+ end
179
+
180
+ lines.insert(end_index, development_dependency_line)
181
+ end
182
+
183
+ File.open(path, 'w') do |f|
184
+ lines.each do |line|
185
+ f.write(line)
186
+ end
187
+ end
188
+ end
189
+
190
+ # The name of the gemspec in the current working directory.
191
+ #
192
+ # @return [String] relative path to the current working directory's gemspec.
193
+ # @raise [SystemExit] if no gemspec is found
194
+ def gemspec_path
195
+ unless instance_variable_defined? :@gemspec
196
+ path = "#{name}.gemspec"
197
+
198
+ unless File.exist?(path)
199
+ shell.say 'No gemspec found'
200
+ exit 1
201
+ end
202
+
203
+ @gemspec_path = path
204
+ end
205
+
206
+ @gemspec_path
207
+ end
208
+
209
+ # The URL of the github repository. Used to calculate the fork and issues URL in `CONTRIBUTING.md`.
210
+ #
211
+ # @return [String] https url to github repository
212
+ def github_url
213
+ @github_url ||= "https://github.com/#{options[:github_owner]}/#{name}"
214
+ end
215
+
216
+ # `bundle install` if the :bundle_install options is `true`
217
+ #
218
+ # @return [void]
219
+ def install_bundle
220
+ if options[:bundle_install]
221
+ system('bundle', 'install')
222
+ end
223
+ end
224
+
225
+ # The name of the gem.
226
+ #
227
+ # @return [String] name of the gem. Assumed to be the name of the pwd as it should match the repository name.
228
+ def name
229
+ @name ||= File.basename(Dir.pwd)
230
+ end
231
+
232
+ # The fully-qualified namespace for the gem.
233
+ #
234
+ # @param [String]
235
+ def namespace_name
236
+ @namespace_name ||= namespaces.join('::')
237
+ end
238
+
239
+ # List of `Module#name`s making up the {#namespace_name the fully-qualifed namespace for the gem}.
240
+ #
241
+ # @return [Array<String>]
242
+ def namespaces
243
+ unless instance_variable_defined? :@namespaces
244
+ underscored_words = name.split('_')
245
+ capitalized_underscored_words = underscored_words.map { |underscored_word|
246
+ capitalize(underscored_word)
247
+ }
248
+ capitalized_hyphenated_name = capitalized_underscored_words.join
249
+ hyphenated_words = capitalized_hyphenated_name.split('-')
250
+
251
+ @namespaces = hyphenated_words.map { |hyphenated_word|
252
+ capitalize(hyphenated_word)
253
+ }
254
+ end
255
+
256
+ @namespaces
257
+ end
258
+
259
+ # The relative path of the gem under `lib`.
260
+ #
261
+ # @return [String] Format of `[<parent>/]*<child>`
262
+ def namespaced_path
263
+ @namespaced_path ||= name.tr('-', '/')
264
+ end
265
+
266
+ # The prerelease version.
267
+ #
268
+ # @return [nil] if on master or HEAD
269
+ # @return [String] if on a branch
270
+ def prerelease
271
+ unless instance_variable_defined? :@prerelease
272
+ branch = Metasploit::Version::Branch.current
273
+ parsed = Metasploit::Version::Branch.parse(branch)
274
+
275
+ if parsed.is_a? Hash
276
+ prerelease = parsed[:prerelease]
277
+
278
+ if prerelease
279
+ @prerelease = prerelease
280
+ end
281
+ end
282
+ end
283
+
284
+ @prerelease
285
+ end
286
+
287
+ # Generates `.rspec`, `Rakefile`, `version_spec.rb`, `<namespace>_spec.rb` and `spec/spec_helper.rb`
288
+ #
289
+ # @return [void]
290
+ def setup_rspec
291
+ template('.rspec.tt', '.rspec')
292
+ template('Rakefile.tt', 'Rakefile')
293
+ template('spec/lib/versioned/version_spec.rb.tt', "spec/#{version_path.sub(/\.rb$/, '_spec.rb')}")
294
+ template('spec/lib/versioned_spec.rb.tt', "spec/lib/#{namespaced_path}_spec.rb")
295
+ template('spec/spec_helper.rb.tt', 'spec/spec_helper.rb')
296
+ end
297
+
298
+ # Path to the `version.rb` for the gem.
299
+ #
300
+ # @return [String]
301
+ def version_path
302
+ @version_path ||= "lib/#{namespaced_path}/version.rb"
303
+ end
304
+
305
+ # The version requirement on the `metasploit-version` development dependency in {#development_dependency_line}.
306
+ #
307
+ # @return [String]
308
+ def version_requirement
309
+ if defined? Metasploit::Version::Version::PRERELEASE
310
+ # require exactly this pre-release in case there are multiple prereleases for the same
311
+ # version number due to parallel branches.
312
+ "= #{Metasploit::Version::GEM_VERSION}"
313
+ elsif Metasploit::Version::Version::MAJOR < 1
314
+ # can only allow the PATCH to wiggle pre-1.0.0
315
+ "~> #{Metasploit::Version::Version::MAJOR}.#{Metasploit::Version::Version::MINOR}.#{Metasploit::Version::Version::PATCH}"
316
+ else
317
+ # can allow the MINOR to wiggle 1.0.0+
318
+ "~> #{Metasploit::Version::Version::MAJOR}.#{Metasploit::Version::Version::MINOR}"
319
+ end
320
+ end
321
+ end