gem_publisher 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/gem_publisher/builder.rb +13 -0
- data/lib/gem_publisher/cli_facade.rb +17 -0
- data/lib/gem_publisher/git_remote.rb +26 -0
- data/lib/gem_publisher/publisher.rb +41 -0
- data/lib/gem_publisher/pusher.rb +20 -0
- data/lib/gem_publisher/version.rb +3 -0
- data/lib/gem_publisher.rb +8 -0
- data/test/builder_test.rb +16 -0
- data/test/cli_facade_test.rb +28 -0
- data/test/common.rb +11 -0
- data/test/data/gem_build +4 -0
- data/test/data/tags +10 -0
- data/test/gem_publisher_test.rb +44 -0
- data/test/git_remote_test.rb +32 -0
- data/test/integration_test.rb +26 -0
- metadata +60 -0
@@ -0,0 +1,13 @@
|
|
1
|
+
require "gem_publisher/cli_facade"
|
2
|
+
|
3
|
+
module GemPublisher
|
4
|
+
class Builder
|
5
|
+
def initialize(cli_facade = CliFacade.new)
|
6
|
+
@cli_facade = cli_facade
|
7
|
+
end
|
8
|
+
|
9
|
+
def build(gemspec)
|
10
|
+
@cli_facade.execute("gem", "build", gemspec)[/File:\s+(.+)/, 1]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require "shellwords"
|
2
|
+
require "open3"
|
3
|
+
|
4
|
+
module GemPublisher
|
5
|
+
class CliFacade
|
6
|
+
Error = Class.new(RuntimeError)
|
7
|
+
|
8
|
+
def execute(*arguments)
|
9
|
+
cmd = Shellwords.join(arguments)
|
10
|
+
stdout_str, stderr_str, status = Open3.capture3(cmd)
|
11
|
+
if status.exitstatus > 0
|
12
|
+
raise Error, stderr_str
|
13
|
+
end
|
14
|
+
stdout_str
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "gem_publisher/cli_facade"
|
2
|
+
|
3
|
+
module GemPublisher
|
4
|
+
class GitRemote
|
5
|
+
def initialize(name = "origin", cli_facade = CliFacade.new)
|
6
|
+
@remote_name = name
|
7
|
+
@cli_facade = cli_facade
|
8
|
+
end
|
9
|
+
|
10
|
+
def tags
|
11
|
+
s = git("ls-remote", "--tags", @remote_name)
|
12
|
+
s.scan(%r{refs/tags/(.+)}).map(&:first).reject { |t| t =~ /\^\{\}$/ }
|
13
|
+
end
|
14
|
+
|
15
|
+
def add_tag(tag_name, commit_ish = "HEAD")
|
16
|
+
sha1 = git("rev-parse", commit_ish)
|
17
|
+
git "update-ref", "refs/tags/#{tag_name}", sha1
|
18
|
+
git "push", @remote_name, "tag", tag_name
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
def git(*args)
|
23
|
+
@cli_facade.execute(*["git"] + args)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require "gem_publisher/git_remote"
|
2
|
+
require "gem_publisher/builder"
|
3
|
+
require "gem_publisher/pusher"
|
4
|
+
|
5
|
+
module GemPublisher
|
6
|
+
class Publisher
|
7
|
+
attr_accessor :git_remote, :builder, :pusher
|
8
|
+
|
9
|
+
def initialize(gemspec, version)
|
10
|
+
@gemspec = gemspec
|
11
|
+
@version = version
|
12
|
+
@git_remote = GitRemote.new
|
13
|
+
@builder = Builder.new
|
14
|
+
@pusher = Pusher.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def publish_if_updated(method)
|
18
|
+
return unless version_bumped?
|
19
|
+
gem = @builder.build(@gemspec)
|
20
|
+
@git_remote.add_tag "v#@version"
|
21
|
+
@pusher.push gem, method
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
def version_bumped?
|
26
|
+
last_release = @git_remote.tags.
|
27
|
+
select { |t| t =~ /^v\d+(\.\d+)+/ }.
|
28
|
+
map { |t| t.scan(/\d+/).map(&:to_i) }.
|
29
|
+
sort.last
|
30
|
+
this_release = @version.split(/\./).map(&:to_i)
|
31
|
+
this_release != last_release
|
32
|
+
end
|
33
|
+
|
34
|
+
def tag_remote
|
35
|
+
return
|
36
|
+
sha1 = `git rev-parse HEAD`
|
37
|
+
system "git update-ref refs/tags/v#@version #{sha1}"
|
38
|
+
system "git push origin tag v#@version"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require "gem_publisher/cli_facade"
|
2
|
+
|
3
|
+
module GemPublisher
|
4
|
+
class Pusher
|
5
|
+
def initialize(cli_facade = CliFacade.new)
|
6
|
+
@cli_facade = cli_facade
|
7
|
+
end
|
8
|
+
|
9
|
+
PUSH_METHODS = {
|
10
|
+
:rubygems => %w[gem push],
|
11
|
+
:gemfury => %w[fury push]
|
12
|
+
}
|
13
|
+
|
14
|
+
def push(gem, method)
|
15
|
+
push_command = PUSH_METHODS[method] or raise "Unknown Gem push method #{method.inspect}."
|
16
|
+
@cli_facade.execute *push_command + [gem]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require File.expand_path("../common", __FILE__)
|
2
|
+
require "gem_publisher/builder"
|
3
|
+
|
4
|
+
module GemPublisher
|
5
|
+
class BuilderTest < MiniTest::Unit::TestCase
|
6
|
+
def test_should_build_gem_and_return_filename
|
7
|
+
cli_facade = mock
|
8
|
+
cli_facade.expects(:execute).
|
9
|
+
with("gem", "build", "foo.gemspec").
|
10
|
+
returns(data_file("gem_build"))
|
11
|
+
|
12
|
+
builder = Builder.new(cli_facade)
|
13
|
+
assert_equal "test_gem-0.0.2.gem", builder.build("foo.gemspec")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require File.expand_path("../common", __FILE__)
|
2
|
+
require "gem_publisher/cli_facade"
|
3
|
+
|
4
|
+
module GemPublisher
|
5
|
+
class CliFacadeTest < MiniTest::Unit::TestCase
|
6
|
+
def test_should_raise_exception_if_command_failed
|
7
|
+
status = stub(exitstatus: 1)
|
8
|
+
Open3.stubs(:capture3).returns(["stdout", "stderr", status])
|
9
|
+
assert_raises CliFacade::Error do
|
10
|
+
CliFacade.new.execute("status")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_should_return_stdout_if_command_succeeded
|
15
|
+
status = stub(exitstatus: 0)
|
16
|
+
Open3.stubs(:capture3).returns(["stdout", "stderr", status])
|
17
|
+
assert_equal "stdout", CliFacade.new.execute("status")
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_should_escape_and_pass_in_all_commands
|
21
|
+
status = stub(exitstatus: 0)
|
22
|
+
Open3.expects(:capture3).
|
23
|
+
with("foo bar \\*").
|
24
|
+
returns(["stdout", "stderr", status])
|
25
|
+
CliFacade.new.execute("foo", "bar", "*")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/test/common.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
lib = File.expand_path("../../lib", __FILE__)
|
2
|
+
$:.unshift lib unless $:.include?(lib)
|
3
|
+
|
4
|
+
require "minitest/autorun"
|
5
|
+
require "mocha"
|
6
|
+
|
7
|
+
class MiniTest::Unit::TestCase
|
8
|
+
def data_file(name)
|
9
|
+
File.read(File.expand_path("../data/#{name}", __FILE__))
|
10
|
+
end
|
11
|
+
end
|
data/test/data/gem_build
ADDED
data/test/data/tags
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
41c5b8ad5122893bb63754c098ee25b93cce20e1 refs/tags/v0.0.10
|
2
|
+
a3762f17d9f234c058a9f8194154513a89f0a19b refs/tags/v0.0.10^{}
|
3
|
+
c7b78dd34aebdce17fae1773ca0a854de3da32a3 refs/tags/v0.0.11
|
4
|
+
5294fac0c70956209494b69bc1a8c38192f6a931 refs/tags/v0.0.11^{}
|
5
|
+
4775fe5cc621a2955b4438d0747069bf6fc56b02 refs/tags/v0.0.6
|
6
|
+
e0911ed33423177183b88d5f3a339f680aa24564 refs/tags/v0.0.6^{}
|
7
|
+
6cad8efbe5df65a3aaf5608f24a5bfb99973cb4a refs/tags/v0.0.7
|
8
|
+
3fd61ea34c30022889bfa2856dc807d98c15e0f5 refs/tags/v0.0.7^{}
|
9
|
+
2cce20d85a69974be0c5fc7fd5af207062690089 refs/tags/v0.0.9
|
10
|
+
f15a4d4a7726c7c624a33e727fcdbabb3938ac02 refs/tags/v0.0.9^{}
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require File.expand_path("../common", __FILE__)
|
2
|
+
require "gem_publisher/publisher"
|
3
|
+
|
4
|
+
module GemPublisher
|
5
|
+
class PublisherTest < MiniTest::Unit::TestCase
|
6
|
+
def test_should_not_do_anything_if_version_has_not_changed
|
7
|
+
p = Publisher.new("foo.gemspec", "0.0.2")
|
8
|
+
p.builder = mock
|
9
|
+
p.builder.expects(:build).never
|
10
|
+
p.pusher = mock
|
11
|
+
p.pusher.expects(:push).never
|
12
|
+
p.git_remote = mock
|
13
|
+
p.git_remote.stubs(:tags).returns(%w[v0.0.1 v0.0.2])
|
14
|
+
p.git_remote.expects(:add_tag).never
|
15
|
+
p.publish_if_updated(:bogus)
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_should_build_and_tag_and_publish_if_version_has_changed
|
19
|
+
p = Publisher.new("foo.gemspec", "0.0.3")
|
20
|
+
p.builder = mock
|
21
|
+
p.builder.expects(:build).
|
22
|
+
with("foo.gemspec").
|
23
|
+
returns("foo-0.0.3.gem")
|
24
|
+
p.pusher = mock
|
25
|
+
p.pusher.expects(:push).with("foo-0.0.3.gem", :method)
|
26
|
+
p.git_remote = mock
|
27
|
+
p.git_remote.stubs(:tags).returns(%w[v0.0.1 v0.0.2])
|
28
|
+
p.git_remote.expects(:add_tag).with("v0.0.3")
|
29
|
+
p.publish_if_updated(:method)
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_should_build_and_tag_and_publish_if_there_is_no_released_version
|
33
|
+
p = Publisher.new("foo.gemspec", "0.0.3")
|
34
|
+
p.builder = mock
|
35
|
+
p.builder.expects(:build).returns("foo-0.0.3.gem")
|
36
|
+
p.pusher = mock
|
37
|
+
p.pusher.expects(:push)
|
38
|
+
p.git_remote = mock
|
39
|
+
p.git_remote.stubs(:tags).returns([])
|
40
|
+
p.git_remote.expects(:add_tag).with("v0.0.3")
|
41
|
+
p.publish_if_updated(:method)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require File.expand_path("../common", __FILE__)
|
2
|
+
require "gem_publisher/git_remote"
|
3
|
+
|
4
|
+
module GemPublisher
|
5
|
+
class GitRemoteTest < MiniTest::Unit::TestCase
|
6
|
+
def test_should_list_remote_tags
|
7
|
+
cli_facade = mock
|
8
|
+
cli_facade.stubs(:execute).
|
9
|
+
with("git", "ls-remote", "--tags", "origin").
|
10
|
+
returns(data_file("tags"))
|
11
|
+
|
12
|
+
remote = GitRemote.new("origin", cli_facade)
|
13
|
+
expected = %w[v0.0.10 v0.0.11 v0.0.6 v0.0.7 v0.0.9]
|
14
|
+
assert_equal expected.sort, remote.tags.sort
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_should_push_tag_to_remote
|
18
|
+
cli_facade = mock
|
19
|
+
sha1 = "5294fac0c70956209494b69bc1a8c38192f6a931"
|
20
|
+
cli_facade.stubs(:execute).
|
21
|
+
with("git", "rev-parse", "HEAD").
|
22
|
+
returns(sha1)
|
23
|
+
cli_facade.expects(:execute).
|
24
|
+
with("git", "update-ref", "refs/tags/nameoftag", sha1)
|
25
|
+
cli_facade.expects(:execute).
|
26
|
+
with("git", "push", "origin", "tag", "nameoftag")
|
27
|
+
|
28
|
+
remote = GitRemote.new("origin", cli_facade)
|
29
|
+
remote.add_tag("nameoftag")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require File.expand_path("../common", __FILE__)
|
2
|
+
require "gem_publisher"
|
3
|
+
|
4
|
+
module GemPublisher
|
5
|
+
class IntegrationTest < MiniTest::Unit::TestCase
|
6
|
+
def setup
|
7
|
+
Open3.stubs(:capture3)
|
8
|
+
end
|
9
|
+
|
10
|
+
def expect_cli(command, response = "")
|
11
|
+
Open3.expects(:capture3).
|
12
|
+
with(command).
|
13
|
+
returns([response, "", stub(exitstatus: 0)])
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_should_build_and_tag_and_publish
|
17
|
+
expect_cli "git ls-remote --tags origin", data_file("tags")
|
18
|
+
expect_cli "gem build example.gemspec", data_file("gem_build")
|
19
|
+
expect_cli "git rev-parse HEAD", "1234abcd"
|
20
|
+
expect_cli "git update-ref refs/tags/v1.0.0 1234abcd"
|
21
|
+
expect_cli "git push origin tag v1.0.0"
|
22
|
+
expect_cli "gem push test_gem-0.0.2.gem"
|
23
|
+
GemPublisher.publish_if_updated "example.gemspec", "1.0.0"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gem_publisher
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Paul Battley
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-05-10 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: Automatically build, tag, and push a gem when its version has been updated.
|
15
|
+
email: pbattley@gmail.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- lib/gem_publisher/git_remote.rb
|
21
|
+
- lib/gem_publisher/builder.rb
|
22
|
+
- lib/gem_publisher/cli_facade.rb
|
23
|
+
- lib/gem_publisher/pusher.rb
|
24
|
+
- lib/gem_publisher/publisher.rb
|
25
|
+
- lib/gem_publisher/version.rb
|
26
|
+
- lib/gem_publisher.rb
|
27
|
+
- test/builder_test.rb
|
28
|
+
- test/git_remote_test.rb
|
29
|
+
- test/data/tags
|
30
|
+
- test/data/gem_build
|
31
|
+
- test/cli_facade_test.rb
|
32
|
+
- test/gem_publisher_test.rb
|
33
|
+
- test/integration_test.rb
|
34
|
+
- test/common.rb
|
35
|
+
homepage: http://github.com/threedaymonk/gem_publisher
|
36
|
+
licenses: []
|
37
|
+
post_install_message:
|
38
|
+
rdoc_options: []
|
39
|
+
require_paths:
|
40
|
+
- lib
|
41
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ! '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
48
|
+
none: false
|
49
|
+
requirements:
|
50
|
+
- - ! '>='
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '0'
|
53
|
+
requirements: []
|
54
|
+
rubyforge_project:
|
55
|
+
rubygems_version: 1.8.24
|
56
|
+
signing_key:
|
57
|
+
specification_version: 3
|
58
|
+
summary: Automatically build, tag, and push gems
|
59
|
+
test_files: []
|
60
|
+
has_rdoc: true
|