gem_publisher 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.
@@ -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,3 @@
1
+ module GemPublisher
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,8 @@
1
+ require "gem_publisher/publisher"
2
+ require "gem_publisher/version"
3
+
4
+ module GemPublisher
5
+ def self.publish_if_updated(gemspec, version, method=:rubygems)
6
+ Publisher.new(gemspec, version).publish_if_updated(method)
7
+ end
8
+ end
@@ -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
@@ -0,0 +1,4 @@
1
+ Successfully built RubyGem
2
+ Name: test_gem
3
+ Version: 0.0.2
4
+ File: test_gem-0.0.2.gem
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