thor-scmversion 0.3.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -46,9 +46,27 @@ Now when you list your thor tasks you'll see 2 new ones.
46
46
 
47
47
  version
48
48
  -------
49
- thor version:bump TYPE # Bump version number (type is major, minor, patch or auto)
49
+ thor version:bump TYPE [PRERELEASE_TYPE] # Bump version number (type is major, minor, patch, prerelease or auto)
50
+ # Prerelease allows an additional parameter to be passed which is used
51
+ # as the prerelease type.
50
52
  thor version:current # Show current SCM tagged version
51
53
 
54
+ Usage Examples:
55
+ $ thor version:current
56
+ 1.2.1
57
+ $ thor version:bump auto
58
+ 1.2.2
59
+ $ thor version:bump major
60
+ 2.0.0
61
+ $ thor version:bump prerelease
62
+ 2.0.1-alpha.1
63
+ $ thor version:bump prerelease
64
+ 2.0.1-alpha.2
65
+ $ thor version:bump prerelease beta
66
+ 2.0.1-beta.1
67
+ $ thor version:bump minor
68
+ 2.1.0
69
+
52
70
  ### Remove your VERSION file from source control
53
71
 
54
72
  Since your CI server will be managing your VERSION file, you don't
@@ -77,23 +95,24 @@ run `thor version:bump patch`. This will increment the patch, push the
77
95
  new tag, and write the VERSION file. Now the artifact you build will
78
96
  have the right version information, every time.
79
97
 
80
- ### You manage the major and minor
98
+ ### You manage the major, minor patch and prerelease
81
99
 
82
- When you make significant changes, you can bump the major or minor
100
+ When you make significant changes, you can bump the major, minor or patch
83
101
  number yourself with `thor version:bump minor`. This will create a tag
84
102
  with a .0 patch level, so the next build made by the server will be
85
103
  .1 patch level.
86
104
 
87
105
  ### Auto bumping
88
106
 
89
- If you include #major or #minor in the subject of commits, and run
90
- `thor version:bump auto` it will see if any major or minor level changes
107
+ If you include #major, #minor or #patch in the subject of commits, and run
108
+ `thor version:bump auto` it will see if any major, minor or patch level changes
91
109
  are included since the last tag, and use the appropriate version. This works
92
110
  especially well with a CI server, allowing you to never have to directly
93
- manage versions at all.
111
+ manage versions at all. If no commits are tagged, the build number for the
112
+ current version will be bumped instead.
94
113
 
95
114
  NOTE: auto bumping currently only works for Git repos. For Perforce repos,
96
- auto is the same as patch.
115
+ auto is the same as build.
97
116
 
98
117
 
99
118
  ## Contributing
@@ -3,74 +3,34 @@ Feature: Bump
3
3
  I want to be able to bump the version of a project's with a simple command
4
4
  So that I don't have to do it manually
5
5
 
6
- Scenario: Bumping a patch version in Git
7
- Given I have a git project of version '1.0.0'
8
- When I run `bundle exec thor version:bump patch` from the temp directory
9
- Then the version should be '1.0.1'
10
- And the origin version should be '1.0.1'
11
-
12
- Scenario: Bumping a minor version in Git
13
- Given I have a git project of version '1.0.0'
14
- When I run `bundle exec thor version:bump minor` from the temp directory
15
- Then the version should be '1.1.0'
16
- And the origin version should be '1.1.0'
17
-
18
- Scenario: Bumping a major version in Git
19
- Given I have a git project of version '1.0.0'
20
- When I run `bundle exec thor version:bump major` from the temp directory
21
- Then the version should be '2.0.0'
22
- And the origin version should be '2.0.0'
23
-
24
- Scenario: Bumping a minor version in Git should reset the patch version
25
- Given I have a git project of version '1.1.5'
26
- When I run `bundle exec thor version:bump minor` from the temp directory
27
- Then the version should be '1.2.0'
28
- And the origin version should be '1.2.0'
29
-
30
- Scenario: Bumping a major version in Git should reset the patch and minor versions
31
- Given I have a git project of version '1.1.5'
32
- When I run `bundle exec thor version:bump major` from the temp directory
33
- Then the version should be '2.0.0'
34
- And the origin version should be '2.0.0'
35
-
36
- Scenario: Bumping a patch version in Git
6
+ Scenario Outline: Bumping a version
7
+ Given I have a <scm> project of version '<starting version>'
8
+ When I run `bundle exec thor version:bump <bump type>` from the temp directory
9
+ Then the version should be '<resulting version>'
10
+ And the <scm> server version should be '<resulting version>'
11
+
12
+ Examples:
13
+ | scm | starting version | bump type | resulting version |
14
+ | git | 1.0.0 | patch | 1.0.1 |
15
+ | git | 1.0.0 | minor | 1.1.0 |
16
+ | git | 1.0.0 | major | 2.0.0 |
17
+ | git | 1.1.5 | minor | 1.2.0 |
18
+ | git | 1.1.5 | major | 2.0.0 |
19
+ | git | 1.0.0 | prerelease | 1.0.1-alpha.1 |
20
+ | git | 1.0.0 | prerelease someth | 1.0.1-someth.1 |
21
+ | git | 1.0.0-alpha.1 | prerelease | 1.0.0-alpha.2 |
22
+ | git | 1.0.0-alpha.5 | prerelease beta | 1.0.0-beta.1 |
23
+ | git | 1.0.0 | build | 1.0.0+build.2 |
24
+ | git | 1.0.0-alpha.3 | build | 1.0.0-alpha.3+build.2 |
25
+ | p4 | 1.0.0 | patch | 1.0.1 |
26
+ | p4 | 1.0.0 | minor | 1.1.0 |
27
+ | p4 | 1.0.0 | major | 2.0.0 |
28
+ | p4 | 1.1.5 | minor | 1.2.0 |
29
+ | p4 | 1.1.5 | major | 2.0.0 |
30
+
31
+ Scenario: Bumping a patch version in Git when the server has an advanced version not yet fetched
37
32
  Given I have a git project of version '1.0.0'
