semmy 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/CHANGELOG.md +7 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +108 -0
- data/Rakefile +9 -0
- data/bin/console +14 -0
- data/bin/rake +17 -0
- data/bin/rspec +17 -0
- data/bin/setup +7 -0
- data/lib/semmy.rb +18 -0
- data/lib/semmy/changelog.rb +140 -0
- data/lib/semmy/configuration.rb +41 -0
- data/lib/semmy/doc_tags.rb +10 -0
- data/lib/semmy/error.rb +4 -0
- data/lib/semmy/files.rb +19 -0
- data/lib/semmy/gemspec.rb +26 -0
- data/lib/semmy/project.rb +15 -0
- data/lib/semmy/scm.rb +23 -0
- data/lib/semmy/shell.rb +27 -0
- data/lib/semmy/tasks.rb +69 -0
- data/lib/semmy/tasks/base.rb +19 -0
- data/lib/semmy/tasks/branches.rb +24 -0
- data/lib/semmy/tasks/changelog_sections.rb +34 -0
- data/lib/semmy/tasks/commit.rb +31 -0
- data/lib/semmy/tasks/docs.rb +20 -0
- data/lib/semmy/tasks/versioning.rb +35 -0
- data/lib/semmy/version.rb +3 -0
- data/lib/semmy/version_file.rb +37 -0
- data/lib/semmy/version_string.rb +60 -0
- data/semmy.gemspec +28 -0
- metadata +162 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ea4c24f886e54232fcab730802a1856eea3dc465
|
4
|
+
data.tar.gz: f188adf4a9d562ff9ab8554d5c1c1e71e3b25454
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: cad40bf070fe5fb65a8cc060ee34d0144935ea405d82828d5befc7f58b2ca9009ee9529575d92d671fc8426587b2d34741303e5900235c4724c888f70b8b1c1d
|
7
|
+
data.tar.gz: edd2e7e8c779b77ba615c6e7d50b1ef80bea8cd99989d9914a34caff12e0dc87af67c0f733aee6a38ced1a6c5469c1fdfb4cdf09cc30d27f24a5006de7ff897d
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
4
|
+
|
5
|
+
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
|
6
|
+
|
7
|
+
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
8
|
+
|
9
|
+
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
|
10
|
+
|
11
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
|
12
|
+
|
13
|
+
This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Tim Fischbach
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
# Semmy
|
2
|
+
|
3
|
+
An opinionated set of rake tasks to maintain gems following semantic
|
4
|
+
versioning principles.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add development dependency to your gemspec:
|
9
|
+
|
10
|
+
# your.gemspec
|
11
|
+
s.add_development_dependency "semmy", "~> 1.0"
|
12
|
+
|
13
|
+
Install gems:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Add the tasks to your Rakefile:
|
18
|
+
|
19
|
+
# Rakefile
|
20
|
+
require 'semmy'
|
21
|
+
|
22
|
+
Semmy::Tasks.install
|
23
|
+
|
24
|
+
## Usage
|
25
|
+
|
26
|
+
Semmy defines a new task to prepare a release:
|
27
|
+
|
28
|
+
$ rake release:prepare
|
29
|
+
|
30
|
+
This task:
|
31
|
+
|
32
|
+
* Removes the `dev` version suffix from the version file
|
33
|
+
* Rewrites doc tags
|
34
|
+
* Closes the section of the new version in the changelog.
|
35
|
+
* Commits the changes
|
36
|
+
|
37
|
+
It is expected that a `release` task exists. Normally this tasks is
|
38
|
+
provided by Bundler.
|
39
|
+
|
40
|
+
$ rake release
|
41
|
+
|
42
|
+
Semmy registers additional actions which shall be
|
43
|
+
executed right after the release:
|
44
|
+
|
45
|
+
* Creates a stable branch
|
46
|
+
* Bumps the version to the next minor version with `alpha` version
|
47
|
+
suffix.
|
48
|
+
* Inserts an "Changes on master" section in the changelog.
|
49
|
+
|
50
|
+
## Assumptions
|
51
|
+
|
52
|
+
### Git Branches and Tags
|
53
|
+
|
54
|
+
Development happens directly on `master` or by merging pull
|
55
|
+
requests. When a release is made, a stable branch called `x-y-stable`
|
56
|
+
is created. Semmy relies on Bundler's `release` task to create version
|
57
|
+
tags.
|
58
|
+
|
59
|
+
Patch level versions are released via backports to the stable
|
60
|
+
branches.
|
61
|
+
|
62
|
+
### Version Suffix
|
63
|
+
|
64
|
+
The version in the gem's version file is increased in a separate
|
65
|
+
commit right after release. The version always has an `alpha` suffix
|
66
|
+
(i.e. `1.1.0.alpha`), which is only removed in the last commit
|
67
|
+
preparing the release. That way it is easy to see in a project's
|
68
|
+
`Gemfile.lock` whether an unreleased version is used.
|
69
|
+
|
70
|
+
Patch level versions are expected to be released immediately after
|
71
|
+
backporting bug fixes. So there are never commits with a version
|
72
|
+
suffix on stable branches.
|
73
|
+
|
74
|
+
### Doc Tags
|
75
|
+
|
76
|
+
Pull requests introducing new features, are expected to markup new
|
77
|
+
code elements with a `@since edge` doc tag. When a release is
|
78
|
+
prepared, `edge` is replaced with the current version string. That way
|
79
|
+
pull request authors do not have to guess, which version will merge
|
80
|
+
their commits.
|
81
|
+
|
82
|
+
### Changelog
|
83
|
+
|
84
|
+
Unreleased changes are listed in a section at the top. When preparing
|
85
|
+
a release this section is closed by placing a version heading above it
|
86
|
+
and inserting a compare link. Changelog entries for patch level
|
87
|
+
versions are only committed on the stable branches since they only
|
88
|
+
backport bug fixes from master.
|
89
|
+
|
90
|
+
## Example Life Cycle
|
91
|
+
|
92
|
+
### Releasing a Minor Version
|
93
|
+
|
94
|
+
1: * (master) Other important bug fix
|
95
|
+
2: * Minor bug fix
|
96
|
+
3: * First new feature
|
97
|
+
4: * Important bug fix
|
98
|
+
5: * Begin work on 1.1
|
99
|
+
6: | * (1-0-stable, v1.0.2) Prepare 1.0.2 release
|
100
|
+
7: | * Backport of other bug fix
|
101
|
+
8: | * (v1.0.1) Prepare 1.0.1 release
|
102
|
+
9: | * Backport of important bug fix
|
103
|
+
10: |/
|
104
|
+
11: * (v1.0.0) Prepare 1.0.0 release
|
105
|
+
|
106
|
+
### Releasing a Patch Level Version
|
107
|
+
|
108
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "semmy"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/rake
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
#
|
4
|
+
# This file was generated by Bundler.
|
5
|
+
#
|
6
|
+
# The application 'rake' is installed as part of a gem, and
|
7
|
+
# this file is here to facilitate running it.
|
8
|
+
#
|
9
|
+
|
10
|
+
require "pathname"
|
11
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
12
|
+
Pathname.new(__FILE__).realpath)
|
13
|
+
|
14
|
+
require "rubygems"
|
15
|
+
require "bundler/setup"
|
16
|
+
|
17
|
+
load Gem.bin_path("rake", "rake")
|
data/bin/rspec
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
#
|
4
|
+
# This file was generated by Bundler.
|
5
|
+
#
|
6
|
+
# The application 'rspec' is installed as part of a gem, and
|
7
|
+
# this file is here to facilitate running it.
|
8
|
+
#
|
9
|
+
|
10
|
+
require "pathname"
|
11
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
12
|
+
Pathname.new(__FILE__).realpath)
|
13
|
+
|
14
|
+
require "rubygems"
|
15
|
+
require "bundler/setup"
|
16
|
+
|
17
|
+
load Gem.bin_path("rspec-core", "rspec")
|
data/bin/setup
ADDED
data/lib/semmy.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'semmy/error'
|
2
|
+
|
3
|
+
require 'semmy/changelog'
|
4
|
+
require 'semmy/configuration'
|
5
|
+
require 'semmy/doc_tags'
|
6
|
+
require 'semmy/files'
|
7
|
+
require 'semmy/gemspec'
|
8
|
+
require 'semmy/project'
|
9
|
+
require 'semmy/scm'
|
10
|
+
require 'semmy/shell'
|
11
|
+
require 'semmy/tasks'
|
12
|
+
require 'semmy/version'
|
13
|
+
require 'semmy/version_file'
|
14
|
+
require 'semmy/version_string'
|
15
|
+
|
16
|
+
module Semmy
|
17
|
+
# Your code goes here...
|
18
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'unindent'
|
2
|
+
|
3
|
+
module Semmy
|
4
|
+
module Changelog
|
5
|
+
extend self
|
6
|
+
|
7
|
+
class InsertPointNotFound < Error; end
|
8
|
+
|
9
|
+
CloseSection = Struct.new(:config, :options) do
|
10
|
+
def call(contents)
|
11
|
+
contents.dup.tap do |result|
|
12
|
+
result.gsub!(unreleased_section_matcher,
|
13
|
+
version_header) ||
|
14
|
+
fail(InsertPointNotFound, <<-END.unindent)
|
15
|
+
Could not find insert point for section heading in:
|
16
|
+
|
17
|
+
#{contents}
|
18
|
+
END
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def unreleased_section_matcher
|
25
|
+
/#{config.changelog_unrelased_section_heading}(\s*#{compare_link_matcher})?/
|
26
|
+
end
|
27
|
+
|
28
|
+
def compare_link_matcher
|
29
|
+
# match markdown link of the form [...](...)
|
30
|
+
/\[[^\]]+\]\([^)]+\)/
|
31
|
+
end
|
32
|
+
|
33
|
+
def version_header
|
34
|
+
<<-END.unindent.chomp
|
35
|
+
#{version_heading}
|
36
|
+
|
37
|
+
#{current_date}
|
38
|
+
|
39
|
+
#{compare_link_for_versions}
|
40
|
+
END
|
41
|
+
end
|
42
|
+
|
43
|
+
def version_heading
|
44
|
+
config.changelog_version_section_heading % {
|
45
|
+
version: options[:new_version]
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
def current_date
|
50
|
+
options[:date].strftime('%Y-%m-%d')
|
51
|
+
end
|
52
|
+
|
53
|
+
def compare_link_for_versions
|
54
|
+
Changelog.compare_link(config,
|
55
|
+
homepage: options[:homepage],
|
56
|
+
old_version_tag: old_version_tag,
|
57
|
+
new_version_tag: new_version_tag)
|
58
|
+
end
|
59
|
+
|
60
|
+
def old_version_tag
|
61
|
+
Changelog.version_tag(options[:old_version])
|
62
|
+
end
|
63
|
+
|
64
|
+
def new_version_tag
|
65
|
+
Changelog.version_tag(options[:new_version])
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
InsertUnreleasedSection = Struct.new(:config, :options) do
|
70
|
+
def call(contents)
|
71
|
+
insert_before(version_line_matcher,
|
72
|
+
contents,
|
73
|
+
unreleased_section(contents))
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def unreleased_section(contents)
|
79
|
+
<<-END.unindent
|
80
|
+
#{config.changelog_unrelased_section_heading}
|
81
|
+
|
82
|
+
#{compare_link_for_master(contents)}
|
83
|
+
|
84
|
+
#{config.changelog_unrelased_section_blank_slate}
|
85
|
+
END
|
86
|
+
end
|
87
|
+
|
88
|
+
def compare_link_for_master(contents)
|
89
|
+
Changelog.compare_link(config,
|
90
|
+
homepage: options[:homepage],
|
91
|
+
old_version_tag: last_version_tag(contents),
|
92
|
+
new_version_tag: 'master')
|
93
|
+
end
|
94
|
+
|
95
|
+
def last_version_tag(contents)
|
96
|
+
match = contents.match(version_line_matcher)
|
97
|
+
match && Changelog.version_tag(match[1])
|
98
|
+
end
|
99
|
+
|
100
|
+
def version_line_matcher
|
101
|
+
Regexp.new(config.changelog_version_section_heading % {
|
102
|
+
version: '([0-9.]+)'
|
103
|
+
})
|
104
|
+
end
|
105
|
+
|
106
|
+
def insert_before(line_matcher, text, inserted_text)
|
107
|
+
text.dup.tap do |result|
|
108
|
+
unless (result.sub!(line_matcher, inserted_text + "\n\\0"))
|
109
|
+
fail(InsertPointNotFound,
|
110
|
+
"Insert point not found.")
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def version_tag(version)
|
117
|
+
"v#{version}"
|
118
|
+
end
|
119
|
+
|
120
|
+
def compare_link(config, interpolations)
|
121
|
+
"[Compare changes](#{compare_url(config, interpolations)})"
|
122
|
+
end
|
123
|
+
|
124
|
+
def compare_url(config, interpolations)
|
125
|
+
config.changelog_compare_url % interpolations
|
126
|
+
.merge(repository: repository_url(config,
|
127
|
+
interpolations[:homepage]))
|
128
|
+
end
|
129
|
+
|
130
|
+
private
|
131
|
+
|
132
|
+
def repository_url(config, homepage)
|
133
|
+
if config.github_repository
|
134
|
+
"https://github.com/#{config.github_repository}"
|
135
|
+
else
|
136
|
+
homepage
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Semmy
|
2
|
+
class Configuration
|
3
|
+
attr_accessor :development_version_suffix
|
4
|
+
|
5
|
+
attr_accessor :stable_branch_name
|
6
|
+
|
7
|
+
attr_accessor :prepare_commit_message
|
8
|
+
attr_accessor :bump_commit_message
|
9
|
+
|
10
|
+
attr_accessor :github_repository
|
11
|
+
|
12
|
+
attr_accessor :changelog_path
|
13
|
+
attr_accessor :changelog_version_section_heading
|
14
|
+
attr_accessor :changelog_compare_url
|
15
|
+
attr_accessor :changelog_unrelased_section_heading
|
16
|
+
attr_accessor :changelog_unrelased_section_blank_slate
|
17
|
+
|
18
|
+
attr_accessor :source_files_with_docs_tags
|
19
|
+
attr_accessor :rewritten_since_doc_tag
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
@development_version_suffix = 'dev'
|
23
|
+
|
24
|
+
@stable_branch_name = '%{major}-%{minor}-stable'
|
25
|
+
|
26
|
+
@prepare_commit_message = 'Prepare %{version} Release'
|
27
|
+
@bump_commit_message = 'Bump Version to %{version}'
|
28
|
+
|
29
|
+
@changelog_path = 'CHANGELOG.md'
|
30
|
+
@changelog_version_section_heading = '### Version %{version}'
|
31
|
+
@changelog_compare_url = '%{repository}/compare/%{old_version_tag}..%{new_version_tag}'
|
32
|
+
@changelog_unrelased_section_heading = '### Changes on `master`'
|
33
|
+
@changelog_unrelased_section_blank_slate = 'None so far.'
|
34
|
+
|
35
|
+
@source_files_with_docs_tags = '{app,lib}/**/*.{js,rb,scss}'
|
36
|
+
@rewritten_since_doc_tag = 'edge'
|
37
|
+
|
38
|
+
yield self if block_given?
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/semmy/error.rb
ADDED
data/lib/semmy/files.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
module Semmy
|
2
|
+
module Files
|
3
|
+
module_function
|
4
|
+
|
5
|
+
def rewrite(path, update)
|
6
|
+
content = File.binread(path)
|
7
|
+
|
8
|
+
File.open(path, 'wb') do |file|
|
9
|
+
file.write(update.call(content))
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def rewrite_all(glob, update)
|
14
|
+
Dir.glob(glob) do |path|
|
15
|
+
rewrite(path, update)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Semmy
|
2
|
+
module Gemspec
|
3
|
+
extend self
|
4
|
+
|
5
|
+
class NotFound < Error; end
|
6
|
+
|
7
|
+
def gem_name
|
8
|
+
specification.name
|
9
|
+
end
|
10
|
+
|
11
|
+
def homepage
|
12
|
+
specification.homepage
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def specification
|
18
|
+
Gem::Specification.load(path)
|
19
|
+
end
|
20
|
+
|
21
|
+
def path
|
22
|
+
Dir.glob('*.gemspec').first ||
|
23
|
+
fail(NotFound, 'Gemspec not found.')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/semmy/scm.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
module Semmy
|
2
|
+
module Scm
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def on_master?
|
6
|
+
git.current_branch == 'master'
|
7
|
+
end
|
8
|
+
|
9
|
+
def on_stable?(stable_branch_name)
|
10
|
+
!!git.current_branch.match(stable_branch_matcher(stable_branch_name))
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def stable_branch_matcher(stable_branch_name)
|
16
|
+
Regexp.new(p(stable_branch_name.gsub(/%\{\w+\}/, '[0-9]+')))
|
17
|
+
end
|
18
|
+
|
19
|
+
def git
|
20
|
+
Git.open('.')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/semmy/shell.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'rainbow'
|
2
|
+
|
3
|
+
module Semmy
|
4
|
+
module Shell
|
5
|
+
extend self
|
6
|
+
|
7
|
+
attr_accessor :silence
|
8
|
+
|
9
|
+
def info(text)
|
10
|
+
say(text, :green)
|
11
|
+
end
|
12
|
+
|
13
|
+
def warn(text)
|
14
|
+
say(text, :yellow)
|
15
|
+
end
|
16
|
+
|
17
|
+
def error(text)
|
18
|
+
say(text, :red)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def say(text, color)
|
24
|
+
puts(Rainbow(text).color(color)) unless silence
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/semmy/tasks.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'rake'
|
2
|
+
|
3
|
+
require 'semmy/tasks/base'
|
4
|
+
require 'semmy/tasks/branches'
|
5
|
+
require 'semmy/tasks/changelog_sections'
|
6
|
+
require 'semmy/tasks/commit'
|
7
|
+
require 'semmy/tasks/docs'
|
8
|
+
require 'semmy/tasks/versioning'
|
9
|
+
|
10
|
+
module Semmy
|
11
|
+
module Tasks
|
12
|
+
include Rake::DSL
|
13
|
+
extend self
|
14
|
+
|
15
|
+
def install
|
16
|
+
config = Configuration.new
|
17
|
+
yield config if block_given?
|
18
|
+
|
19
|
+
namespace 'semmy' do
|
20
|
+
Versioning.new(config)
|
21
|
+
Docs.new(config)
|
22
|
+
ChangelogSections.new(config)
|
23
|
+
Commit.new(config)
|
24
|
+
Branches.new(config)
|
25
|
+
end
|
26
|
+
|
27
|
+
desc 'Prepare minor or major release'
|
28
|
+
task 'release:prepare:master' => [
|
29
|
+
'semmy:versioning:remove_development_version_suffix',
|
30
|
+
'semmy:docs:rewrite_since_tags',
|
31
|
+
'semmy:changelog:close_section',
|
32
|
+
'semmy:commit:prepare'
|
33
|
+
]
|
34
|
+
|
35
|
+
desc 'Prepare patch level release'
|
36
|
+
task 'release:prepare:stable' => [
|
37
|
+
'semmy:changelog:close_section',
|
38
|
+
'semmy:commit:prepare'
|
39
|
+
]
|
40
|
+
|
41
|
+
desc 'Prepare release'
|
42
|
+
task 'release:prepare' do
|
43
|
+
if Scm.on_master?
|
44
|
+
Rake.application['release:prepare:master'].invoke
|
45
|
+
elsif Scm.on_stable?(config.stable_branch_name)
|
46
|
+
Rake.application['release:prepare:stable'].invoke
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
task 'release:after:master' => [
|
51
|
+
'semmy:branches:create_stable',
|
52
|
+
'semmy:versioning:bump_minor',
|
53
|
+
'semmy:changelog:add_unreleased_section',
|
54
|
+
'semmy:commit:bump'
|
55
|
+
]
|
56
|
+
|
57
|
+
desc 'Prepare repository for development of next verion'
|
58
|
+
task 'release:after' do
|
59
|
+
if Scm.on_master?
|
60
|
+
Rake.application['release:after:master'].invoke
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
task 'release' do
|
65
|
+
Rake.application['release:after'].invoke
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Semmy
|
2
|
+
module Tasks
|
3
|
+
class Base
|
4
|
+
include Rake::DSL
|
5
|
+
|
6
|
+
attr_reader :config
|
7
|
+
|
8
|
+
def initialize(config = nil)
|
9
|
+
@config = config || Configuration.new
|
10
|
+
yield(@config) if block_given?
|
11
|
+
|
12
|
+
define
|
13
|
+
end
|
14
|
+
|
15
|
+
def define
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'git'
|
2
|
+
|
3
|
+
module Semmy
|
4
|
+
module Tasks
|
5
|
+
class Branches < Base
|
6
|
+
def define
|
7
|
+
namespace 'branches' do
|
8
|
+
task 'create_stable' do
|
9
|
+
name = config.stable_branch_name %
|
10
|
+
VersionString.components(Project.version)
|
11
|
+
|
12
|
+
Shell.info("Creating stable branch #{name}...")
|
13
|
+
|
14
|
+
git.branch(name).create
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def git
|
19
|
+
Git.open('.')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Semmy
|
2
|
+
module Tasks
|
3
|
+
class ChangelogSections < Base
|
4
|
+
def define
|
5
|
+
namespace 'changelog' do
|
6
|
+
task 'close_section' do
|
7
|
+
new_version = Project.version
|
8
|
+
old_version = VersionString.previous_minor(new_version)
|
9
|
+
homepage = Gemspec.homepage
|
10
|
+
|
11
|
+
Shell.info("Inserting #{new_version} section " \
|
12
|
+
"in #{config.changelog_path}...")
|
13
|
+
|
14
|
+
Files.rewrite(config.changelog_path,
|
15
|
+
Changelog::CloseSection.new(config,
|
16
|
+
new_version: new_version,
|
17
|
+
old_version: old_version,
|
18
|
+
homepage: homepage,
|
19
|
+
date: Date.today))
|
20
|
+
end
|
21
|
+
|
22
|
+
task 'add_unreleased_section' do
|
23
|
+
Shell.info('Inserting unreleased section ' \
|
24
|
+
"in #{config.changelog_path}...")
|
25
|
+
|
26
|
+
Files.rewrite(config.changelog_path,
|
27
|
+
Changelog::InsertUnreleasedSection.new(config,
|
28
|
+
homepage: Gemspec.homepage))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'git'
|
2
|
+
|
3
|
+
module Semmy
|
4
|
+
module Tasks
|
5
|
+
class Commit < Base
|
6
|
+
def define
|
7
|
+
namespace 'commit' do
|
8
|
+
task 'prepare' do
|
9
|
+
Shell.info('Creating prepare commit...')
|
10
|
+
|
11
|
+
git.commit_all(config.prepare_commit_message % {
|
12
|
+
version: Project.version
|
13
|
+
})
|
14
|
+
end
|
15
|
+
|
16
|
+
task 'bump' do
|
17
|
+
Shell.info('Creating bump commit...')
|
18
|
+
|
19
|
+
git.commit_all(config.bump_commit_message % {
|
20
|
+
version: Project.version
|
21
|
+
})
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def git
|
26
|
+
Git.open('.')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'git'
|
2
|
+
|
3
|
+
module Semmy
|
4
|
+
module Tasks
|
5
|
+
class Docs < Base
|
6
|
+
def define
|
7
|
+
namespace 'docs' do
|
8
|
+
task 'rewrite_since_tags' do
|
9
|
+
Shell.info("Rewriting @since #{config.rewritten_since_doc_tag} " \
|
10
|
+
"doc tags in #{config.source_files_with_docs_tags}...")
|
11
|
+
|
12
|
+
Files.rewrite_all(config.source_files_with_docs_tags,
|
13
|
+
DocTags::UpdateSinceTags.new(config.rewritten_since_doc_tag,
|
14
|
+
Project.version))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Semmy
|
2
|
+
module Tasks
|
3
|
+
class Versioning < Base
|
4
|
+
def define
|
5
|
+
namespace 'versioning' do
|
6
|
+
task 'remove_development_version_suffix' do
|
7
|
+
new_version = VersionString
|
8
|
+
.remove_suffix(Project.version, config.development_version_suffix)
|
9
|
+
|
10
|
+
Shell.info("Removing #{config.development_version_suffix} suffix " \
|
11
|
+
'from version.')
|
12
|
+
|
13
|
+
rewrite_gemspec_version(new_version)
|
14
|
+
end
|
15
|
+
|
16
|
+
task 'bump_minor' do
|
17
|
+
new_version = VersionString
|
18
|
+
.bump_minor(Project.version, config.development_version_suffix)
|
19
|
+
|
20
|
+
Shell.info("Bumping version to #{new_version}.")
|
21
|
+
|
22
|
+
rewrite_gemspec_version(new_version)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def rewrite_gemspec_version(new_version)
|
30
|
+
Files.rewrite(VersionFile.find(Gemspec.gem_name),
|
31
|
+
VersionFile::Update.new(new_version))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Semmy
|
2
|
+
module VersionFile
|
3
|
+
extend self
|
4
|
+
|
5
|
+
class NotFound < Error; end
|
6
|
+
|
7
|
+
class ConstantNotFound < Error; end
|
8
|
+
|
9
|
+
class UpdateFailed < Error; end
|
10
|
+
|
11
|
+
def find(gem_name)
|
12
|
+
gem_name_matcher = gem_name.gsub(/[_-]/, '[/_-]')
|
13
|
+
|
14
|
+
Dir.glob('lib/**/version.rb').detect do |file_name|
|
15
|
+
file_name =~ %r{lib/#{gem_name_matcher}/version.rb}
|
16
|
+
end || fail(NotFound, 'No version file found.')
|
17
|
+
end
|
18
|
+
|
19
|
+
def parse_version(contents)
|
20
|
+
match = contents.match(/VERSION\s*=\s*['"]([^'"]+)['"]/) ||
|
21
|
+
fail(ConstantNotFound, 'Could not find version constant')
|
22
|
+
|
23
|
+
match[1]
|
24
|
+
end
|
25
|
+
|
26
|
+
Update = Struct.new(:new_version) do
|
27
|
+
def call(contents)
|
28
|
+
contents.dup.tap do |result|
|
29
|
+
result.gsub!(/VERSION\s*=\s*(['"])[^']+['"]/,
|
30
|
+
"VERSION = \\1#{new_version}\\1") ||
|
31
|
+
fail(UpdateFailed,
|
32
|
+
"Could not update version to #{new_version} in\n\n#{contents}\n\n")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Semmy
|
2
|
+
module VersionString
|
3
|
+
extend self
|
4
|
+
|
5
|
+
class SuffixNotFound < Error; end
|
6
|
+
|
7
|
+
class UnexpectedSuffix < Error; end
|
8
|
+
|
9
|
+
class NoPreviousMinor < Error; end
|
10
|
+
|
11
|
+
def remove_suffix(version, suffix)
|
12
|
+
new_version = version.dup
|
13
|
+
|
14
|
+
unless new_version.gsub!(/.#{suffix}$/, '')
|
15
|
+
fail(SuffixNotFound, "Suffix #{suffix} not found in #{version}")
|
16
|
+
end
|
17
|
+
|
18
|
+
new_version
|
19
|
+
end
|
20
|
+
|
21
|
+
def bump_minor(version, suffix)
|
22
|
+
components = version.split('.')
|
23
|
+
|
24
|
+
unless components.last =~ /^[0-9]+$/
|
25
|
+
fail(UnexpectedSuffix, "Expected a version without suffix, found #{version}.")
|
26
|
+
end
|
27
|
+
|
28
|
+
components.map!(&:to_i)
|
29
|
+
components[1] += 1
|
30
|
+
components << suffix
|
31
|
+
|
32
|
+
components.join('.')
|
33
|
+
end
|
34
|
+
|
35
|
+
def previous_minor(version)
|
36
|
+
components = version.split('.').map(&:to_i)
|
37
|
+
|
38
|
+
if components[1] == 0
|
39
|
+
fail(NoPreviousMinor, "Cannot get previous minor of #{version}.")
|
40
|
+
end
|
41
|
+
|
42
|
+
components[1] -= 1
|
43
|
+
components.join('.')
|
44
|
+
end
|
45
|
+
|
46
|
+
def minor_only(version)
|
47
|
+
version.split('.')[0..1].join('.')
|
48
|
+
end
|
49
|
+
|
50
|
+
def components(version)
|
51
|
+
components = version.split('.')
|
52
|
+
|
53
|
+
{
|
54
|
+
major: components[0],
|
55
|
+
minor: components[1],
|
56
|
+
patch: components[2]
|
57
|
+
}
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/semmy.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'semmy/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "semmy"
|
8
|
+
spec.version = Semmy::VERSION
|
9
|
+
spec.authors = ["Tim Fischbach"]
|
10
|
+
spec.email = ["mail@timfischbach.de"]
|
11
|
+
|
12
|
+
spec.summary = 'Rake tasks for a semantic versioning of gems'
|
13
|
+
spec.homepage = 'https://github.com/tf/semmy'
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
|
+
spec.bindir = "exe"
|
18
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency 'git', '~> 1.3'
|
22
|
+
spec.add_dependency 'unindent', '~> 1.0'
|
23
|
+
spec.add_dependency 'rainbow', '~> 2.1'
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
26
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
27
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: semmy
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tim Fischbach
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-06-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: git
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: unindent
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rainbow
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.1'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.1'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bundler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.10'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.10'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '10.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '10.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '3.0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '3.0'
|
97
|
+
description:
|
98
|
+
email:
|
99
|
+
- mail@timfischbach.de
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- ".gitignore"
|
105
|
+
- ".rspec"
|
106
|
+
- ".travis.yml"
|
107
|
+
- CHANGELOG.md
|
108
|
+
- CODE_OF_CONDUCT.md
|
109
|
+
- Gemfile
|
110
|
+
- LICENSE.txt
|
111
|
+
- README.md
|
112
|
+
- Rakefile
|
113
|
+
- bin/console
|
114
|
+
- bin/rake
|
115
|
+
- bin/rspec
|
116
|
+
- bin/setup
|
117
|
+
- lib/semmy.rb
|
118
|
+
- lib/semmy/changelog.rb
|
119
|
+
- lib/semmy/configuration.rb
|
120
|
+
- lib/semmy/doc_tags.rb
|
121
|
+
- lib/semmy/error.rb
|
122
|
+
- lib/semmy/files.rb
|
123
|
+
- lib/semmy/gemspec.rb
|
124
|
+
- lib/semmy/project.rb
|
125
|
+
- lib/semmy/scm.rb
|
126
|
+
- lib/semmy/shell.rb
|
127
|
+
- lib/semmy/tasks.rb
|
128
|
+
- lib/semmy/tasks/base.rb
|
129
|
+
- lib/semmy/tasks/branches.rb
|
130
|
+
- lib/semmy/tasks/changelog_sections.rb
|
131
|
+
- lib/semmy/tasks/commit.rb
|
132
|
+
- lib/semmy/tasks/docs.rb
|
133
|
+
- lib/semmy/tasks/versioning.rb
|
134
|
+
- lib/semmy/version.rb
|
135
|
+
- lib/semmy/version_file.rb
|
136
|
+
- lib/semmy/version_string.rb
|
137
|
+
- semmy.gemspec
|
138
|
+
homepage: https://github.com/tf/semmy
|
139
|
+
licenses:
|
140
|
+
- MIT
|
141
|
+
metadata: {}
|
142
|
+
post_install_message:
|
143
|
+
rdoc_options: []
|
144
|
+
require_paths:
|
145
|
+
- lib
|
146
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
147
|
+
requirements:
|
148
|
+
- - ">="
|
149
|
+
- !ruby/object:Gem::Version
|
150
|
+
version: '0'
|
151
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
152
|
+
requirements:
|
153
|
+
- - ">="
|
154
|
+
- !ruby/object:Gem::Version
|
155
|
+
version: '0'
|
156
|
+
requirements: []
|
157
|
+
rubyforge_project:
|
158
|
+
rubygems_version: 2.2.5
|
159
|
+
signing_key:
|
160
|
+
specification_version: 4
|
161
|
+
summary: Rake tasks for a semantic versioning of gems
|
162
|
+
test_files: []
|