gta 0.2.0 → 0.3.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6282ea52c54d66479c7bb48a0429f8e4e4113ef7
4
- data.tar.gz: c1e27e7e8a29454847b5081109aeb2a6ae4444e0
3
+ metadata.gz: c4ffc18a8b88a8e3ffa8c99a042a8862ae4ce43c
4
+ data.tar.gz: 85a82924459a0def9f5430961d4be27d4d2ebc83
5
5
  SHA512:
6
- metadata.gz: 817bcf144b42c8e96c56f0ec3709d1fcc8c1f217a1bbb15c3f7bce9428645569fe4817fa2279c0f179b75ac65f6a21619769694c82b3a77c62f817fc788e4765
7
- data.tar.gz: 16cfc6bc1416af0080822bf1fef98a585421e59fc58c9684789080dc6e47a4567f19c456a18287f685f8e524e85ddee58b975f61d04f83d6b5f937a6f2ca62c1
6
+ metadata.gz: 421748d39efd34d5b21c3fa5a24b42ed4857cf5c93e3f126bc0bda394ab89a3216fc7921b77eeec4992a0aef7c1aa9bee881fdb962fa6085d01c06e2fcb1956c
7
+ data.tar.gz: 156c066293e496a062830acee3ed9bf828ea59c0d18ad497f5dcd38de8653c7e23a2c2675af3dec20199e2b45d3377aa14332bba35d3178103082fe47c47b4dc
data/.gitignore CHANGED
@@ -18,3 +18,4 @@ tmp
18
18
  .idea
19
19
  .DS_Store
20
20
  **/.DS_Store