38
33
  And the origin version is '1.0.10'
39
34
  When I run `bundle exec thor version:bump patch` from the temp directory
40
35
  Then the version should be '1.0.11'
41
- And the origin version should be '1.0.11'
42
-
43
- @p4 @wip
44
- Scenario: Bumping a patch version in Perforce
45
- Given I have a Perforce project of version '1.0.0'
46
- When I run `bundle exec thor version:bump patch` from the Perforce project directory
47
- Then the version should be '1.0.1' in the Perforce project directory
48
- And the p4 server version should be '1.0.1'
49
-
50
- @p4
51
- Scenario: Bumping a minor version in Perforce
52
- Given I have a Perforce project of version '1.0.0'
53
- When I run `bundle exec thor version:bump minor` from the Perforce project directory
54
- Then the version should be '1.1.0' in the Perforce project directory
55
- And the p4 server version should be '1.1.0'
56
-
57
- @p4
58
- Scenario: Bumping a major version in Perforce
59
- Given I have a Perforce project of version '1.0.0'
60
- When I run `bundle exec thor version:bump major` from the Perforce project directory
61
- Then the version should be '2.0.0' in the Perforce project directory
62
- And the p4 server version should be '2.0.0'
63
-
64
- @p4
65
- Scenario: Bumping a minor version in Perforce should reset the patch version
66
- Given I have a Perforce project of version '1.1.5'
67
- When I run `bundle exec thor version:bump minor` from the Perforce project directory
68
- Then the version should be '1.2.0' in the Perforce project directory
69
- And the p4 server version should be '1.2.0'
70
-
71
- @p4
72
- Scenario: Bumping a major version in Perforce should reset the patch and minor versions
73
- Given I have a Perforce project of version '1.1.5'
74
- When I run `bundle exec thor version:bump major` from the Perforce project directory
75
- Then the version should be '2.0.0' in the Perforce project directory
76
- And the p4 server version should be '2.0.0'
36
+ And the git server version should be '1.0.11'
@@ -3,71 +3,34 @@ Feature: Guessing the level of a bump
3
3
  I want a command that figures out a major, minor or patch bump based on commit logs
4
4
  So that I can control semver changes while still using a CI server
5
5
 
6
- Background:
7
- Given I have a git project of version '1.2.3'
8
-
9
- Scenario: changeset with no tags
10
- Given a commit message "This is an untagged commit"
11
- And a commit message "this is another commit"
12
- And a commit message "this is another change to the project"
13
- When I run `bundle exec thor version:bump auto` from the temp directory
14
- Then the version should be '1.2.4'
15
- And the origin version should be '1.2.4'
16
-
17
- Scenario: changeset with a [major] tag
18
- Given a commit message "This is an untagged commit"
19
- And a commit message "this is another commit"
20
- And a commit message "this is a big change to the project [major]"
21
- When I run `bundle exec thor version:bump auto` from the temp directory
22
- Then the version should be '2.0.0'
23
- And the origin version should be '2.0.0'
24
-
25
- Scenario: changeset with a [minor] tag
26
- Given a commit message "This is an untagged commit"
27
- And a commit message "this is another commit"
28
- And a commit message "this is a smallish change to the project [minor]"
29
- When I run `bundle exec thor version:bump auto` from the temp directory
30
- Then the version should be '1.3.0'
31
- And the origin version should be '1.3.0'
32
-
33
- Scenario: changeset with a [major] and [minor] tag
34
- Given a commit message "This is an untagged commit"
35
- And a commit message "this is another commit"
36
- And a commit message "this is a big change to the project [major]"
37
- And a commit message "this is a smallish change to the project [minor]"
38
- When I run `bundle exec thor version:bump auto` from the temp directory
39
- Then the version should be '2.0.0'
40
- And the origin version should be '2.0.0'
41
-
42
- Scenario: changeset with a #major tag
43
- Given a commit message "This is an untagged commit"
44
- And a commit message "this is another commit"
45
- And a commit message "this is a big change to the project #major"
46
- When I run `bundle exec thor version:bump auto` from the temp directory
47
- Then the version should be '2.0.0'
48
- And the origin version should be '2.0.0'
49
-
50
- Scenario: changeset with a #minor tag
51
- Given a commit message "This is an untagged commit"
52
- And a commit message "this is another commit"
53
- And a commit message "this is a smallish change to the project #minor"
54
- When I run `bundle exec thor version:bump auto` from the temp directory
55
- Then the version should be '1.3.0'
56
- And the origin version should be '1.3.0'
57
-
58
- Scenario: changeset with a #major and #minor tag
59
- Given a commit message "This is an untagged commit"
60
- And a commit message "this is another commit"
61
- And a commit message "this is a big change to the project #major"
62
- And a commit message "this is a smallish change to the project #minor"
63
- When I run `bundle exec thor version:bump auto` from the temp directory
64
- Then the version should be '2.0.0'
65
- And the origin version should be '2.0.0'
66
-
67
- Scenario: changeset with a [MAJOR] tag
68
- Given a commit message "This is an untagged commit"
69
- And a commit message "this is another commit"
70
- And a commit message "this is a big change to the project [MAJOR]"
71
- When I run `bundle exec thor version:bump auto` from the temp directory
72
- Then the version should be '2.0.0'
73
- And the origin version should be '2.0.0'
6
+ Scenario Outline: changeset tags
7
+ Given I have a git project of version '<starting version>'
8
+ And a commit message "<message 1>"
9
+ And a commit message "<message 2>"
10
+ And a commit message "<message 3>"
11
+ When I run `bundle exec thor version:bump auto` from the temp directory
12
+ Then the version should be '<resulting version>'
13
+ And the <scm> server version should be '<resulting version>'
14
+
15
+ Examples:
16
+ | starting version | scm | message 1 | message 2 | message 3 | resulting version |
17
+ | 1.2.3 | git | untagged | another commit | another change | 1.2.3+build.2 |
18
+ | 1.2.3 | git | untagged | another commit | big change [major] | 2.0.0 |
19
+ | 1.2.3 | git | untagged | another commit | big change [MAJOR] | 2.0.0 |
20
+ | 1.2.3 | git | untagged | another commit | smallish change [minor] | 1.3.0 |
21
+ | 1.2.3 | git | untagged | big change [major] | smallish change [minor] | 2.0.0 |
22
+ | 1.2.3 | git | untagged | another commit | pre [prerelease] | 1.2.4-alpha.1 |
23
+ | 1.2.3 | git | untagged | another commit | pre [prerelease beta] | 1.2.4-beta.1 |
24
+ | 1.2.3-beta.1 | git | untagged | another commit | pre [prerelease beta] | 1.2.3-beta.2 |
25
+ | 1.2.3-beta.1 | git | untagged | another commit | pre [prerelease someth] | 1.2.3-someth.1 |
26
+ | 1.2.3-beta.1 | git | untagged | another commit | pre [prerelease] | 1.2.3-beta.2 |
27
+ | 1.2.3 | git | untagged | another commit | build [patch] | 1.2.4 |
28
+ | 1.2.3 | git | untagged | another commit | big change #major | 2.0.0 |
29
+ | 1.2.3 | git | untagged | another commit | smallish change #minor | 1.3.0 |
30
+ | 1.2.3 | git | untagged | big change #major | smallish change #minor | 2.0.0 |
31
+ | 1.2.3 | git | untagged | another commit | pre #prerelease | 1.2.4-alpha.1 |
32
+ | 1.2.3 | git | untagged | another commit | pre #prerelease-beta | 1.2.4-beta.1 |
33
+ | 1.2.3-beta.1 | git | untagged | another commit | pre #prerelease-beta | 1.2.3-beta.2 |
34
+ | 1.2.3-beta.1 | git | untagged | another commit | pre #prerelease-someth | 1.2.3-someth.1 |
35
+ | 1.2.3-beta.1 | git | untagged | another commit | pre #prerelease | 1.2.3-beta.2 |
36
+ | 1.2.3 | git | untagged | another commit | build #patch | 1.2.4 |
@@ -34,13 +34,13 @@ Then /^the version should be '(.*)'$/ do |version|
34
34
  }
