bard 0.41.2 → 0.42.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 +5 -5
- data/.gitignore +1 -0
- data/bard.gemspec +0 -4
- data/features/support/env.rb +0 -1
- data/features/support/io.rb +2 -1
- data/lib/bard.rb +82 -43
- data/lib/bard/base.rb +59 -8
- data/lib/bard/ci.rb +122 -49
- data/lib/bard/config.rb +85 -0
- data/lib/bard/data.rb +49 -0
- data/lib/bard/version.rb +1 -1
- metadata +5 -63
- data/.ruby-gemset +0 -1
- data/.ruby-version +0 -1
- data/lib/bard/capistrano.rb +0 -131
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 8ede698ef957275736c6d373ab637f83e9a676bf3a7214a789d4314ee69f3532
|
4
|
+
data.tar.gz: c0ac00d558f50fa633858358d2387f68095810e8741b13b4227a08cfd3f9c5c4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a237a2cc3ddc4611486f1dfe6b14a8c282daa7656330bc0748c960f668c4e5ec213ebd7cb9b7cd0671de95476a4a61b0e6763bb5588cda63dc08ab2d0809f9f0
|
7
|
+
data.tar.gz: 6861a6cf67056c5762144eb4fafc809c3a7a1040a8213a5bcac5860bc4f33aec9d44dccd7567e761f96465b20e0f14e5fa4a45aadcd96217a47b92ac7c8ce008
|
data/.gitignore
CHANGED
data/bard.gemspec
CHANGED
@@ -19,11 +19,7 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
21
|
spec.add_dependency "thor", ">= 0.19.0"
|
22
|
-
spec.add_dependency "capistrano", "= 2.5.10"
|
23
|
-
spec.add_dependency "net-ssh", "~> 4.0"
|
24
22
|
spec.add_dependency "rvm"
|
25
|
-
spec.add_dependency "rvm-capistrano"
|
26
|
-
spec.add_dependency "systemu", ">= 1.2.0"
|
27
23
|
spec.add_dependency "term-ansicolor", ">= 1.0.3"
|
28
24
|
spec.add_dependency "bard-rake", ">= 0.1.1"
|
29
25
|
|
data/features/support/env.rb
CHANGED
data/features/support/io.rb
CHANGED
data/lib/bard.rb
CHANGED
@@ -1,60 +1,71 @@
|
|
1
|
-
$:.unshift File.expand_path(File.dirname(__FILE__))
|
2
|
-
|
3
1
|
module Bard; end
|
4
2
|
|
5
3
|
require "bard/base"
|
6
4
|
require "bard/git"
|
7
5
|
require "bard/ci"
|
6
|
+
require "bard/data"
|
7
|
+
|
8
|
+
require "bard/config"
|
8
9
|
|
9
10
|
class Bard::CLI < Thor
|
11
|
+
def initialize(*args, **kwargs, &block)
|
12
|
+
super
|
13
|
+
@config = Config.new(project_name, "bard.rb")
|
14
|
+
end
|
15
|
+
|
10
16
|
desc "data [FROM=production, TO=local]", "copy database and assets from FROM to TO"
|
11
|
-
def data(from
|
12
|
-
|
13
|
-
exec "cap _2.5.10_ data:push ROLES=#{to}" if from == "local"
|
17
|
+
def data(from="production", to="local")
|
18
|
+
Data.new(self, from, to).call
|
14
19
|
end
|
15
20
|
|
16
21
|
method_options %w( verbose -v ) => :boolean
|
17
|
-
desc "stage", "pushes current branch, and stages it"
|
18
|
-
def stage
|
19
|
-
unless
|
22
|
+
desc "stage [BRANCH=HEAD]", "pushes current branch, and stages it"
|
23
|
+
def stage branch=Git.current_branch
|
24
|
+
unless @config.servers.key?(:production)
|
20
25
|
raise Thor::Error.new("`bard stage` is disabled until a production server is defined. Until then, please use `bard deploy` to deploy to the staging server.")
|
21
26
|
end
|
22
27
|
|
23
|
-
branch = Git.current_branch
|
24
|
-
|
25
28
|
run_crucial "git push -u origin #{branch}", true
|
26
|
-
|
29
|
+
command = "git fetch && git checkout -f origin/#{branch} && bin/setup"
|
30
|
+
run_crucial ssh_command(:staging, command)
|
27
31
|
puts green("Stage Succeeded")
|
28
32
|
|
29
|
-
|
30
|
-
puts red("Staging is now down!")
|
31
|
-
end
|
33
|
+
ping :staging
|
32
34
|
end
|
33
35
|
|
34
|
-
method_options %w
|
35
|
-
desc "deploy [
|
36
|
-
def deploy
|
36
|
+
method_options %w[verbose -v] => :boolean, %w[skip-ci] => :boolean, %w[local-ci -l] => :boolean
|
37
|
+
desc "deploy [TO=production]", "checks that current branch is a ff with master, checks with ci, merges into master, deploys to target, and then deletes branch."
|
38
|
+
def deploy to=nil
|
39
|
+
branch = Git.current_branch
|
40
|
+
|
37
41
|
if branch == "master"
|
38
42
|
run_crucial "git push origin master:master"
|
39
|
-
invoke :ci unless options["skip-ci"]
|
43
|
+
invoke :ci, [branch], options.slice("local-ci") unless options["skip-ci"]
|
40
44
|
|
41
45
|
else
|
42
46
|
run_crucial "git fetch origin master:master"
|
43
47
|
|
44
|
-
|
48
|
+
unless Git.fast_forward_merge?("origin/master", branch)
|
45
49
|
puts "The master branch has advanced. Attempting rebase..."
|
46
50
|
run_crucial "git rebase origin/master"
|
47
51
|
end
|
48
52
|
|
49
53
|
run_crucial "git push -f origin #{branch}:#{branch}"
|
50
54
|
|
51
|
-
invoke :ci unless options["skip-ci"]
|
55
|
+
invoke :ci, [branch], options.slice("local-ci") unless options["skip-ci"]
|
52
56
|
|
53
57
|
run_crucial "git push origin #{branch}:master"
|
54
58
|
run_crucial "git fetch origin master:master"
|
55
59
|
end
|
56
60
|
|
57
|
-
|
61
|
+
if `git remote` =~ /\bgithub\b/
|
62
|
+
run_crucial "git push github"
|
63
|
+
end
|
64
|
+
|
65
|
+
to ||= @config.servers.key?(:production) ? :production : :staging
|
66
|
+
|
67
|
+
command = "git pull origin master && bin/setup"
|
68
|
+
run_crucial ssh_command(to, command)
|
58
69
|
|
59
70
|
puts green("Deploy Succeeded")
|
60
71
|
|
@@ -62,26 +73,20 @@ class Bard::CLI < Thor
|
|
62
73
|
puts "Deleting branch: #{branch}"
|
63
74
|
run_crucial "git push --delete origin #{branch}"
|
64
75
|
|
65
|
-
|
66
|
-
when branch
|
76
|
+
if branch == Git.current_branch
|
67
77
|
run_crucial "git checkout master"
|
68
|
-
run_crucial "git branch -d #{branch}"
|
69
|
-
when "master"
|
70
|
-
run_crucial "git branch -d #{branch}"
|
71
|
-
else
|
72
|
-
run_crucial "git branch -D #{branch}"
|
73
78
|
end
|
74
|
-
end
|
75
79
|
|
76
|
-
|
77
|
-
puts red("Production is now down!")
|
80
|
+
run_crucial "git branch -D #{branch}"
|
78
81
|
end
|
82
|
+
|
83
|
+
ping to
|
79
84
|
end
|
80
85
|
|
81
|
-
method_options %w
|
86
|
+
method_options %w[verbose -v] => :boolean, %w[local-ci -l] => :boolean
|
82
87
|
desc "ci [BRANCH=HEAD]", "runs ci against BRANCH"
|
83
88
|
def ci branch=Git.current_branch
|
84
|
-
ci = CI.new(project_name, `git rev-parse #{branch}`.chomp)
|
89
|
+
ci = CI.new(project_name, `git rev-parse #{branch}`.chomp, local: options["local-ci"])
|
85
90
|
if ci.exists?
|
86
91
|
puts "Continuous integration: starting build on #{branch}..."
|
87
92
|
|
@@ -100,9 +105,9 @@ class Bard::CLI < Thor
|
|
100
105
|
if success
|
101
106
|
puts
|
102
107
|
puts "Continuous integration: success!"
|
103
|
-
if File.exist?("coverage")
|
108
|
+
if !options["local-ci"] && File.exist?("coverage")
|
104
109
|
puts "Downloading test coverage from CI..."
|
105
|
-
|
110
|
+
download_ci_test_coverage
|
106
111
|
end
|
107
112
|
puts "Deploying..."
|
108
113
|
else
|
@@ -132,16 +137,16 @@ class Bard::CLI < Thor
|
|
132
137
|
end
|
133
138
|
end
|
134
139
|
|
135
|
-
method_options %w
|
140
|
+
method_options %w[home] => :boolean
|
136
141
|
desc "ssh [TO=production]", "logs into the specified server via SSH"
|
137
|
-
def ssh to
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
command = %(
|
142
|
-
exec command
|
142
|
+
def ssh to=:production
|
143
|
+
command = "exec $SHELL -l"
|
144
|
+
if to == "gubs" && !options["home"]
|
145
|
+
server = @config.servers[:gubs]
|
146
|
+
command = %(bash -lic "exec ./vagrant \\"cd #{server.path} && #{command}\\"")
|
147
|
+
exec ssh_command(to, command, home: true)
|
143
148
|
else
|
144
|
-
exec
|
149
|
+
exec ssh_command(to, command, home: options["home"])
|
145
150
|
end
|
146
151
|
end
|
147
152
|
|
@@ -150,5 +155,39 @@ class Bard::CLI < Thor
|
|
150
155
|
install_files_path = File.expand_path(File.join(__dir__, "../install_files/*"))
|
151
156
|
system "cp #{install_files_path} bin/"
|
152
157
|
end
|
158
|
+
|
159
|
+
desc "ping [SERVER=production]", "hits the server over http to verify that its up."
|
160
|
+
def ping server=:production
|
161
|
+
server = @config.servers[server.to_sym]
|
162
|
+
return false if server.ping == false
|
163
|
+
|
164
|
+
url = server.default_ping
|
165
|
+
if server.ping =~ %r{^/}
|
166
|
+
url += server.ping
|
167
|
+
elsif server.ping.to_s.length > 0
|
168
|
+
url = server.ping
|
169
|
+
end
|
170
|
+
|
171
|
+
command = "curl -sfL #{url} 2>&1 1>/dev/null"
|
172
|
+
unless system command
|
173
|
+
puts "#{server.to_s.capitalize} is down!"
|
174
|
+
exit 1
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
desc "master_key [FROM=production, TO=local]", "copy master key from FROM to TO"
|
179
|
+
def master_key from="production", to="local"
|
180
|
+
if to == "local"
|
181
|
+
copy :from, from, "config/master.key"
|
182
|
+
end
|
183
|
+
if from == "local"
|
184
|
+
copy :to, to, "config/master.key"
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
desc "download_ci_test_coverage", "download latest test coverage information from CI"
|
189
|
+
def download_ci_test_coverage
|
190
|
+
rsync :from, :ci, "coverage"
|
191
|
+
end
|
153
192
|
end
|
154
193
|
|
data/lib/bard/base.rb
CHANGED
@@ -1,28 +1,79 @@
|
|
1
1
|
require "thor"
|
2
2
|
require "term/ansicolor"
|
3
|
-
require "
|
3
|
+
require "open3"
|
4
4
|
|
5
5
|
class Bard::CLI < Thor
|
6
6
|
include Term::ANSIColor
|
7
7
|
|
8
8
|
private
|
9
9
|
|
10
|
-
def fatal(message)
|
11
|
-
raise red("!!! ") + message
|
12
|
-
end
|
13
|
-
|
14
10
|
def run_crucial(command, verbose = false)
|
15
|
-
|
16
|
-
|
17
|
-
if verbose
|
11
|
+
stdout, stderr, status = Open3.capture3(command)
|
12
|
+
failed = status.to_i.nonzero?
|
13
|
+
if verbose || failed
|
18
14
|
$stdout.puts stdout
|
19
15
|
$stderr.puts stderr
|
20
16
|
end
|
17
|
+
if failed
|
18
|
+
puts red("!!! ") + "Running command failed: #{yellow(command)}"
|
19
|
+
exit 1
|
20
|
+
end
|
21
21
|
stdout.chomp
|
22
22
|
end
|
23
23
|
|
24
24
|
def project_name
|
25
25
|
@project_name ||= File.expand_path(".").split("/").last
|
26
26
|
end
|
27
|
+
|
28
|
+
def ssh_command server, command, home: false
|
29
|
+
server = @config.servers[server.to_sym]
|
30
|
+
uri = URI.parse("ssh://#{server.ssh}")
|
31
|
+
command = "cd #{server.path} && #{command}" unless home
|
32
|
+
command = "ssh -tt #{"-p#{uri.port} " if uri.port}#{uri.user}@#{uri.host} '#{command}'"
|
33
|
+
if server.gateway
|
34
|
+
uri = URI.parse("ssh://#{server.gateway}")
|
35
|
+
command = "ssh -tt #{" -p#{uri.port} " if uri.port}#{uri.user}@#{uri.host} \"#{command}\""
|
36
|
+
end
|
37
|
+
command
|
38
|
+
end
|
39
|
+
|
40
|
+
def copy direction, server, path
|
41
|
+
server = @config.servers[server.to_sym]
|
42
|
+
|
43
|
+
uri = URI.parse("ssh://#{server.gateway}")
|
44
|
+
port = uri.port ? "-p#{uri.port}" : ""
|
45
|
+
gateway = server.gateway ? "-oProxyCommand='ssh #{port} #{uri.user}@#{uri.host} -W %h:%p'" : ""
|
46
|
+
|
47
|
+
uri = URI.parse("ssh://#{server.ssh}")
|
48
|
+
port = uri.port ? "-P#{uri.port}" : ""
|
49
|
+
from_and_to = [path, "#{uri.user}@#{uri.host}:#{server.path}/#{path}"]
|
50
|
+
|
51
|
+
from_and_to.reverse! if direction == :from
|
52
|
+
command = "scp #{gateway} #{port} #{from_and_to.join(" ")}"
|
53
|
+
|
54
|
+
run_crucial command
|
55
|
+
end
|
56
|
+
|
57
|
+
def rsync direction, server, path
|
58
|
+
server = @config.servers[server.to_sym]
|
59
|
+
|
60
|
+
uri = URI.parse("ssh://#{server.gateway}")
|
61
|
+
port = uri.port ? "-p#{uri.port}" : ""
|
62
|
+
gateway = server.gateway ? "-oProxyCommand=\"ssh #{port} #{uri.user}@#{uri.host} -W %h:%p\"" : ""
|
63
|
+
|
64
|
+
uri = URI.parse("ssh://#{server.ssh}")
|
65
|
+
port = uri.port ? "-p#{uri.port}" : ""
|
66
|
+
ssh = "-e'ssh #{port} #{gateway}'"
|
67
|
+
|
68
|
+
dest_path = path.dup
|
69
|
+
dest_path = "./#{dest_path}"
|
70
|
+
from_and_to = [dest_path, "#{uri.user}@#{uri.host}:#{server.path}/#{path}"]
|
71
|
+
from_and_to.reverse! if direction == :from
|
72
|
+
from_and_to[-1].sub! %r(/[^/]+$), '/'
|
73
|
+
|
74
|
+
command = "rsync #{ssh} --delete -avz #{from_and_to.join(" ")}"
|
75
|
+
|
76
|
+
run_crucial command
|
77
|
+
end
|
27
78
|
end
|
28
79
|
|
data/lib/bard/ci.rb
CHANGED
@@ -1,69 +1,142 @@
|
|
1
|
+
require "json"
|
2
|
+
require "forwardable"
|
3
|
+
require "open3"
|
4
|
+
|
1
5
|
class Bard::CLI < Thor
|
2
|
-
class CI
|
3
|
-
def
|
4
|
-
|
5
|
-
|
6
|
-
|
6
|
+
class CI
|
7
|
+
def initialize project_name, sha, local: false
|
8
|
+
@project_name = project_name
|
9
|
+
@sha = sha
|
10
|
+
@local = !!local
|
11
|
+
@runner = @local ? Local.new(project_name, sha) : Remote.new(project_name, sha)
|
12
|
+
end
|
7
13
|
|
8
|
-
|
9
|
-
while building?
|
10
|
-
elapsed_time = Time.new.to_i - start_time
|
11
|
-
yield elapsed_time, last_time_elapsed
|
12
|
-
sleep(2)
|
13
|
-
end
|
14
|
+
attr_reader :project_name, :sha, :runner
|
14
15
|
|
15
|
-
|
16
|
+
def local?
|
17
|
+
@local
|
16
18
|
end
|
17
19
|
|
18
|
-
|
19
|
-
`curl -s -I #{ci_host}/?token=botandrose` =~ /\b200 OK\b/
|
20
|
-
end
|
20
|
+
extend Forwardable
|
21
21
|
|
22
|
-
|
23
|
-
raw = `curl -s #{ci_host}/lastBuild/console?token=botandrose`
|
24
|
-
raw[%r{<pre.*?>(.+)</pre>}m, 1]
|
25
|
-
end
|
22
|
+
delegate [:run, :exists?, :console, :last_response] => :runner
|
26
23
|
|
27
|
-
|
24
|
+
class Remote < Struct.new(:project_name, :sha)
|
25
|
+
def run
|
26
|
+
last_time_elapsed = get_last_time_elapsed
|
27
|
+
start
|
28
|
+
sleep(2) until started?
|
28
29
|
|
29
|
-
|
30
|
+
start_time = Time.new.to_i
|
31
|
+
while building?
|
32
|
+
elapsed_time = Time.new.to_i - start_time
|
33
|
+
yield elapsed_time, last_time_elapsed
|
34
|
+
sleep(2)
|
35
|
+
end
|
30
36
|
|
31
|
-
|
32
|
-
|
33
|
-
response.match(/<duration>(\d+)<\/duration>/)
|
34
|
-
$1 ? $1.to_i / 1000 : nil
|
35
|
-
end
|
37
|
+
success?
|
38
|
+
end
|
36
39
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
+
def exists?
|
41
|
+
`curl -s -I #{ci_host}/?token=botandrose` =~ /\b200 OK\b/
|
42
|
+
end
|
40
43
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
end
|
44
|
+
def console
|
45
|
+
raw = `curl -s #{ci_host}/lastBuild/console?token=botandrose`
|
46
|
+
raw[%r{<pre.*?>(.+)</pre>}m, 1]
|
47
|
+
end
|
46
48
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
output =~ /"queueId":#{@queueId}\b/
|
51
|
-
end
|
49
|
+
attr_accessor :last_response
|
50
|
+
|
51
|
+
private
|
52
52
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
53
|
+
def get_last_time_elapsed
|
54
|
+
response = `curl -s #{ci_host}/lastStableBuild/api/xml?token=botandrose`
|
55
|
+
response.match(/<duration>(\d+)<\/duration>/)
|
56
|
+
$1 ? $1.to_i / 1000 : nil
|
57
|
+
end
|
58
|
+
|
59
|
+
def ci_host
|
60
|
+
"http://botandrose:thecakeisalie!@ci.botandrose.com/job/#{project_name}"
|
57
61
|
end
|
58
|
-
end
|
59
62
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
+
def start
|
64
|
+
command = "curl -s -I -X POST '#{ci_host}/buildWithParameters?token=botandrose&GIT_REF=#{sha}'"
|
65
|
+
output = `#{command}`
|
66
|
+
@queueId = output[%r{Location: .+/queue/item/(\d+)/}, 1].to_i
|
67
|
+
end
|
68
|
+
|
69
|
+
def started?
|
70
|
+
command = "curl -s -g '#{ci_host}/api/json?depth=1&tree=builds[queueId,number]'"
|
71
|
+
output = `#{command}`
|
72
|
+
JSON.parse(output)["builds"][0]["queueId"] == @queueId
|
73
|
+
end
|
74
|
+
|
75
|
+
def job_id
|
76
|
+
@job_id ||= begin
|
77
|
+
output = `curl -s -g '#{ci_host}/api/json?depth=1&tree=builds[queueId,number]'`
|
78
|
+
output[/"number":(\d+),"queueId":#{@queueId}\b/, 1].to_i
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def building?
|
83
|
+
self.last_response = `curl -s #{ci_host}/#{job_id}/api/json?tree=building,result`
|
84
|
+
if last_response.blank?
|
85
|
+
sleep(2) # retry
|
86
|
+
self.last_response = `curl -s #{ci_host}/#{job_id}/api/json?tree=building,result`
|
87
|
+
if last_response.blank?
|
88
|
+
raise "Blank response from CI twice in a row. Aborting!"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
last_response.include? '"building":true'
|
92
|
+
end
|
93
|
+
|
94
|
+
def success?
|
95
|
+
last_response.include? '"result":"SUCCESS"'
|
96
|
+
end
|
63
97
|
end
|
64
98
|
|
65
|
-
|
66
|
-
|
99
|
+
class Local < Struct.new(:project_name, :sha)
|
100
|
+
def run
|
101
|
+
start
|
102
|
+
|
103
|
+
start_time = Time.new.to_i
|
104
|
+
while building?
|
105
|
+
elapsed_time = Time.new.to_i - start_time
|
106
|
+
yield elapsed_time, nil
|
107
|
+
sleep(2)
|
108
|
+
end
|
109
|
+
|
110
|
+
@stdin.close
|
111
|
+
@console = @stdout_and_stderr.read
|
112
|
+
@stdout_and_stderr.close
|
113
|
+
|
114
|
+
success?
|
115
|
+
end
|
116
|
+
|
117
|
+
def exists?
|
118
|
+
true
|
119
|
+
end
|
120
|
+
|
121
|
+
def console
|
122
|
+
@console
|
123
|
+
end
|
124
|
+
|
125
|
+
attr_accessor :last_response
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
def start
|
130
|
+
@stdin, @stdout_and_stderr, @wait_thread = Open3.popen2e("bin/rake ci")
|
131
|
+
end
|
132
|
+
|
133
|
+
def building?
|
134
|
+
![nil, false].include?(@wait_thread.status)
|
135
|
+
end
|
136
|
+
|
137
|
+
def success?
|
138
|
+
@wait_thread.value.success?
|
139
|
+
end
|
67
140
|
end
|
68
141
|
end
|
69
142
|
end
|
data/lib/bard/config.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
class Bard::CLI < Thor
|
2
|
+
class Config
|
3
|
+
def initialize project_name, path
|
4
|
+
@project_name = project_name
|
5
|
+
@servers = {
|
6
|
+
local: Server.new(
|
7
|
+
project_name,
|
8
|
+
:local,
|
9
|
+
false,
|
10
|
+
"./",
|
11
|
+
false,
|
12
|
+
),
|
13
|
+
gubs: Server.new(
|
14
|
+
project_name,
|
15
|
+
:gubs,
|
16
|
+
"gubito@gubs.pagekite.me",
|
17
|
+
"Sites/#{project_name}",
|
18
|
+
false,
|
19
|
+
),
|
20
|
+
ci: Server.new(
|
21
|
+
project_name,
|
22
|
+
:ci,
|
23
|
+
"jenkins@ci.botandrose.com:22022",
|
24
|
+
"jobs/#{project_name}/workspace",
|
25
|
+
false,
|
26
|
+
),
|
27
|
+
staging: Server.new(
|
28
|
+
project_name,
|
29
|
+
:staging,
|
30
|
+
"www@#{project_name}.botandrose.com:22022",
|
31
|
+
),
|
32
|
+
}
|
33
|
+
load_local_config! path
|
34
|
+
end
|
35
|
+
|
36
|
+
attr_reader :servers
|
37
|
+
|
38
|
+
def server key, &block
|
39
|
+
@servers[key] ||= Server.new(@project_name, key)
|
40
|
+
@servers[key].instance_eval &block if block_given?
|
41
|
+
@servers[key]
|
42
|
+
end
|
43
|
+
|
44
|
+
def data *paths
|
45
|
+
if paths.length == 0
|
46
|
+
Array(@data)
|
47
|
+
else
|
48
|
+
@data = paths
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def load_local_config! path
|
55
|
+
instance_eval File.read(File.expand_path(path)) if File.exist?(path)
|
56
|
+
end
|
57
|
+
|
58
|
+
class Server < Struct.new(:project_name, :key, :ssh, :path, :ping, :gateway)
|
59
|
+
def self.setting *fields
|
60
|
+
fields.each do |field|
|
61
|
+
define_method field do |*args|
|
62
|
+
if args.length == 1
|
63
|
+
send :"#{field}=", args.first
|
64
|
+
elsif args.length == 0
|
65
|
+
super()
|
66
|
+
else
|
67
|
+
raise ArgumentError
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
setting :ssh, :path, :ping, :gateway
|
74
|
+
|
75
|
+
def default_ping
|
76
|
+
uri = URI.parse("ssh://#{ssh}")
|
77
|
+
"http://#{uri.host}"
|
78
|
+
end
|
79
|
+
|
80
|
+
def path
|
81
|
+
super || project_name
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
data/lib/bard/data.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
class Bard::CLI < Thor
|
2
|
+
class Data < Struct.new(:bard, :from, :to)
|
3
|
+
def call
|
4
|
+
if to == "local"
|
5
|
+
data_pull_db from.to_sym
|
6
|
+
data_pull_assets from.to_sym
|
7
|
+
end
|
8
|
+
if from == "local"
|
9
|
+
data_push_db to.to_sym
|
10
|
+
data_push_assets to.to_sym
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def data_pull_db server
|
17
|
+
bard.instance_eval do
|
18
|
+
run_crucial ssh_command(server, "bin/rake db:dump && gzip -9f db/data.sql")
|
19
|
+
copy :from, server, "db/data.sql.gz"
|
20
|
+
run_crucial "gunzip -f db/data.sql.gz && bin/rake db:load"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def data_push_db server
|
25
|
+
bard.instance_eval do
|
26
|
+
run_crucial "bin/rake db:dump && gzip -9f db/data.sql"
|
27
|
+
copy :to, server, "db/data.sql.gz"
|
28
|
+
run_crucial ssh_command(server, "gunzip -f db/data.sql.gz && bin/rake db:load")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def data_pull_assets server
|
33
|
+
bard.instance_eval do
|
34
|
+
@config.data.each do |path|
|
35
|
+
rsync :from, server, path
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def data_push_assets server
|
41
|
+
bard.instance_eval do
|
42
|
+
@config.data.each do |path|
|
43
|
+
rsync :to, server, path
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
data/lib/bard/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bard
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.42.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Micah Geisel
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-04-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -24,34 +24,6 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 0.19.0
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: capistrano
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - '='
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: 2.5.10
|
34
|
-
type: :runtime
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - '='
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: 2.5.10
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: net-ssh
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - "~>"
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '4.0'
|
48
|
-
type: :runtime
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - "~>"
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '4.0'
|
55
27
|
- !ruby/object:Gem::Dependency
|
56
28
|
name: rvm
|
57
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,34 +38,6 @@ dependencies:
|
|
66
38
|
- - ">="
|
67
39
|
- !ruby/object:Gem::Version
|
68
40
|
version: '0'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: rvm-capistrano
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - ">="
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '0'
|
76
|
-
type: :runtime
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - ">="
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '0'
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: systemu
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - ">="
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: 1.2.0
|
90
|
-
type: :runtime
|
91
|
-
prerelease: false
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
93
|
-
requirements:
|
94
|
-
- - ">="
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: 1.2.0
|
97
41
|
- !ruby/object:Gem::Dependency
|
98
42
|
name: term-ansicolor
|
99
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -174,8 +118,6 @@ extra_rdoc_files: []
|
|
174
118
|
files:
|
175
119
|
- ".gitignore"
|
176
120
|
- ".gitmodules"
|
177
|
-
- ".ruby-gemset"
|
178
|
-
- ".ruby-version"
|
179
121
|
- Gemfile
|
180
122
|
- LICENSE
|
181
123
|
- README.rdoc
|
@@ -203,8 +145,9 @@ files:
|
|
203
145
|
- install_files/specified_yarn.rb
|
204
146
|
- lib/bard.rb
|
205
147
|
- lib/bard/base.rb
|
206
|
-
- lib/bard/capistrano.rb
|
207
148
|
- lib/bard/ci.rb
|
149
|
+
- lib/bard/config.rb
|
150
|
+
- lib/bard/data.rb
|
208
151
|
- lib/bard/git.rb
|
209
152
|
- lib/bard/version.rb
|
210
153
|
- spec/bard_spec.rb
|
@@ -228,8 +171,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
228
171
|
- !ruby/object:Gem::Version
|
229
172
|
version: '0'
|
230
173
|
requirements: []
|
231
|
-
|
232
|
-
rubygems_version: 2.6.14
|
174
|
+
rubygems_version: 3.0.3
|
233
175
|
signing_key:
|
234
176
|
specification_version: 4
|
235
177
|
summary: CLI to automate common development tasks.
|
data/.ruby-gemset
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
bard
|
data/.ruby-version
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
ruby-2.4.3
|
data/lib/bard/capistrano.rb
DELETED
@@ -1,131 +0,0 @@
|
|
1
|
-
require 'uri'
|
2
|
-
|
3
|
-
Capistrano::Configuration.instance(:must_exist).load do
|
4
|
-
require "rvm/capistrano"
|
5
|
-
set :rvm_type, :user
|
6
|
-
ruby_version = File.read(".ruby-version").chomp
|
7
|
-
ruby_gemset = File.read(".ruby-gemset").chomp
|
8
|
-
set :rvm_ruby_string, [ruby_version, ruby_gemset].join("@")
|
9
|
-
|
10
|
-
set :application, File.basename(Dir.pwd)
|
11
|
-
|
12
|
-
role :staging, "www@staging.botandrose.com:22022"
|
13
|
-
role :ci, "jenkins@ci.botandrose.com:22022"
|
14
|
-
|
15
|
-
set :asset_paths, []
|
16
|
-
|
17
|
-
namespace "data" do
|
18
|
-
namespace "pull" do
|
19
|
-
desc "pull data"
|
20
|
-
task "default" do
|
21
|
-
run "cd #{application} && bundle exec rake db:dump && gzip -9f db/data.sql"
|
22
|
-
transfer :down, "#{application}/db/data.sql.gz", "db/data.sql.gz"
|
23
|
-
system "gunzip -f db/data.sql.gz && bundle exec rake db:load"
|
24
|
-
end
|
25
|
-
|
26
|
-
desc "sync the static assets"
|
27
|
-
task "assets" do
|
28
|
-
uri = URI.parse("ssh://#{roles[ENV['ROLES'].to_sym].first.to_s}")
|
29
|
-
portopt = "-e'ssh -p#{uri.port}'" if uri.port
|
30
|
-
|
31
|
-
[asset_paths].flatten.each do |path|
|
32
|
-
dest_path = path.dup
|
33
|
-
dest_path.sub! %r(/[^/]+$), '/'
|
34
|
-
system "rsync #{portopt} --delete -avz #{uri.user}@#{uri.host}:#{application}/#{path} #{dest_path}"
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
namespace "push" do
|
40
|
-
desc "push data"
|
41
|
-
task "default" do
|
42
|
-
system "bundle exec rake db:dump && gzip -9f db/data.sql"
|
43
|
-
transfer :up, "db/data.sql.gz", "#{application}/db/data.sql.gz"
|
44
|
-
run "cd #{application} && gunzip -f db/data.sql.gz && bundle exec rake db:load"
|
45
|
-
end
|
46
|
-
|
47
|
-
desc "sync the static assets"
|
48
|
-
task "assets" do
|
49
|
-
uri = URI.parse("ssh://#{roles[ENV['ROLES'].to_sym].first.to_s}")
|
50
|
-
portopt = "-e'ssh -p#{uri.port}'" if uri.port
|
51
|
-
|
52
|
-
[asset_paths].flatten.each do |path|
|
53
|
-
dest_path = path.dup
|
54
|
-
dest_path.sub! %r(/[^/]+$), '/'
|
55
|
-
system "rsync #{portopt} --delete -avz #{path} #{uri.user}@#{uri.host}:#{application}/#{dest_path}"
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
after 'data:pull', 'data:pull:assets'
|
62
|
-
after 'data:push', 'data:push:assets'
|
63
|
-
|
64
|
-
desc "push app to production"
|
65
|
-
task :deploy do
|
66
|
-
deploy_roles = roles.keys.include?(:production) ? :production : :staging
|
67
|
-
ENV["ROLES"] = deploy_roles.to_s
|
68
|
-
find_and_execute_task "rvm:install_ruby"
|
69
|
-
system "git push github" if `git remote` =~ /\bgithub\b/
|
70
|
-
run "cd #{application} && git pull origin master && bin/setup", roles: deploy_roles
|
71
|
-
end
|
72
|
-
|
73
|
-
desc "push app to staging"
|
74
|
-
task :stage do
|
75
|
-
ENV["ROLES"] = "staging"
|
76
|
-
find_and_execute_task "rvm:install_ruby"
|
77
|
-
branch = ENV.fetch("BRANCH")
|
78
|
-
run "cd #{application} && git fetch && git checkout -f origin/#{branch} && bin/setup", :roles => :staging
|
79
|
-
end
|
80
|
-
|
81
|
-
desc "test app for downtime"
|
82
|
-
task :ping do
|
83
|
-
deploy_roles = roles.keys.include?(:production) ? :production : :staging
|
84
|
-
ENV["ROLES"] = deploy_roles.to_s
|
85
|
-
role_name = ENV.fetch("ROLES", "production").to_sym
|
86
|
-
server_definition = roles[role_name].first
|
87
|
-
|
88
|
-
url = server_definition.host
|
89
|
-
if role_name == :staging && server_definition.host == "staging.botandrose.com"
|
90
|
-
url = "#{application}.botandrose.com"
|
91
|
-
end
|
92
|
-
|
93
|
-
ping_option = server_definition.options.fetch(:ping, "")
|
94
|
-
if ping_option =~ %r{^/}
|
95
|
-
url += ping_option
|
96
|
-
elsif ping_option.to_s.length > 0
|
97
|
-
url = ping_option
|
98
|
-
end
|
99
|
-
|
100
|
-
command = "curl -sfL #{url} 2>&1 1>/dev/null"
|
101
|
-
unless system command
|
102
|
-
puts "#{role_name.to_s.capitalize} is down!"
|
103
|
-
exit 1
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
desc "push master key"
|
108
|
-
task :push_master_key do
|
109
|
-
transfer :up, "config/master.key", "#{application}/config/master.key"
|
110
|
-
end
|
111
|
-
|
112
|
-
desc "pull master key"
|
113
|
-
task :pull_master_key do
|
114
|
-
transfer :down, "#{application}/config/master.key", "config/master.key"
|
115
|
-
end
|
116
|
-
|
117
|
-
desc "log in via ssh"
|
118
|
-
task :ssh do
|
119
|
-
role = ENV['ROLES'].to_sym
|
120
|
-
path = role == :ci ? "jobs/#{application}/workspace" : application
|
121
|
-
uri = URI.parse("ssh://#{roles[role].first.to_s}")
|
122
|
-
exec "ssh -t #{"-p#{uri.port} " if uri.port}#{uri.user}@#{uri.host} '#{"cd #{path} && " unless ENV['NOCD']}exec $SHELL -l'"
|
123
|
-
end
|
124
|
-
|
125
|
-
desc "download latest test coverage information from CI"
|
126
|
-
task :download_ci_test_coverage do
|
127
|
-
uri = URI.parse("ssh://#{roles[:staging].first}")
|
128
|
-
portopt = "-e'ssh -p#{uri.port}'" if uri.port
|
129
|
-
system "rsync #{portopt} --delete -avz #{uri.user}@#{uri.host}:~jenkins/jobs/#{application}/workspace/coverage ./"
|
130
|
-
end
|
131
|
-
end
|