yes_ship_it 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rspec +1 -0
- data/README.md +108 -0
- data/assertions/built_gem.rb +43 -0
- data/assertions/change_log.rb +25 -0
- data/assertions/published_gem.rb +39 -0
- data/assertions/pushed_tag.rb +27 -0
- data/assertions/tag.rb +32 -0
- data/assertions/version.rb +33 -0
- data/bin/yes_ship_it +25 -0
- data/lib/yes_ship_it/assertion.rb +10 -0
- data/lib/yes_ship_it/engine.rb +104 -0
- data/lib/yes_ship_it/exceptions.rb +4 -0
- data/lib/yes_ship_it.rb +9 -0
- data/spec/data/red_herring-000.tar.gz +0 -0
- data/spec/data/red_herring-001.tar.gz +0 -0
- data/spec/data/red_herring-002.tar.gz +0 -0
- data/spec/data/red_herring-003.tar.gz +0 -0
- data/spec/data/yes_ship_it.conf +3 -0
- data/spec/data/yes_ship_it.unknown.conf +4 -0
- data/spec/integration/cli_spec.rb +123 -0
- data/spec/integration/spec_helper.rb +10 -0
- data/spec/unit/assertions_spec.rb +13 -0
- data/spec/unit/engine_spec.rb +68 -0
- data/spec/unit/spec_helper.rb +5 -0
- data/spec/unit/support/assertion_examples.rb +21 -0
- data/yes_ship_it.conf +9 -0
- metadata +114 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 444ecc2191ed065ffeb7d23921a8518897ef5f59
|
4
|
+
data.tar.gz: 41ccaa7257b29964108a58bd27ddd5a9519984b8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e5ab4853e2fd9337988199767d0264046b49f3122c773bda25d15d0f182f9f75caa73fa30764cb6dfa4c53fc72b1dafb52ccdba7ed625a9fafbf2d2599042afb
|
7
|
+
data.tar.gz: 9396162f0020b8ce79e9fc15475010f41b0da38a55c3f677172436a12e1c2d61b3a8d5152a07721688f1f5ab7037b6d9fc78d1a6323d3eb12da132c9a7fb84c4
|
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/README.md
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
# Yes, ship it!
|
2
|
+
|
3
|
+
*The ultimate release script*
|
4
|
+
|
5
|
+
Whenever the answer is "Yes, ship it!" you will need `yes_ship_it`, this tool.
|
6
|
+
It is the ultimate helper in releasing software.
|
7
|
+
|
8
|
+
Shipping software is not an action. It's a state of mind. `yes_ship_it` helps
|
9
|
+
you to make sure that reality matches this state of mind. It won't fix the bugs
|
10
|
+
in your software, it doesn't create the tests which would need to be there, it
|
11
|
+
doesn't write your release notes. It does make sure that your good engineering
|
12
|
+
is delivered to your users with zero cost.
|
13
|
+
|
14
|
+
The approach of `yes_ship_it` is different from the typical release script. It
|
15
|
+
doesn't define a series of steps which are executed to make a release. It
|
16
|
+
defines a sequence of assertions about the release, which then are checked and
|
17
|
+
enforced.
|
18
|
+
|
19
|
+
This works in a way you could describe as idempotent, which means you
|
20
|
+
can execute it as many times as you want, the result will always be the same,
|
21
|
+
regardless of the initial state. That means the whole process is robust against
|
22
|
+
errors which happen during releasing, it transparently copes with manual tweaks
|
23
|
+
and changes, and it works on old releases just as well as on new releases.
|
24
|
+
|
25
|
+
After successfully running `yes_ship_it` your release will have shipped, and the
|
26
|
+
state of the world will reflect the state of your mind.
|
27
|
+
|
28
|
+
## Assumptions
|
29
|
+
|
30
|
+
`yes_ship_it` assumes that you develop your software in a version control
|
31
|
+
system and keep at least some minimal state of releases there. This could just
|
32
|
+
be a tag. It also assumes that you are able to run `yes_ship_it` from the
|
33
|
+
command line and have some access to the systems which are needed to complete
|
34
|
+
the things part of a release.
|
35
|
+
|
36
|
+
## How to run it
|
37
|
+
|
38
|
+
Go to the checkout of the sources of your software. Check that there is a file
|
39
|
+
called `yes_ship_it.conf` there. Run `yes_ship_it`. Done.
|
40
|
+
|
41
|
+
## Configuration
|
42
|
+
|
43
|
+
The central configuration of what happens when you run `yes_ship_it` is the
|
44
|
+
`yes_ship_it.conf` file in the root directory of the sources of your software.
|
45
|
+
There you define the sequence of assertions which are run through for making
|
46
|
+
sure that the release is happening in the way you intend. `yes_ship_it` comes
|
47
|
+
with some predefined sequences of assertions you can simply reuse.
|
48
|
+
|
49
|
+
The format of `yes_ship_it.conf` uses YAML. A minimal `yes_ship_it.conf` file
|
50
|
+
could look like this:
|
51
|
+
|
52
|
+
```yaml
|
53
|
+
include:
|
54
|
+
standard_rubygem
|
55
|
+
```
|
56
|
+
|
57
|
+
A more detailed configuration could look like this:
|
58
|
+
|
59
|
+
```yaml
|
60
|
+
assertions:
|
61
|
+
ci_green
|
62
|
+
release_notes
|
63
|
+
version_number
|
64
|
+
gem_built
|
65
|
+
release_tagged
|
66
|
+
gem_pushed
|
67
|
+
```
|
68
|
+
|
69
|
+
There can be more configuration on a general level or the level of the
|
70
|
+
assertions to adjust to specific needs of each software project. There also can
|
71
|
+
be other or additional assertions.
|
72
|
+
|
73
|
+
## Extending `yes_ship_it`
|
74
|
+
|
75
|
+
Many software projects will have specific needs for their releases. These can
|
76
|
+
be broken down and reflected in additional assertions. Adding assertions is
|
77
|
+
easy. There is a well-defined API for it.
|
78
|
+
|
79
|
+
There is no supported plugin concept. The idea is that all assertions ship with
|
80
|
+
`yes_ship_it`. This will maximize the value for the community of people who ship
|
81
|
+
software. The project is very open to include new assertions. Just submit a pull
|
82
|
+
request. `yes_ship_it` will ship it with the next release.
|
83
|
+
|
84
|
+
## Testing
|
85
|
+
|
86
|
+
Testing a tool such as `yes_ship_it` is not easy because its prime functionality
|
87
|
+
is to interact with other systems and publish data. There are unit tests for the
|
88
|
+
code. There also are integration tests which work in a virtual environment which
|
89
|
+
fakes the services `yes_ship_it` interacts with. It uses the `pennyworth` tool
|
90
|
+
to manage the virtual environment and make them accessible in a convenient way
|
91
|
+
from the RSpec tests.
|
92
|
+
|
93
|
+
## Further documentation
|
94
|
+
|
95
|
+
You get basic documentation from the tool itself by executing
|
96
|
+
`yes_ship_it help`. There also is a simple man page with some pointers.
|
97
|
+
|
98
|
+
The main documentation with details about different scenarios how `yes_ship_it`
|
99
|
+
can be used is maintained in the Wiki. You are welcome to contribute there.
|
100
|
+
|
101
|
+
## License
|
102
|
+
|
103
|
+
`yes_ship_it` is licensed under the MIT license.
|
104
|
+
|
105
|
+
## Contact
|
106
|
+
|
107
|
+
If you have any questions or comments, please don't hesitate to get in touch
|
108
|
+
with me: Cornelius Schumacher <schumacher@kde.org>.
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module YSI
|
2
|
+
class BuiltGem < Assertion
|
3
|
+
attr_accessor :error
|
4
|
+
|
5
|
+
def initialize(engine)
|
6
|
+
@engine = engine
|
7
|
+
end
|
8
|
+
|
9
|
+
def display_name
|
10
|
+
"built gem"
|
11
|
+
end
|
12
|
+
|
13
|
+
def gem_file
|
14
|
+
"#{@engine.project_name}-#{@engine.version}.gem"
|
15
|
+
end
|
16
|
+
|
17
|
+
def gemspec_file
|
18
|
+
"#{@engine.project_name}.gemspec"
|
19
|
+
end
|
20
|
+
|
21
|
+
def check
|
22
|
+
if !File.exist?(gemspec_file)
|
23
|
+
@error = "I need a gemspec: #{gemspec_file}"
|
24
|
+
return nil
|
25
|
+
end
|
26
|
+
|
27
|
+
if !File.exist?(gem_file)
|
28
|
+
return nil
|
29
|
+
end
|
30
|
+
|
31
|
+
gem_file
|
32
|
+
end
|
33
|
+
|
34
|
+
def assert(dry_run: false)
|
35
|
+
if !dry_run
|
36
|
+
if !system("gem build #{gemspec_file}")
|
37
|
+
return nil
|
38
|
+
end
|
39
|
+
end
|
40
|
+
gem_file
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module YSI
|
2
|
+
class ChangeLog < Assertion
|
3
|
+
attr_reader :error
|
4
|
+
|
5
|
+
def initialize(engine)
|
6
|
+
@engine = engine
|
7
|
+
end
|
8
|
+
|
9
|
+
def display_name
|
10
|
+
"change log"
|
11
|
+
end
|
12
|
+
|
13
|
+
def check
|
14
|
+
if !File.exist?("CHANGELOG.md")
|
15
|
+
@error = "Expected change log in CHANGELOG.md"
|
16
|
+
return nil
|
17
|
+
end
|
18
|
+
|
19
|
+
"CHANGELOG.md"
|
20
|
+
end
|
21
|
+
|
22
|
+
def assert(dry_run: false)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module YSI
|
2
|
+
class PublishedGem < Assertion
|
3
|
+
attr_accessor :error
|
4
|
+
|
5
|
+
def initialize(engine)
|
6
|
+
@engine = engine
|
7
|
+
end
|
8
|
+
|
9
|
+
def display_name
|
10
|
+
"published gem"
|
11
|
+
end
|
12
|
+
|
13
|
+
def gem_file
|
14
|
+
"#{@engine.project_name}-#{@engine.version}.gem"
|
15
|
+
end
|
16
|
+
|
17
|
+
def check
|
18
|
+
begin
|
19
|
+
json = RestClient.get("http://rubygems.org/api/v1/versions/#{engine.project_name}.json")
|
20
|
+
rescue RestClient::ResourceNotFound
|
21
|
+
return nil
|
22
|
+
end
|
23
|
+
versions = JSON.parse(json)
|
24
|
+
versions.each do |version|
|
25
|
+
if version["number"] == @engine.version
|
26
|
+
return @engine.version
|
27
|
+
end
|
28
|
+
end
|
29
|
+
nil
|
30
|
+
end
|
31
|
+
|
32
|
+
def assert(dry_run: false)
|
33
|
+
if !dry_run
|
34
|
+
`gem push #{gem_file}`
|
35
|
+
end
|
36
|
+
gem_file
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module YSI
|
2
|
+
class PushedTag < Assertion
|
3
|
+
def display_name
|
4
|
+
"pushed tag"
|
5
|
+
end
|
6
|
+
|
7
|
+
def tag
|
8
|
+
"v#{@engine.version}"
|
9
|
+
end
|
10
|
+
|
11
|
+
def check
|
12
|
+
response = `git ls-remote --tags origin #{tag}`
|
13
|
+
if response.include?(tag)
|
14
|
+
return response
|
15
|
+
end
|
16
|
+
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def assert(dry_run: false)
|
21
|
+
if !dry_run
|
22
|
+
`git push --tags`
|
23
|
+
end
|
24
|
+
tag
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/assertions/tag.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
module YSI
|
2
|
+
class Tag < Assertion
|
3
|
+
def display_name
|
4
|
+
"tag"
|
5
|
+
end
|
6
|
+
|
7
|
+
def tag
|
8
|
+
"v#{@engine.version}"
|
9
|
+
end
|
10
|
+
|
11
|
+
def check
|
12
|
+
`git tag`.each_line do |line|
|
13
|
+
if line.chomp == tag
|
14
|
+
`git show #{tag}`.each_line do |show_line|
|
15
|
+
if show_line =~ /Date:\s+(.*)/
|
16
|
+
@engine.tag_date = $1
|
17
|
+
end
|
18
|
+
end
|
19
|
+
return tag
|
20
|
+
end
|
21
|
+
end
|
22
|
+
return nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def assert(dry_run: false)
|
26
|
+
if !dry_run
|
27
|
+
`git tag -a #{tag} -m "Version #{@engine.version}"`
|
28
|
+
end
|
29
|
+
tag
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module YSI
|
2
|
+
class Version < Assertion
|
3
|
+
attr_accessor :error
|
4
|
+
|
5
|
+
def initialize(engine)
|
6
|
+
@engine = engine
|
7
|
+
end
|
8
|
+
|
9
|
+
def display_name
|
10
|
+
"version number"
|
11
|
+
end
|
12
|
+
|
13
|
+
def check
|
14
|
+
version_file = "lib/version.rb"
|
15
|
+
if !File.exist?(version_file)
|
16
|
+
@error = "Expected version in #{version_file}"
|
17
|
+
return nil
|
18
|
+
end
|
19
|
+
|
20
|
+
File.read(version_file).each_line do |line|
|
21
|
+
if line =~ /VERSION = "(.*)"/
|
22
|
+
@engine.version = $1
|
23
|
+
return @engine.version
|
24
|
+
end
|
25
|
+
end
|
26
|
+
@error = "Couldn't find version in #{version_file}"
|
27
|
+
nil
|
28
|
+
end
|
29
|
+
|
30
|
+
def assert(dry_run: false)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/bin/yes_ship_it
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require_relative("../lib/yes_ship_it.rb")
|
4
|
+
|
5
|
+
puts "Shipping..."
|
6
|
+
puts
|
7
|
+
|
8
|
+
config_file = "yes_ship_it.conf"
|
9
|
+
|
10
|
+
if !File.exist?(config_file)
|
11
|
+
STDERR.puts("Unable to find file `yes_ship_it.conf`. I need it.")
|
12
|
+
exit 1
|
13
|
+
end
|
14
|
+
|
15
|
+
engine = YSI::Engine.new
|
16
|
+
engine.dry_run = ARGV == ["--dry-run"]
|
17
|
+
|
18
|
+
begin
|
19
|
+
engine.read(config_file)
|
20
|
+
|
21
|
+
exit engine.run
|
22
|
+
rescue YSI::Error => e
|
23
|
+
STDERR.puts e
|
24
|
+
exit 1
|
25
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module YSI
|
2
|
+
class Engine
|
3
|
+
attr_reader :assertions
|
4
|
+
attr_accessor :version, :tag_date
|
5
|
+
attr_accessor :out
|
6
|
+
attr_accessor :dry_run
|
7
|
+
|
8
|
+
def self.class_for_assertion_name(name)
|
9
|
+
class_name = name.split("_").map { |n| n.capitalize }.join
|
10
|
+
begin
|
11
|
+
Object.const_get("YSI::" + class_name)
|
12
|
+
rescue NameError
|
13
|
+
raise YSI::Error.new("Error: Unknown assertion '#{name}'")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@assertions = []
|
19
|
+
@out = STDOUT
|
20
|
+
end
|
21
|
+
|
22
|
+
def read(filename)
|
23
|
+
config = YAML.load_file(filename)
|
24
|
+
|
25
|
+
assertions = config["assertions"]
|
26
|
+
if assertions
|
27
|
+
assertions.each do |assertion|
|
28
|
+
if assertion == "version_number"
|
29
|
+
out.puts "Warning: use `version` instead of `version_number`."
|
30
|
+
out.puts
|
31
|
+
assertion = "version"
|
32
|
+
end
|
33
|
+
|
34
|
+
@assertions << YSI::Engine.class_for_assertion_name(assertion).new(self)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def project_name
|
40
|
+
File.basename(Dir.pwd)
|
41
|
+
end
|
42
|
+
|
43
|
+
def run
|
44
|
+
failed_assertions = []
|
45
|
+
errored_assertions = []
|
46
|
+
|
47
|
+
@assertions.each do |assertion|
|
48
|
+
out.print "Checking #{assertion.display_name}: "
|
49
|
+
success = assertion.check
|
50
|
+
if success
|
51
|
+
out.puts success
|
52
|
+
else
|
53
|
+
if assertion.error
|
54
|
+
out.puts "error"
|
55
|
+
out.puts " " + assertion.error
|
56
|
+
errored_assertions.push(assertion)
|
57
|
+
else
|
58
|
+
out.puts "fail"
|
59
|
+
failed_assertions.push(assertion)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
out.puts
|
65
|
+
|
66
|
+
if !errored_assertions.empty?
|
67
|
+
out.puts "Couldn't ship #{project_name}. Help me."
|
68
|
+
return 1
|
69
|
+
else
|
70
|
+
if failed_assertions.empty?
|
71
|
+
if tag_date
|
72
|
+
out.puts "#{project_name} #{version} already shipped on #{tag_date}"
|
73
|
+
else
|
74
|
+
out.puts "#{project_name} #{version} already shipped"
|
75
|
+
end
|
76
|
+
return 0
|
77
|
+
else
|
78
|
+
failed_assertions.each do |assertion|
|
79
|
+
if dry_run
|
80
|
+
out.print "Dry run: "
|
81
|
+
end
|
82
|
+
out.print "Asserting #{assertion.display_name}: "
|
83
|
+
success = assertion.assert(dry_run: dry_run)
|
84
|
+
if !success
|
85
|
+
out.puts
|
86
|
+
out.puts "Ran into an error. Stopping shipping."
|
87
|
+
return 1
|
88
|
+
end
|
89
|
+
out.puts success
|
90
|
+
end
|
91
|
+
|
92
|
+
out.puts
|
93
|
+
if dry_run
|
94
|
+
out.puts "Did a dry run of shipping #{project_name} #{version}." +
|
95
|
+
" Nothing was changed."
|
96
|
+
else
|
97
|
+
out.puts "Shipped #{project_name} #{version}. Hooray!"
|
98
|
+
end
|
99
|
+
return 0
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
data/lib/yes_ship_it.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
require "yaml"
|
2
|
+
require "rest-client"
|
3
|
+
|
4
|
+
require_relative "yes_ship_it/assertion.rb"
|
5
|
+
require_relative "yes_ship_it/engine.rb"
|
6
|
+
require_relative "yes_ship_it/exceptions.rb"
|
7
|
+
|
8
|
+
assertions_dir = File.expand_path("../../assertions", __FILE__)
|
9
|
+
Dir[File.join(assertions_dir, "*.rb")].each { |f| require(f) }
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
|
3
|
+
include CliTester
|
4
|
+
include GivenFilesystemSpecHelpers
|
5
|
+
|
6
|
+
describe "command line interface" do
|
7
|
+
use_given_filesystem(keep_files: true)
|
8
|
+
|
9
|
+
it "does not find yes_ship_it.conf" do
|
10
|
+
dir = given_directory
|
11
|
+
expect(run_command(working_directory: dir)).to exit_with_error(1, /yes_ship_it.conf/)
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "runs" do
|
15
|
+
it "fails when version is not there" do
|
16
|
+
dir = given_directory
|
17
|
+
|
18
|
+
setup_test_git_repo("000", dir)
|
19
|
+
|
20
|
+
expected_output = <<EOT
|
21
|
+
Shipping...
|
22
|
+
|
23
|
+
Warning: use `version` instead of `version_number`.
|
24
|
+
|
25
|
+
Checking version number: error
|
26
|
+
Expected version in lib/version.rb
|
27
|
+
Checking change log: error
|
28
|
+
Expected change log in CHANGELOG.md
|
29
|
+
|
30
|
+
Couldn't ship red_herring. Help me.
|
31
|
+
EOT
|
32
|
+
|
33
|
+
expect(run_command(working_directory: File.join(dir, "red_herring"))).
|
34
|
+
to exit_with_error(1, "", expected_output)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "succeeds when all checks are met" do
|
38
|
+
dir = given_directory
|
39
|
+
|
40
|
+
setup_test_git_repo("001", dir)
|
41
|
+
|
42
|
+
expected_output = <<EOT
|
43
|
+
Shipping...
|
44
|
+
|
45
|
+
Warning: use `version` instead of `version_number`.
|
46
|
+
|
47
|
+
Checking version number: 0.0.1
|
48
|
+
Checking change log: CHANGELOG.md
|
49
|
+
|
50
|
+
red_herring 0.0.1 already shipped
|
51
|
+
EOT
|
52
|
+
|
53
|
+
expect(run_command(working_directory: File.join(dir, "red_herring"))).
|
54
|
+
to exit_with_success(expected_output)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "succeeds when tag is there" do
|
58
|
+
dir = given_directory
|
59
|
+
|
60
|
+
setup_test_git_repo("002", dir)
|
61
|
+
|
62
|
+
expected_output = <<EOT
|
63
|
+
Shipping...
|
64
|
+
|
65
|
+
Warning: use `version` instead of `version_number`.
|
66
|
+
|
67
|
+
Checking version number: 0.0.1
|
68
|
+
Checking change log: CHANGELOG.md
|
69
|
+
Checking tag: v0.0.1
|
70
|
+
|
71
|
+
red_herring 0.0.1 already shipped on Wed Jul 1 00:46:19 2015 +0200
|
72
|
+
EOT
|
73
|
+
|
74
|
+
expect(run_command(working_directory: File.join(dir, "red_herring"))).
|
75
|
+
to exit_with_success(expected_output)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "fails when gem is not built" do
|
79
|
+
dir = given_directory
|
80
|
+
|
81
|
+
setup_test_git_repo("003", dir)
|
82
|
+
|
83
|
+
expected_output = <<EOT
|
84
|
+
Shipping...
|
85
|
+
|
86
|
+
Checking version number: 0.0.1
|
87
|
+
Checking change log: CHANGELOG.md
|
88
|
+
Checking built gem: fail
|
89
|
+
Checking tag: v0.0.1
|
90
|
+
|
91
|
+
Asserting built gem: red_herring-0.0.1.gem
|
92
|
+
|
93
|
+
Shipped red_herring 0.0.1. Hooray!
|
94
|
+
EOT
|
95
|
+
|
96
|
+
expect(run_command(working_directory: File.join(dir, "red_herring"))).
|
97
|
+
to exit_with_success(expected_output)
|
98
|
+
end
|
99
|
+
|
100
|
+
it "processes the --dry-run option" do
|
101
|
+
dir = given_directory
|
102
|
+
|
103
|
+
setup_test_git_repo("003", dir)
|
104
|
+
|
105
|
+
expected_output = <<EOT
|
106
|
+
Shipping...
|
107
|
+
|
108
|
+
Checking version number: 0.0.1
|
109
|
+
Checking change log: CHANGELOG.md
|
110
|
+
Checking built gem: fail
|
111
|
+
Checking tag: v0.0.1
|
112
|
+
|
113
|
+
Dry run: Asserting built gem: red_herring-0.0.1.gem
|
114
|
+
|
115
|
+
Did a dry run of shipping red_herring 0.0.1. Nothing was changed.
|
116
|
+
EOT
|
117
|
+
|
118
|
+
expect(run_command(args: ["--dry-run"],
|
119
|
+
working_directory: File.join(dir, "red_herring"))).
|
120
|
+
to exit_with_success(expected_output)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require "cli_tester"
|
2
|
+
require "given_filesystem/spec_helpers"
|
3
|
+
|
4
|
+
def setup_test_git_repo(version, dir)
|
5
|
+
tarball = "spec/data/red_herring-#{version}.tar.gz"
|
6
|
+
tarball_path = File.expand_path(tarball)
|
7
|
+
if !system("cd #{dir}; tar xzf #{tarball_path}")
|
8
|
+
raise "Unable to extract tarball #{tarball}"
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
|
3
|
+
describe "all assertions" do
|
4
|
+
all_classes = ObjectSpace.each_object(Class).select { |klass| klass < YSI::Assertion }
|
5
|
+
|
6
|
+
all_classes.each do |assertion_class|
|
7
|
+
describe assertion_class do
|
8
|
+
it_behaves_like "an assertion" do
|
9
|
+
let(:assertion) {assertion_class.new(YSI::Engine.new) }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require_relative "spec_helper.rb"
|
2
|
+
|
3
|
+
include GivenFilesystemSpecHelpers
|
4
|
+
|
5
|
+
describe YSI::Engine do
|
6
|
+
use_given_filesystem
|
7
|
+
|
8
|
+
describe "#class_for_assertion_name" do
|
9
|
+
it "creates VersionNumber class" do
|
10
|
+
expect(YSI::Engine.class_for_assertion_name("version")).
|
11
|
+
to be(YSI::Version)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "creates ChangeLog class" do
|
15
|
+
expect(YSI::Engine.class_for_assertion_name("change_log")).
|
16
|
+
to be(YSI::ChangeLog)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "#read" do
|
21
|
+
it "reads valid configuration" do
|
22
|
+
path = nil
|
23
|
+
given_directory do
|
24
|
+
path = given_file("yes_ship_it.conf")
|
25
|
+
end
|
26
|
+
|
27
|
+
ysi = YSI::Engine.new
|
28
|
+
|
29
|
+
ysi.read(path)
|
30
|
+
|
31
|
+
expect(ysi.assertions.count).to eq(2)
|
32
|
+
expect(ysi.assertions[0].class).to eq(YSI::Version)
|
33
|
+
expect(ysi.assertions[1].class).to eq(YSI::ChangeLog)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "fails on configuration with unknown assertions" do
|
37
|
+
path = nil
|
38
|
+
given_directory do
|
39
|
+
path = given_file("yes_ship_it.conf", from: "yes_ship_it.unknown.conf")
|
40
|
+
end
|
41
|
+
|
42
|
+
ysi = YSI::Engine.new
|
43
|
+
|
44
|
+
expect {
|
45
|
+
ysi.read(path)
|
46
|
+
}.to raise_error YSI::Error
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
it "runs assertions" do
|
51
|
+
path = nil
|
52
|
+
given_directory do
|
53
|
+
path = given_file("yes_ship_it.conf")
|
54
|
+
end
|
55
|
+
|
56
|
+
ysi = YSI::Engine.new
|
57
|
+
|
58
|
+
ysi.read(path)
|
59
|
+
|
60
|
+
ysi.assertions.each do |a|
|
61
|
+
expect(a).to receive(:check)
|
62
|
+
end
|
63
|
+
|
64
|
+
ysi.out = StringIO.new
|
65
|
+
|
66
|
+
ysi.run
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
shared_examples "an assertion" do
|
2
|
+
it "initializes with an engine" do
|
3
|
+
expect(assertion.engine).to be_a(YSI::Engine)
|
4
|
+
end
|
5
|
+
|
6
|
+
it "has a display name" do
|
7
|
+
expect(assertion.display_name).to be_a(String)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "checks" do
|
11
|
+
expect(assertion).to respond_to(:check)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "asserts" do
|
15
|
+
expect(assertion).to respond_to(:assert)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "reports errors" do
|
19
|
+
expect(assertion).to respond_to(:error)
|
20
|
+
end
|
21
|
+
end
|
data/yes_ship_it.conf
ADDED
metadata
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: yes_ship_it
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Cornelius Schumacher
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-07-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: given_filesystem
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: cli_tester
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: Whenever the answer is 'Yes, ship it!' you will need yes_ship_it. It
|
56
|
+
is the ultimate helper in releasing software..
|
57
|
+
email:
|
58
|
+
- schumacher@kde.org
|
59
|
+
executables: []
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- .rspec
|
64
|
+
- README.md
|
65
|
+
- assertions/built_gem.rb
|
66
|
+
- assertions/change_log.rb
|
67
|
+
- assertions/published_gem.rb
|
68
|
+
- assertions/pushed_tag.rb
|
69
|
+
- assertions/tag.rb
|
70
|
+
- assertions/version.rb
|
71
|
+
- bin/yes_ship_it
|
72
|
+
- lib/yes_ship_it.rb
|
73
|
+
- lib/yes_ship_it/assertion.rb
|
74
|
+
- lib/yes_ship_it/engine.rb
|
75
|
+
- lib/yes_ship_it/exceptions.rb
|
76
|
+
- spec/data/red_herring-000.tar.gz
|
77
|
+
- spec/data/red_herring-001.tar.gz
|
78
|
+
- spec/data/red_herring-002.tar.gz
|
79
|
+
- spec/data/red_herring-003.tar.gz
|
80
|
+
- spec/data/yes_ship_it.conf
|
81
|
+
- spec/data/yes_ship_it.unknown.conf
|
82
|
+
- spec/integration/cli_spec.rb
|
83
|
+
- spec/integration/spec_helper.rb
|
84
|
+
- spec/unit/assertions_spec.rb
|
85
|
+
- spec/unit/engine_spec.rb
|
86
|
+
- spec/unit/spec_helper.rb
|
87
|
+
- spec/unit/support/assertion_examples.rb
|
88
|
+
- yes_ship_it.conf
|
89
|
+
homepage: http://github.com/cornelius/yes_ship_it
|
90
|
+
licenses:
|
91
|
+
- MIT
|
92
|
+
metadata: {}
|
93
|
+
post_install_message:
|
94
|
+
rdoc_options: []
|
95
|
+
require_paths:
|
96
|
+
- lib
|
97
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - '>='
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: 1.3.6
|
107
|
+
requirements: []
|
108
|
+
rubyforge_project: yes_ship_it
|
109
|
+
rubygems_version: 2.0.3
|
110
|
+
signing_key:
|
111
|
+
specification_version: 4
|
112
|
+
summary: The ultimate release script
|
113
|
+
test_files: []
|
114
|
+
has_rdoc:
|