35
35
  end
36
36
 
37
- Then /^the version should be '(.+)' in the Perforce project directory$/ do |version|
37
+ Then /^the version should be '(.+)' in the p4 project directory$/ do |version|
38
38
  Dir.chdir(perforce_project_dir) {
39
39
  ThorSCMVersion.versioner.from_path.to_s.should == version
40
40
  }
41
41
  end
42
42
 
43
- Then /^the origin version should be '(.*)'$/ do |version|
43
+ Then /^the git server version should be '(.*)'$/ do |version|
44
44
  Dir.chdir(origin_dir) {
45
45
  ThorSCMVersion.versioner.from_path.to_s.should == version
46
46
  }
@@ -59,13 +59,13 @@ When /^I run `(.*)` from the temp directory$/ do |run|
59
59
  }
60
60
  end
61
61
 
62
- When /^I run `(.*)` from the Perforce project directory$/ do |run|
62
+ When /^I run `(.*)` from the p4 project directory$/ do |run|
63
63
  Dir.chdir(perforce_project_dir) {
64
64
  `#{run}`
65
65
  }
66
66
  end
67
67
 
68
- Given /^I have a Perforce project of version '(.*)'$/ do |version|
68
+ Given /^I have a p4 project of version '(.*)'$/ do |version|
69
69
  ENV['P4PORT'] = 'p4server.example.com:1666'
70
70
  ENV['P4USER'] = 'tester'
71
71
  ENV['P4PASSWD'] = 'tester'
@@ -1,18 +1,20 @@
1
1
  require 'pathname'
2
2
  require 'tmpdir'
3
3
  require 'thor'
4
+ require 'thor-scmversion/prerelease'
4
5
  require 'thor-scmversion/scm_version'
5
6
  require 'thor-scmversion/git_version'
6
7
  require 'thor-scmversion/p4_version'
7
8
  require 'thor-scmversion/shell_utils'
9
+ require 'thor-scmversion/errors'
8
10
 
9
11
  module ThorSCMVersion
10
12
  class Tasks < Thor
11
13
  namespace "version"
12
14
 
13
- desc "bump TYPE", "Bump version number (type is major, minor, patch or auto)"
14
- def bump(type)
15
- current_version.bump! type
15
+ desc "bump TYPE [PRERELEASE_TYPE]", "Bump version number (type is major, minor, patch, prerelease or auto)"
16
+ def bump(type, prerelease_type = nil)
17
+ current_version.bump! type, prerelease_type
16
18
  begin
17
19
  say "Creating and pushing tags", :yellow
18
20
  current_version.tag