21
+ log/*
data/README.md CHANGED
@@ -31,7 +31,10 @@ Or install it yourself as:
31
31
 
32
32
  ## Usage
33
33
 
34
- Rake tasks are in progress; stay tuned.
34
+ The main use case is via rake task. Include the rake tasks via the
35
+ project Rakefile.
36
+
37
+
35
38
 
36
39
  ## Contributing
37
40
 
data/lib/gta.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'open3'
1
2
  require 'ansi/code'
2
3
 
3
4
  require "gta/version"
@@ -9,3 +10,5 @@ require "gta/local_db"
9
10
  require "gta/db"
10
11
  require "gta/hotfix"
11
12
  require "gta/tag_finder"
13
+ require "gta/commander"
14
+ require "gta/file_logger"
@@ -0,0 +1,104 @@
1
+ module GTA
2
+ class Commander
3
+ attr_reader :command, :output, :exit_status
4
+
5
+ LINE_LENGTH = 80
6
+
7
+ def initialize(command=nil)
8
+ @output = []
9
+ @command = command
10
+ end
11
+
12
+ def perform
13
+ _perform(false)
14
+ end
15
+
16
+ def perform!
17
+ _perform(true)
18
+ end
19
+
20
+ def write_output(line)
21
+ message = normalize_output(" #{line}")
22
+ puts "#{ANSI.white_on_black}#{message}"
23
+ output << line
24
+ end
25
+
26
+ def write_failure(message = command)
27
+ message = normalize_output(" Command failed: #{message}")
28
+ puts "#{ANSI.red_on_black}#{message}#{ANSI.ansi}"
29
+ write_to_log(message)
30
+ write_to_log("-"*LINE_LENGTH)
31
+ end
32
+
33
+ # --------------
34
+
35
+ def _perform(should_raise)
36
+ run_command(should_raise)
37
+ puts_reset
38
+
39
+ write_to_log(output.join)
40
+ handle_failure(should_raise) unless exit_status && exit_status.success?
41
+
42
+ output.join
43
+ end
44
+
45
+ def run_command(should_raise)
46
+ write_command
47
+ Open3.popen3(command) do |stdin, stdout, stderr, process_status|
48
+ stderr.sync = true
49
+ stdout.sync = true
50
+
51
+ while (line = stderr.gets)
52
+ write_output line
53
+ end
54
+
55
+ while (line = stdout.gets)
56
+ write_output line
57
+ end
58
+
59
+ @exit_status = process_status.value
60
+ end
61
+ rescue Errno::ENOENT => e
62
+ handle_failure(should_raise, e)
63
+ end
64
+
65
+ def handle_failure(should_raise, e=nil)
66
+ write_failure
67
+ if e
68
+ output << e.message
69
+ write_failure(e.message)
70
+ write_failure(e.backtrace)
71
+ end
72
+ raise CommandFailure, "FAILED! #{command}" if should_raise
73
+ end
74
+
75
+ def normalize_output(output)
76
+ normalized = output.gsub("\n",'')
77
+ normalized += " "*(LINE_LENGTH-normalized.size-1) if normalized.size < LINE_LENGTH
78
+ normalized += ' '
79
+ normalized
80
+ end
81
+
82
+ def write_command
83
+ message = normalize_output(" GTA: #{command}")
84
+ puts "#{ANSI.black_on_white}#{message}#{ANSI.ansi}"
85
+ write_to_log(command)
86
+ write_to_log("-"*LINE_LENGTH)
87
+ end
88
+
89
+ def puts_reset
90
+ puts "#{ANSI.ansi}"
91
+ end
92
+
93
+ def logger
94
+ @logger ||= FileLogger.new
95
+ end
96
+
97
+ def write_to_log(response)
98
+ logger.write(response)
99
+ end
100
+
101
+ class CommandFailure < StandardError
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,25 @@
1
+ module GTA
2
+ class FileLogger
3
+ attr_reader :log_dir
4
+
5
+ def initialize(log_dir=nil)
6
+ @log_dir = log_dir ||= "#{Dir.pwd}/log"
7
+ ensure_log_dir_and_file
8
+ end
9
+
10
+ def ensure_log_dir_and_file
11
+ FileUtils.mkdir_p(log_dir)
12
+ FileUtils.touch(log_file) unless File.exist?(log_file)
13
+ end
14
+
15
+ def log_file
16
+ "#{log_dir}/gta.log"
17
+ end
18
+
19
+ def write(stuff)
20
+ File.open(log_file, 'a') do |f|
21
+ f.write(stuff)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -10,8 +10,7 @@ module GTA
10
10
  end
11
11
 
12
12
  def url
13
- # using backticks in order to get the bash response
14
- `heroku pgbackups:url --app #{app_signature}`
13
+ sh!("heroku pgbackups:url --app #{app_signature}").strip
15
14
  end
16
15
 
17
16
  def backup
@@ -19,11 +18,11 @@ module GTA
19
18
  end
20
19
 
21
20
  def restore_from(url)
22
- sh("heroku pgbackups:restore DATABASE_URL \"#{url}\" --app #{app_signature} --confirm #{app_signature}")
21
+ sh!("heroku pgbackups:restore DATABASE_URL \"#{url}\" --app #{app_signature} --confirm #{app_signature}")
23
22
  end
24
23
 
25
24
  def fetch
26
- sh("curl -o #{file_name} \"#{url}\"")
25
+ sh!("curl -o #{file_name} \"#{url}\"")
27
26
  end
28
27
 
29
28
  def file_name
@@ -1,5 +1,7 @@
1
1
  module GTA
2
2
  class Hotfix
3
+ include Sh
4
+
3
5
  attr_reader :gta_config_path
4
6
 
5
7
  def initialize(gta_config_path = nil)
@@ -20,7 +22,7 @@ module GTA
20
22
  stage_name = branch_name
21
23
  stage = stage_for(stage_name)
22
24
  not_hotfixable!(stage_name) if !stage || !stage_name
23
- sh "git push #{stage_name} #{stage_name}:master"
25
+ sh!("git push #{stage_name} #{stage_name}:master")
24
26
  end
25
27
 
26
28
  def not_hotfixable!(stage_name)
@@ -32,8 +34,7 @@ module GTA
32
34
  end
33
35
 
34
36
  def branch_name
35
- # using `` because we need the bash output
36
- branches = `git branch`
37
+ branches = sh!("git branch").strip
37
38
  matches = branches.match(/\*\s+(.*)/)
38
39
  matches[1].strip if matches
39
40
  end
@@ -10,7 +10,7 @@ module GTA
10
10
  end
11
11
 
12
12
  def load(backup_path)
13
- sh "pg_restore --verbose --clean --no-acl --no-owner -h localhost#{username}#{database} #{backup_path}"
13
+ sh("pg_restore --verbose --clean --no-acl --no-owner -h localhost#{username}#{database} #{backup_path}")
14
14
  end
15
15
 
16
16
  def config
@@ -8,7 +8,7 @@ module GTA
8
8
 
9
9
  def push_to(name, forced = nil)
10
10
  s = stage!(name)
11
- fetch
11
+ fetch!
12
12
  if forced == :force
13
13
  s.force_push
14
14
  else
@@ -30,6 +30,10 @@ module GTA
30
30
  stages.each(&:fetch)
31
31
  end
32
32
 
33
+ def fetch!
34
+ stages.each(&:fetch!)
35
+ end
36
+
33
37
  def setup
34
38
  stages.each(&:add_remote)
35
39
  end
@@ -1,8 +1,15 @@
1
1
  module GTA
2
2
  module Sh
3
3
  def sh(command)
4
- puts "#{ANSI.black_on_white} GTA: #{command} #{ANSI.ansi}"
5
- system(command)
4
+ commander(command).perform
5
+ end
6
+
7
+ def sh!(command)
8
+ commander(command).perform
9
+ end
10
+
11
+ def commander(command=nil)
12
+ Commander.new(command)
6
13
  end
7
14
  end
8
15
  end
@@ -29,23 +29,39 @@ module GTA
29
29
  raise "no name defined for #{self}" unless name
30
30
  raise "no repository defined for #{name}" unless repository
31
31
 
32
- sh "git remote add #{name} #{repository}"
32
+ sh("git remote add #{name} #{repository}")
33
33
  end
34
34
 
35
35
  def checkout
36
- sh "git checkout -b #{name} -t #{name}/#{branch}"
36
+ sh(checkout_command)
37
+ end
38
+
39
+ def checkout!
40
+ sh!(checkout_command)
41
+ end
42
+
43
+ def checkout_command
44
+ "git checkout -b #{name} -t #{name}/#{branch}"
37
45
  end
38
46
 
39
47
  def push(s=source, forced=nil)
40
- sh push_command(source_from(s), forced)
48
+ sh!(push_command(source_from(s), forced))
41
49
  end
42
50
 
43
51
  def force_push(s=source)
44
- sh push_command(source_from(s), :force)
52
+ sh!(push_command(source_from(s), :force))
45
53
  end
46
54
 
47
55
  def fetch
48
- sh "git fetch #{name}"
56
+ sh(fetch_command)
57
+ end
58
+
59
+ def fetch!
60
+ sh!(fetch_command)
61
+ end
62
+
63
+ def fetch_command
64
+ "git fetch #{name}"
49
65
  end
50
66
 
51
67
  def ==(other)
File without changes
@@ -1,3 +1,3 @@
1
1
  module GTA
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -0,0 +1,69 @@
1
+ require 'spec_helper'
2
+
3
+ describe GTA::Commander do
4
+ let(:commander) { GTA::Commander.new(command) }
5
+ let(:log_path) { File.dirname(__FILE__) + "/../log/gta.log" }
6
+ let(:log) { File.read(log_path) }
7
+
8
+ before do
9
+ File.delete(log_path) if File.exist?(log_path)
10
+ end
11
+
12
+ context 'when command is successful' do
13
+ let(:command) { 'echo foo' }
14
+
15
+ it "performs the command and returns the output" do
16
+ capture_stdout do
17
+ commander.perform.should == "foo\n"
18
+ end
19
+ end
20
+
21
+ it "writes to the log" do
22
+ capture_stdout do
23
+ commander.perform
24
+ end
25
+
26
+ log.should include "foo"
27
+ end
28
+ end
29
+
30
+ context 'when the command is not successful' do
31
+ context 'with a bash error, that bubbles up to a Ruby error' do
32
+ let(:command) { 'foo' }
33
+
34
+ context 'when #perform! used' do
35
+ it "raises an error when the #perform! method is called" do
36
+ capture_stdout do
37
+ expect {
38
+ commander.perform!
39
+ }.to raise_error
40
+ end
41
+ end
42
+ end
43
+
44
+ context 'when #perform is used' do
45
+ it "sends outputs the message in red" do
46
+ output = capture_stdout do
47
+ commander.perform
48
+ end
49
+ output.should include(ANSI.red_on_black)
50
+ output.should include("No such file or directory - foo")
51
+ end
52
+
53
+ it "returns the error message when the non-bang method is used" do
54
+ capture_stdout do
55
+ commander.perform.should == "No such file or directory - foo"
56
+ end
57
+ end
58
+
59
+ it "writes to the log" do
60
+ capture_stdout do
61
+ commander.perform
62
+ end
63
+
64
+ log.should include "No such file or directory - foo"
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -6,7 +6,7 @@ describe GTA::HerokuDB do
6
6
 
7
7
  describe '#url' do
8
8
  it "gets the temporary database url from heroku" do
9
- heroku_db.should_receive(:`)
9
+ heroku_db.should_receive(:sh!)
10
10
  .with("heroku pgbackups:url --app activator-staging")
11
11
  .and_return('backup url')
12
12
  heroku_db.url.should == 'backup url'
@@ -23,7 +23,7 @@ describe GTA::HerokuDB do
23
23
 
24
24
  describe '#restore_from(url)' do
25
25
  it "sends a heroku command to restore the database from the given url" do
26
- heroku_db.should_receive(:sh)
26
+ heroku_db.should_receive(:sh!)
27
27
  .with('heroku pgbackups:restore DATABASE_URL "http://my-database-url.com" --app activator-staging --confirm activator-staging')
28
28
  heroku_db.restore_from("http://my-database-url.com")
29
29
  end
@@ -32,7 +32,7 @@ describe GTA::HerokuDB do
32
32
  describe '#fetch' do
33
33
  it "downloads the latest database backup to an appropriately named file in downloads" do
34
34
  heroku_db.should_receive(:url).and_return('http://heroku-backup-url.com')
35
- heroku_db.should_receive(:sh)
35
+ heroku_db.should_receive(:sh!)
36
36
  .with('curl -o ~/Downloads/activator-staging.sql "http://heroku-backup-url.com"')
37
37
  heroku_db.fetch
38
38
  end
@@ -43,8 +43,13 @@ describe GTA::Hotfix do
43
43
  describe '#deploy' do
44
44
  context "when on a branch that maps to a stage" do
45
45
  before do
46
- hotfix.stub(:`).and_return(branch_output)
47
- hotfix.stub(:sh)
46
+ hotfix.stub(:sh!) do |command|
47
+ if command == "git branch"
48
+ branch_output
49
+ else
50
+ "deploying"
51
+ end
52
+ end
48
53
  end
49
54
 
50
55
  context "if it is hotfixable" do
@@ -53,7 +58,7 @@ describe GTA::Hotfix do
53
58
  }
54
59
 
55
60
  it "should call #sh with the right deploy command" do
56
- hotfix.should_receive(:sh)
61
+ hotfix.should_receive(:sh!)
57
62
  .with("git push qa qa:master")
58
63
  .and_return("deploying")
59
64
  hotfix.deploy.should == "deploying"
@@ -93,12 +93,12 @@ describe GTA::Manager do
93
93
 
94
94
  describe '#push_to' do
95
95
  before do
96
- manager.stub(:fetch)
96
+ manager.stub(:fetch!)
97
97
  manager.stage(:qa).stub(:push)
98
98
  end
99
99
 
100
100
  it "fetches" do
101
- manager.should_receive(:fetch)
101
+ manager.should_receive(:fetch!)
102
102
  manager.push_to(:qa)
103
103
  end
104
104
 
@@ -98,7 +98,7 @@ describe GTA::Stage do
98
98
 
99
99
  context "when using internally defined source object" do
100
100
  it "sends the right git shell command" do
101
- stage.should_receive(:sh).with("git push staging ci:master")
101
+ stage.should_receive(:sh!).with("git push staging ci:master")
102
102
  stage.push
103
103
  end
104
104
  end
@@ -107,7 +107,7 @@ describe GTA::Stage do
107
107
  let(:origin) { GTA::Stage.new('origin', manager, opts.merge('branch' => 'sendit')) }
108
108
 
109
109
  it "sends the right git shell command" do
110
- stage.should_receive(:sh).with("git push staging origin:master")
110
+ stage.should_receive(:sh!).with("git push staging origin:master")
111
111
  stage.push(origin)
112
112
  end
113
113
  end
@@ -116,7 +116,7 @@ describe GTA::Stage do
116
116
  let(:branch) { 'alt_branch' }
117
117
 
118
118
  it "sends the right git shell command" do
119
- stage.should_receive(:sh).with("git push staging foo:alt_branch")
119
+ stage.should_receive(:sh!).with("git push staging foo:alt_branch")
120
120
  stage.push('foo')
121
121
  end
122
122
  end
@@ -127,14 +127,14 @@ describe GTA::Stage do
127
127
 
128
128
  it "uses the tag as the source reference" do
129
129
  GTA::TagFinder.should_receive(:new).with(tag).and_return(tag_finder)
130
- stage.should_receive(:sh).with("git push staging my-tag:master")
130
+ stage.should_receive(:sh!).with("git push staging my-tag:master")
131
131
  stage.push
132
132
  end
133
133
  end
134
134
 
135
135
  context "force push" do
136
136
  it "adds the -f flag to the git command" do
137
- stage.should_receive(:sh).with("git push -f staging ci:master")
137
+ stage.should_receive(:sh!).with("git push -f staging ci:master")
138
138
  stage.force_push
139
139
  end
140
140
  end
@@ -0,0 +1,21 @@
1
+ def capture_stdout
2
+ old_stdout = $stdout.dup
3
+ rd, wr = IO.method(:pipe).arity.zero? ? IO.pipe : IO.pipe("BINARY")
4
+ $stdout = wr
5
+ yield
6
+ wr.close
7
+ rd.read
8
+ ensure
9
+ $stdout = old_stdout
10
+ end
11
+
12
+ def capture_stderr
13
+ old_stderr = $stderr.dup
14
+ rd, wr = IO.method(:pipe).arity.zero? ? IO.pipe : IO.pipe("BINARY")
15
+ $stderr = wr
16
+ yield
17
+ wr.close
18
+ rd.read
19
+ ensure
20
+ $stderr = old_stderr
21
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gta
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - socialchorus
@@ -84,7 +84,9 @@ files:
84
84
  - Rakefile
85
85
  - gta.gemspec
86
86
  - lib/gta.rb
87
+ - lib/gta/commander.rb
87
88
  - lib/gta/db.rb
89
+ - lib/gta/file_logger.rb
88
90
  - lib/gta/heroku_db.rb
89
91
  - lib/gta/hotfix.rb
90
92
  - lib/gta/local_db.rb
@@ -94,11 +96,13 @@ files:
94
96
  - lib/gta/stage.rb
95
97
  - lib/gta/tag_finder.rb
96
98
  - lib/gta/tasks.rb
99
+ - lib/gta/tasks/commander.rb
97
100
  - lib/gta/tasks/deploy.rake
98
101
  - lib/gta/tasks/gta.rake
99
102
  - lib/gta/tasks/heroku_db.rake
100
103
  - lib/gta/tasks/hotfix.rake
101
104
  - lib/gta/version.rb
105
+ - spec/commander_spec.rb
102
106
  - spec/db_spec.rb
103
107
  - spec/fixtures/config/database.yml
104
108
  - spec/fixtures/config/gta.yml
@@ -108,6 +112,7 @@ files:
108
112
  - spec/manager_spec.rb
109
113
  - spec/spec_helper.rb
110
114
  - spec/stage_spec.rb
115
+ - spec/support/capturers.rb
111
116
  - spec/tag_finder_spec.rb
112
117
  homepage: http://github.com/socialchorus/gta
113
118
  licenses:
@@ -135,6 +140,7 @@ specification_version: 4
135
140
  summary: 'GTA: the Git Transit Authority - A git based deploy tool for moving code
136
141
  from stage to stage.'
137
142
  test_files:
143
+ - spec/commander_spec.rb
138
144
  - spec/db_spec.rb
139
145
  - spec/fixtures/config/database.yml
140
146
  - spec/fixtures/config/gta.yml
@@ -144,4 +150,5 @@ test_files:
144
150
  - spec/manager_spec.rb
145
151
  - spec/spec_helper.rb
146
152
  - spec/stage_spec.rb
153
+ - spec/support/capturers.rb
147
154
  - spec/tag_finder_spec.rb