@@ -0,0 +1,26 @@
1
+ module ThorSCMVersion
2
+ # @author Josiah Kiehl <josiah@skirmisher.net>
3
+ # adapted from code written by Jamie Winsor <jamie@vialstudios.com>
4
+ class SCMVersionError < StandardError
5
+ class << self
6
+ # @param [Integer] code
7
+ def status_code(code)
8
+ define_method(:status_code) { code }
9
+ define_singleton_method(:status_code) { code }
10
+ end
11
+ end
12
+
13
+ alias_method :message, :to_s
14
+ end
15
+
16
+ class InternalError < SCMVersionError; status_code(99); end
17
+
18
+ class TagFormatError < SCMVersionError
19
+ status_code(100)
20
+ def initialize(tag); @tag = tag; end
21
+ def to_s; "#{@tag.inspect} is formatted incorrectly."; end
22
+ end
23
+ class InvalidPrereleaseFormatError < TagFormatError
24
+ def to_s; super + " Format must be: #{Prerelease::FORMAT.inspect}."; end
25
+ end
26
+ end
@@ -7,7 +7,7 @@ module ThorSCMVersion
7
7
  Dir.chdir(path) do
8
8
  tags = Open3.popen3("git tag") { |stdin, stdout, stderr| stdout.read }.split(/\n/)
9
9
  version_tags = tags.select { |tag| tag.match(ScmVersion::VERSION_FORMAT) }
10
- version_tags.collect { |tag| new(*tag.split('.')) }.sort.reverse
10
+ version_tags.collect { |tag| from_tag(tag) }.sort.reverse
11
11
  end
12
12
  end
13
13
 
@@ -21,6 +21,7 @@ module ThorSCMVersion
21
21
  ShellUtils.sh "git push --tags || true"
22
22
  end
23
23
 
24
+ # Check the commit messages to see what type of bump is required
24
25
  def auto_bump
25
26
  last_tag = self.class.from_path.to_s
26
27
  logs = ShellUtils.sh "git log --abbrev-commit --format=oneline #{last_tag}.."
@@ -28,10 +29,15 @@ module ThorSCMVersion
28
29
  :major
29
30
  elsif logs =~ /\[minor\]|\#minor/i
30
31
  :minor
31
- else
32
+ elsif logs =~ /\[prerelease\s?(#{Prerelease::TYPE_FORMAT})?\]|\#prerelease\-?(#{Prerelease::TYPE_FORMAT})?/
33
+ prerelease_type = $1 || $2
34
+ :prerelease
35
+ elsif logs =~ /\[patch\]|\#patch/i
32
36
  :patch
37
+ else
38
+ :build
33
39
  end
34
- bump!(guess)
40
+ bump!(guess, prerelease_type)
35
41
  end
36
42
  end
37
43
  end
@@ -81,10 +81,10 @@ module ThorSCMVersion
81
81
  end
82
82
  end
83
83
 
84
- def initialize(major=0, minor=0, patch=0)
84
+ def initialize(major = 0, minor = 0, patch = 0, prerelease = nil, build = 1)
85
85
  self.p4_depot_path = self.class.depot_path('.')
86
86
  self.p4_module_name = self.class.module_name('.')
87
- super(major, minor, patch)
87
+ super
88
88
  end
89
89
 
90
90
  attr_accessor :version_file_path
@@ -0,0 +1,80 @@
1
+ module ThorSCMVersion
2
+ class Prerelease
3
+ TYPE_FORMAT = /[A-Za-z0-9]+/
4
+ FORMAT = /(#{TYPE_FORMAT})\.(\d+)/.freeze
5
+ DEFAULT_TYPE = 'alpha'
6
+
7
+ class << self
8
+ # Reads the prerelease segment of the version. While semver
9
+ # provides a more general format, the format with
10
+ # thor-scmversion is more strictly defined:
11
+ #
12
+ # Examples:
13
+ # str = 'alpha.1'
14
+ # str = 'beta.10'
15
+ # str = 'rc.2'
16
+ #
17
+ # @param [String] str String in the format /[A-Za-z]+\.\d+/
18
+ # @return [Prerelease]
19
+ def from_string(str)
20
+ return nil if str.nil?
21
+ matchdata = str.match(FORMAT)
22
+ type, version = matchdata.captures unless matchdata.nil?
23
+ raise InvalidPrereleaseFormatError.new(str) if (matchdata.nil? ||
24
+ matchdata.captures.size != 2)
25
+ new(type, version)
26
+ end
27
+ end
28
+
29
+ attr_reader :version
30
+ attr_reader :type
31
+
32
+ # initialize
33
+ #
34
+ # @param [String] type Type of prerelease
35
+ # @param [responds_to? :to_i] version Version of the given type of prerelease
36
+ #
37
+ # Examples:
38
+ # 'beta', 1 #=> x.x.x-beta.1
39
+ def initialize(type = DEFAULT_TYPE, version = 1)
40
+ @version = version.to_i
41
+ @type = type.nil? || type.empty? ? DEFAULT_TYPE : type
42
+ end
43
+
44
+ def to_s
45
+ "#{@type}.#{@version}"
46
+ end
47
+
48
+ def <=>(other)
49
+ raise ArgumentError unless kind_of? other.class
50
+ if self.type == other.type
51
+ self.version <=> other.version
52
+ else
53
+ self.type <=> other.type
54
+ end
55
+ end
56
+
57
+ def >(other)
58
+ raise ArgumentError unless kind_of? other.class
59
+ return (self <=> other) == 1
60
+ end
61
+
62
+ def <(other)
63
+ raise ArgumentError unless kind_of? other.class
64
+ return (self <=> other) == -1
65
+ end
66
+
67
+ def ==(other)
68
+ other &&
69
+ kind_of?(other.class) &&
70
+ self.version == other.version &&
71
+ self.type == other.type
72
+ end
73
+
74
+ # Delegate methods to @version so this object acts like an Integer
75
+ def method_missing(method, *args, &blk)
76
+ @version = self.version.send(method, *args, &blk)
77
+ self
78
+ end
79
+ end
80
+ end
@@ -1,6 +1,9 @@
1
1
  module ThorSCMVersion
2
2
 
3
3
  class << self
4
+ # Figures out whether the repository is managed by git. If not, use p4.
5
+ #
6
+ # @return [#kind_of? ScmVersion]
4
7
  def versioner
5
8
  if(File.directory?(".git"))
6
9
  return GitVersion
@@ -10,17 +13,43 @@ module ThorSCMVersion
10
13
  end
11
14
  end
12
15
 
16
+ # author Josiah Kiehl <josiah@skirmisher.net>
13
17
  class ScmVersion
14
18
  include Comparable
15
19
 
16
- VERSION_FORMAT = /^(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)$/
20
+ # Tags not matching this format will not show up in the tags list
21
+ #
22
+ # Examples:
23
+ # 1.2.3 #=> valid
24
+ # 1.2.3.4 #=> invalid
25
+ # 1.2.3-alpha.1 #=> valid
26
+ # 1.2.3-alpha #=> invalid
27
+ VERSION_FORMAT = /^(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)-?(?<prerelease>#{Prerelease::FORMAT})?(\+build\.)?(?<build>\d+)?$/
28
+
29
+ # Default file to write the current version to
17
30
  VERSION_FILENAME = 'VERSION'
18
31
  class << self
32
+ # Retrieve all versions from the repository contained at path
33
+ #
34
+ # @param [String] path Path to the repository
35
+ # @return [Array<ScmVersion>]
19
36
  def from_path(path = '.')
20
37
  retrieve_tags
21
38
  all_from_path(path).first || new(0,0,1)
22
39
  end
23
40
 
41
+ # Create an ScmVersion object from a tag
42
+ #
43
+ # @param [String] tag
44
+ # @return [ScmVersion]
45
+ def from_tag(tag)
46
+ matchdata = tag.match VERSION_FORMAT
47
+ new(matchdata[:major], matchdata[:minor], matchdata[:patch], Prerelease.from_string(matchdata[:prerelease]), matchdata[:build])
48
+ end
49
+
50
+ # In distributed SCMs, tags must be fetched from the server to
51
+ # ensure that the latest tags are being used to calculate the
52
+ # next version.
24
53
  def retrieve_tags
25
54
  # noop
26
55
  end
@@ -28,33 +57,80 @@ module ThorSCMVersion
28
57
  attr_accessor :major
29
58
  attr_accessor :minor
30
59
  attr_accessor :patch
31
-
32
- def initialize(major=0, minor=0, patch=0)
60
+ attr_accessor :prerelease
61
+ attr_accessor :build
62
+
63
+ def initialize(major = 0, minor = 0, patch = 0, prerelease = nil, build = 1)
33
64
  @major = major.to_i
34
65
  @minor = minor.to_i
35
66
  @patch = patch.to_i
67
+ @prerelease = prerelease
68
+ @build = build.nil? ? 1 : build.to_i
36
69
  end
37
-
38
- def bump!(type)
70
+
71
+ # Bumps the version in place
72
+ #
73
+ # @param [Symbol] type Type of bump to be performed
74
+ # @param [String] prerelease_type Type of prerelease to bump to when doing a :prerelease bump
75
+ # @return [ScmVersion]
76
+ def bump!(type, prerelease_type = nil)
39
77
  case type.to_sym
40
78
  when :auto
41
79
  self.auto_bump
42
80
  when :major
43
- self.major += 1
44
- self.minor = 0
45
- self.patch = 0
81
+ self.major += 1
46
82
  when :minor
47
83
  self.minor += 1
48
- self.patch = 0
49
84
  when :patch
50
85
  self.patch += 1
86
+ when :prerelease
87
+ if self.prerelease
88
+ if prerelease_type.nil? || prerelease_type == self.prerelease.type
89
+ self.prerelease += 1
90
+ else
91
+ self.prerelease = Prerelease.new(prerelease_type)
92
+ end
93
+ else
94
+ self.patch += 1
95
+ self.prerelease = Prerelease.new(prerelease_type)
96
+ end
97
+ when :build
98
+ self.build += 1
51
99
  else
52
100
  raise "Invalid release type: #{type}. Valid types are: major, minor, patch, or auto"
53
101
  end
54
102
  raise "Version: #{self.to_s} is less than or equal to the existing version." if self <= self.class.from_path
103
+ reset_for type unless type == :auto
55
104
  self
56
105
  end
57
106
 
107
+ # Reset levels lower than the type being reset for
108
+ #
109
+ # @param [Symbol] type Type under which all segments are to be reset
110
+ def reset_for(type)
111
+ matched = false
112
+ [[:major, Proc.new {
113
+ self.minor = 0
114
+ }],
115
+ [:minor, Proc.new {
116
+ self.patch = 0
117
+ }],
118
+ [:patch, Proc.new {
119
+ self.prerelease = nil
120
+ }],
121
+ [:prerelease, Proc.new {
122
+ self.build = 1
123
+ }]].each do |matcher, reset_proc|
124
+ next unless matched or type.to_sym == matcher
125
+ matched = true
126
+ reset_proc.call
127
+ end
128
+ self
129
+ end
130
+
131
+ # Write the version to the passed in file paths
132
+ #
133
+ # @param [Array<String>] files List of files to write
58
134
  def write_version(files = [ScmVersion::VERSION_FILENAME])
59
135
  files.each do |ver_file|
60
136
  File.open(ver_file, 'w+') do |f|
@@ -64,24 +140,31 @@ module ThorSCMVersion
64
140
  self
65
141
  end
66
142
 
143
+ # Create the tag in the SCM corresponding to the version contained in self.
144
+ # Abstract method. Must be implemented by subclasses.
67
145
  def tag
68
146
  raise NotImplementedError
69
147
  end
70
148
 
71
- def auto_bump
149
+ # Perform a bump by reading recent commit messages in the SCM
150
+ # Abstract method. Must be implemented by subclasses.
151
+ def auto_bump(prerelease_type = nil)
72
152
  raise NotImplementedError
73
153
  end
74
-
154
+
75
155
  def to_s
76
- "#{major}.#{minor}.#{patch}"
156
+ s = "#{major}.#{minor}.#{patch}"
157
+ s << "-#{prerelease}" unless prerelease.nil?
158
+ s << "+build.#{build}" unless build < 2
159
+ s
77
160
  end
78
161
  alias_method :version, :to_s
79
-
162
+
80
163
  def <=>(other)
81
164
  return unless other.is_a?(self.class)
82
165
  return 0 if self.version == other.version
83
166
 
84
- [:major, :minor, :patch].each do |segment|
167
+ [:major, :minor, :patch, :prerelease, :build].each do |segment|
85
168
  next if self.send(segment) == other.send(segment)
86
169
  return 1 if self.send(segment) > other.send(segment)
87
170
  return -1 if self.send(segment) < other.send(segment)
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+
3
+ module ThorSCMVersion
4
+ describe Prerelease do
5
+ subject { Prerelease.new }
6
+ describe "::new" do
7
+ it { subject.to_s.should == 'alpha.1' }
8
+ it "should default to alpha if provided a nil or empty type" do
9
+ described_class.new('').type.should == 'alpha'
10
+ end
11
+ end
12
+ describe "::FORMAT" do
13
+ %w[alpha.1 beta.124].each do |example|
14
+ it "should match #{example}" do
15
+ example.match(described_class::FORMAT).should_not be_nil
16
+ end
17
+ end
18
+ end
19
+ describe "#to_s" do
20
+ it { subject.to_s.should == "alpha.1" }
21
+ end
22
+ describe "#method_missing" do
23
+ it { (subject + 1).class.should == Prerelease }
24
+ it { (subject + 1).version.should == 2 }
25
+ it { pending("Figure out why this doesn't work. Works in irb."); (subject += 1).version.should == 2 } #TODO jk
26
+ end
27
+ describe "#<=>" do
28
+ [[['alpha', 5], ['beta', 1], false],
29
+ [['alpha', 5], ['alpha', 2], true],
30
+ [['alpha', 5], ['zzz', 1], false]].each do |params|
31
+ it "#{params[0].inspect} should #{params[2] ? '' : 'not'} be greater than #{params[1].inspect}" do
32
+ (described_class.new(*params[0]) > described_class.new(*params[1])).should == params[2]
33
+ end
34
+ it "#{params[0].inspect} should #{params[2] ? '' : 'not'} be less than #{params[1].inspect}" do
35
+
36
+ (described_class.new(*params[0]) < described_class.new(*params[1])).should == !params[2]
37
+ end
38
+ end
39
+ end
40
+ describe "::from_string" do
41
+ it 'should parse the segment of the semver containing the prerelease information' do
42
+ p = described_class.from_string('alpha.1')
43
+ p.type.should == 'alpha'
44
+ p.version.should == 1
45
+ end
46
+ it 'should return nil if the str passed in is nil' do
47
+ described_class.from_string(nil).should be_nil
48
+ end
49
+ %w[1 1.alpha alpha alpha.alpha .1].each do |str|
50
+ it "should raise an error if an invalid format (#{str}) is provided" do
51
+ -> { Prerelease.from_string(str) }.should raise_error(InvalidPrereleaseFormatError)
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,127 @@
1
+ require 'spec_helper'
2
+
3
+ module ThorSCMVersion
4
+ shared_examples 'scmversion' do
5
+ it 'should take major minor and patch segments on initialization' do
6
+ described_class.new('1', '2', '3').to_s.should == '1.2.3'
7
+ end
8
+ it 'should optionally take a prerelease' do
9
+ described_class.new('1', '2', '3', Prerelease.new('alpha', 1)).to_s.should == '1.2.3-alpha.1'
10
+ end
11
+ describe 'should take a build number' do
12
+ it do
13
+ described_class.new('1', '2', '3', nil, '2').to_s.should == '1.2.3+build.2'
14
+ end
15
+ it 'and a prerelease' do
16
+ described_class.new('1', '2', '3', Prerelease.new('beta', 3), '2').to_s.should == '1.2.3-beta.3+build.2'
17
+ end
18
+ end
19
+ describe '#bump!' do
20
+ subject { described_class.new('1', '2', '3', Prerelease.new('beta', 3), '2') }
21
+ describe 'should bump' do
22
+ it 'major' do
23
+ subject.bump!(:major).to_s.should == '2.0.0'
24
+ end
25
+ it 'minor' do
26
+ subject.bump!(:minor).to_s.should == '1.3.0'
27
+ end
28
+ it 'patch' do
29
+ subject.bump!(:patch).to_s.should == '1.2.4'
30
+ end
31
+ describe 'prerelease' do
32
+ describe 'with explicit type' do
33
+ it do
34
+ subject.bump!(:prerelease, 'someth').to_s.should == '1.2.3-someth.1'
35
+ end
36
+ it 'with no prerelease on previous version' do
37
+ subject.prerelease = nil
38
+ subject.bump!(:prerelease, 'someth').to_s.should == '1.2.4-someth.1'
39
+ end
40
+ end
41
+ describe 'with default type' do
42
+ it do
43
+ subject.bump!(:prerelease).to_s.should == '1.2.3-beta.4'
44
+ end
45
+ it 'with no prerelease on previous version' do
46
+ subject.prerelease = nil
47
+ subject.bump!(:prerelease).to_s.should == '1.2.4-alpha.1'
48
+ end
49
+ end
50
+ end
51
+ it 'build' do
52
+ subject.bump!(:build).to_s.should == '1.2.3-beta.3+build.3'
53
+ end
54
+ end
55
+ end
56
+ describe '#from_tag should create a version object from a tag' do
57
+ it do
58
+ v = described_class.from_tag('1.2.3')
59
+ v.major.should == 1
60
+ v.minor.should == 2
61
+ v.patch.should == 3
62
+ v.prerelease.should == nil
63
+ end
64
+ it 'with a prerelease' do
65
+ v = described_class.from_tag('1.2.3-alpha.1')
66
+ v.major.should == 1
67
+ v.minor.should == 2
68
+ v.patch.should == 3
69
+ v.prerelease.should == Prerelease.new('alpha', 1)
70
+ end
71
+ end
72
+ describe "#reset_for" do
73
+ subject { described_class.new('1', '2', '3', Prerelease.new, '4') }
74
+ [[:major, '1.0.0'],
75
+ [:minor, '1.2.0'],
76
+ [:patch, '1.2.3'],
77
+ [:prerelease, '1.2.3-alpha.1'],
78
+ [:build, '1.2.3-alpha.1+build.4']].each do |type, result|
79
+ it "should reset for #{type}" do
80
+ subject.reset_for(type).to_s.should == result
81
+ end
82
+ end
83
+ end
84
+ end
85
+ describe ScmVersion do
86
+ describe "::VERSION_FORMAT should " do
87
+ %w[1.2.3 1.2.3-alpha.1 1.2.3+build.2 1.2.45-alpha.3+build.52 1.0.0-alpha.3+build.2].each do |example|
88
+ it "match #{example}" do
89
+ example.match(described_class::VERSION_FORMAT).should_not be_nil
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ describe GitVersion do
96
+ it_behaves_like 'scmversion'
97
+ end
98
+
99
+ describe Perforce do
100
+ it 'should set environment variables' do
101
+ described_class.stub(:set) { <<-here }
102
+ P4CHARSET=utf8\nP4CLIENT=kallan_mac (config)
103
+ P4CONFIG=p4config (config '/Users/kallan/src/kallan_mac/p4config')
104
+ P4PORT=perflax01:1666 (config)
105
+ P4USER=kallan
106
+ here
107
+
108
+ described_class.parse_and_set_p4_set
109
+
110
+ ENV["P4CHARSET"].should == "utf8"
111
+ ENV["P4CONFIG"].should == "p4config"
112
+ ENV["P4PORT"].should == "perflax01:1666"
113
+ ENV["P4USER"].should == "kallan"
114
+ end
115
+ end
116
+
117
+ describe P4Version do
118
+ it_behaves_like 'scmversion'
119
+
120
+ it 'should parse labels correctly' do
121
+ described_class.parse_label("Label testing-1.0.0 2012/10/01 'Created by kallan. '", "testing").should eq(["1","0","0"])
122
+ described_class.parse_label("Label testing-1.0.1 2012/10/01 'Created by kallan. '", "testing").should eq(["1","0","1"])
123
+ described_class.parse_label("Label testing-2.0.5 2012/10/01 'Created by kallan. '", "testing").should eq(["2","0","5"])
124
+ described_class.parse_label("Label testing-4.2.2 2012/10/01 'Created by kallan. '", "testing").should eq(["4","2","2"])
125
+ end
126
+ end
127
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: thor-scmversion
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 1.0.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -11,11 +11,11 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2012-10-08 00:00:00.000000000Z
14
+ date: 2012-10-15 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: thor
18
- requirement: &70281121566680 !ruby/object:Gem::Requirement
18
+ requirement: &70341045418340 !ruby/object:Gem::Requirement
19
19
  none: false
20
20
  requirements:
21
21
  - - ! '>='
@@ -23,10 +23,10 @@ dependencies:
23
23
  version: '0'
24
24
  type: :runtime
25
25
  prerelease: false
26
- version_requirements: *70281121566680
26
+ version_requirements: *70341045418340
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: webmock
29
- requirement: &70281121904800 !ruby/object:Gem::Requirement
29
+ requirement: &70341045417740 !ruby/object:Gem::Requirement
30
30
  none: false
31
31
  requirements:
32
32
  - - ! '>='
@@ -34,10 +34,10 @@ dependencies:
34
34
  version: '0'
35
35
  type: :development
36
36
  prerelease: false
37
- version_requirements: *70281121904800
37
+ version_requirements: *70341045417740
38
38
  - !ruby/object:Gem::Dependency
39
39
  name: geminabox
40
- requirement: &70281122109820 !ruby/object:Gem::Requirement
40
+ requirement: &70341045417140 !ruby/object:Gem::Requirement
41
41
  none: false
42
42
  requirements:
43
43
  - - ! '>='
@@ -45,10 +45,10 @@ dependencies:
45
45
  version: '0'
46
46
  type: :development
47
47
  prerelease: false
48
- version_requirements: *70281122109820
48
+ version_requirements: *70341045417140
49
49
  - !ruby/object:Gem::Dependency
50
50
  name: spork
51
- requirement: &70281122489560 !ruby/object:Gem::Requirement
51
+ requirement: &70341045416600 !ruby/object:Gem::Requirement
52
52
  none: false
53
53
  requirements:
54
54
  - - ! '>='
@@ -56,10 +56,10 @@ dependencies:
56
56
  version: '0'
57
57
  type: :development
58
58
  prerelease: false
59
- version_requirements: *70281122489560
59
+ version_requirements: *70341045416600
60
60
  - !ruby/object:Gem::Dependency
61
61
  name: simplecov
62
- requirement: &70281125870580 !ruby/object:Gem::Requirement
62
+ requirement: &70341045416120 !ruby/object:Gem::Requirement
63
63
  none: false
64
64
  requirements:
65
65
  - - ! '>='
@@ -67,10 +67,10 @@ dependencies:
67
67
  version: '0'
68
68
  type: :development
69
69
  prerelease: false
70
- version_requirements: *70281125870580
70
+ version_requirements: *70341045416120
71
71
  - !ruby/object:Gem::Dependency
72
72
  name: vcr
73
- requirement: &70281126177140 !ruby/object:Gem::Requirement
73
+ requirement: &70341045415700 !ruby/object:Gem::Requirement
74
74
  none: false
75
75
  requirements:
76
76
  - - ! '>='
@@ -78,10 +78,10 @@ dependencies:
78
78
  version: '0'
79
79
  type: :development
80
80
  prerelease: false
81
- version_requirements: *70281126177140
81
+ version_requirements: *70341045415700
82
82
  - !ruby/object:Gem::Dependency
83
83
  name: aruba
84
- requirement: &70281126843440 !ruby/object:Gem::Requirement
84
+ requirement: &70341045415280 !ruby/object:Gem::Requirement
85
85
  none: false
86
86
  requirements:
87
87
  - - ! '>='
@@ -89,10 +89,10 @@ dependencies:
89
89
  version: '0'
90
90
  type: :development
91
91
  prerelease: false
92
- version_requirements: *70281126843440
92
+ version_requirements: *70341045415280
93
93
  - !ruby/object:Gem::Dependency
94
94
  name: rspec
95
- requirement: &70281126962300 !ruby/object:Gem::Requirement
95
+ requirement: &70341045414860 !ruby/object:Gem::Requirement
96
96
  none: false
97
97
  requirements:
98
98
  - - ! '>='
@@ -100,7 +100,7 @@ dependencies:
100
100
  version: '0'
101
101
  type: :development
102
102
  prerelease: false
103
- version_requirements: *70281126962300
103
+ version_requirements: *70341045414860
104
104
  description: Thor tasks to manage a VERSION file based on SCM tags
105
105
  email:
106
106
  - ivey@gweezlebur.com
@@ -124,13 +124,16 @@ files:
124
124
  - features/step_definitions/bump_steps.rb
125
125
  - features/support/env.rb
126
126
  - lib/thor-scmversion.rb
127
+ - lib/thor-scmversion/errors.rb
127
128
  - lib/thor-scmversion/git_version.rb
128
129
  - lib/thor-scmversion/p4_version.rb
130
+ - lib/thor-scmversion/prerelease.rb
129
131
  - lib/thor-scmversion/scm_version.rb
130
132
  - lib/thor-scmversion/shell_utils.rb
131
133
  - lib/thor-scmversion/version.rb
132
134
  - lib/thor/scmversion.rb
133
- - spec/lib/scm_version_spec.rb
135
+ - spec/lib/thor-scmversion/prerelease_spec.rb
136
+ - spec/lib/thor-scmversion/scm_version_spec.rb
134
137
  - spec/spec_helper.rb
135
138
  - thor-scmversion.gemspec
136
139
  homepage: ''
@@ -153,7 +156,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
153
156
  version: '0'
154
157
  requirements: []
155
158
  rubyforge_project:
156
- rubygems_version: 1.8.10
159
+ rubygems_version: 1.8.11
157
160
  signing_key:
158
161
  specification_version: 3
159
162
  summary: A small set of Thor tasks you can include in your build scripts to manage
@@ -165,6 +168,7 @@ test_files:
165
168
  - features/fixtures/Thorfile
166
169
  - features/step_definitions/bump_steps.rb
167
170
  - features/support/env.rb
168
- - spec/lib/scm_version_spec.rb
171
+ - spec/lib/thor-scmversion/prerelease_spec.rb
172
+ - spec/lib/thor-scmversion/scm_version_spec.rb
169
173
  - spec/spec_helper.rb
170
174
  has_rdoc:
@@ -1,36 +0,0 @@
1
- require 'spec_helper'
2
-
3
- module ThorSCMVersion
4
- describe GitVersion do
5
- it 'should take major minor and patch segments on initialization' do
6
- -> { described_class.new('1', '2', '3') }.should_not raise_error
7
- end
8
- end
9
-
10
- describe Perforce do
11
- it 'should set good' do
12
- described_class.stub(:set) { <<-here }
13
- P4CHARSET=utf8\nP4CLIENT=kallan_mac (config)
14
- P4CONFIG=p4config (config '/Users/kallan/src/kallan_mac/p4config')
15
- P4PORT=perflax01:1666 (config)
16
- P4USER=kallan
17
- here
18
-
19
- described_class.parse_and_set_p4_set
20
-
21
- ENV["P4CHARSET"].should == "utf8"
22
- ENV["P4CONFIG"].should == "p4config"
23
- ENV["P4PORT"].should == "perflax01:1666"
24
- ENV["P4USER"].should == "kallan"
25
- end
26
- end
27
-
28
- describe P4Version do
29
- it 'should parse labels correctly' do
30
- described_class.parse_label("Label testing-1.0.0 2012/10/01 'Created by kallan. '", "testing").should eq(["1","0","0"])
31
- described_class.parse_label("Label testing-1.0.1 2012/10/01 'Created by kallan. '", "testing").should eq(["1","0","1"])
32
- described_class.parse_label("Label testing-2.0.5 2012/10/01 'Created by kallan. '", "testing").should eq(["2","0","5"])
33
- described_class.parse_label("Label testing-4.2.2 2012/10/01 'Created by kallan. '", "testing").should eq(["4","2","2"])
34
- end
35
- end
36
- end