bard 0.41.2 → 0.44.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 +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/install_files/specified_node.rb +13 -27
- data/install_files/specified_yarn.rb +6 -9
- data/install_files/yarn +5 -0
- data/lib/bard.rb +90 -43
- data/lib/bard/base.rb +59 -8
- data/lib/bard/ci.rb +126 -49
- data/lib/bard/config.rb +91 -0
- data/lib/bard/data.rb +49 -0
- data/lib/bard/version.rb +1 -1
- metadata +6 -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: 86e79ccc420d8bed3ff834d150b9f58da74e34981dc59a244dd92de38fb5e831
|
4
|
+
data.tar.gz: 816db71d99a04cb75aff222369e41e719de7da1b608fc518b74e9ec3ba5a5ec0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 70fedcdd193a7588a6342a7efee99ddf95598599d80b50d48206fb29674e1dcf002ac3a239c0e836630190a1b5e8552d2bfe8ecc53f600fc031e3d44330631ac
|
7
|
+
data.tar.gz: 1c2dc2a6b0f6c6b225b520f7d801f8ae0dfd16b4007302db9ab2b713036f1bf89a4a5e4cd9f0bd422e23c5c3830b45ea94095283a780540fb1e87c88c0559917
|
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
@@ -1,45 +1,31 @@
|
|
1
1
|
module SpecifiedNode
|
2
|
-
class NVMError < StandardError; end
|
3
|
-
|
4
|
-
NVM_PATH = File.expand_path("~/.nvm/nvm.sh")
|
5
|
-
|
6
2
|
extend self
|
7
3
|
|
4
|
+
NODE_VERSION = "v12.16.1"
|
5
|
+
NODE_PATH = "tmp/node-#{NODE_VERSION}-linux-x64/bin/node"
|
6
|
+
|
8
7
|
def ensure!
|
9
|
-
install_nvm unless nvm_installed?
|
10
|
-
restart unless nvm_active?
|
11
8
|
install_node unless node_installed?
|
12
|
-
|
9
|
+
install_binstub
|
10
|
+
"bin/node --version"
|
13
11
|
end
|
14
12
|
|
15
13
|
private
|
16
14
|
|
17
|
-
def install_nvm
|
18
|
-
system("curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash")\
|
19
|
-
or raise "Couldn't install nvm"
|
20
|
-
end
|
21
|
-
|
22
|
-
def nvm_installed?
|
23
|
-
File.exist?(NVM_PATH)
|
24
|
-
end
|
25
|
-
|
26
15
|
def install_node
|
27
|
-
|
28
|
-
end
|
29
|
-
|
30
|
-
def node_installed?
|
31
|
-
nvm "use"
|
16
|
+
system("wget -cO- https://nodejs.org/dist/#{NODE_VERSION}/node-#{NODE_VERSION}-linux-x64.tar.xz | tar xJ -C tmp/")
|
32
17
|
end
|
33
18
|
|
34
|
-
def
|
35
|
-
|
19
|
+
def install_binstub
|
20
|
+
system("cd bin && ln -fs ../#{NODE_PATH}")
|
36
21
|
end
|
37
22
|
|
38
|
-
def
|
39
|
-
|
23
|
+
def node_installed?
|
24
|
+
File.exist?(NODE_PATH) && `#{NODE_PATH} --version`.chomp == NODE_VERSION
|
40
25
|
end
|
41
26
|
|
42
|
-
def
|
43
|
-
|
27
|
+
def binstub_installed?
|
28
|
+
File.exist?("bin/node")
|
44
29
|
end
|
45
30
|
end
|
31
|
+
|
@@ -1,34 +1,31 @@
|
|
1
1
|
module SpecifiedYarn
|
2
2
|
extend self
|
3
3
|
|
4
|
-
|
4
|
+
YARN_VERSION = "v1.22.0"
|
5
|
+
YARN_PATH = "tmp/yarn-#{YARN_VERSION}/bin/yarn.js"
|
5
6
|
|
6
7
|
def ensure!
|
7
8
|
install_yarn unless yarn_installed?
|
8
|
-
install_binstub
|
9
|
+
install_binstub
|
9
10
|
"bin/yarn install"
|
10
11
|
end
|
11
12
|
|
12
13
|
private
|
13
14
|
|
14
15
|
def install_yarn
|
15
|
-
system(".
|
16
|
+
system("wget -cO- https://github.com/yarnpkg/yarn/releases/download/#{YARN_VERSION}/yarn-#{YARN_VERSION}.tar.gz | tar -xz -C tmp/")
|
16
17
|
end
|
17
18
|
|
18
19
|
def install_binstub
|
19
|
-
system("cd bin && ln -
|
20
|
+
system("cd bin && ln -fs ../#{YARN_PATH}")
|
20
21
|
end
|
21
22
|
|
22
23
|
def yarn_installed?
|
23
|
-
File.exist?(YARN_PATH) &&
|
24
|
+
File.exist?(YARN_PATH) && `bin/node #{YARN_PATH} --version`.chomp == YARN_VERSION[1..-1]
|
24
25
|
end
|
25
26
|
|
26
27
|
def binstub_installed?
|
27
28
|
File.exist?("bin/yarn")
|
28
29
|
end
|
29
|
-
|
30
|
-
def version
|
31
|
-
File.read("package.json")[/"yarn": "([0-9\.]+)"/, 1]
|
32
|
-
end
|
33
30
|
end
|
34
31
|
|
data/install_files/yarn
ADDED
data/lib/bard.rb
CHANGED
@@ -1,60 +1,72 @@
|
|
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
|
-
|
17
|
+
def data(from=nil, to="local")
|
18
|
+
from ||= @config.servers.key?(:production) ? "production" : "staging"
|
19
|
+
Data.new(self, from, to).call
|
14
20
|
end
|
15
21
|
|
16
22
|
method_options %w( verbose -v ) => :boolean
|
17
|
-
desc "stage", "pushes current branch, and stages it"
|
18
|
-
def stage
|
19
|
-
unless
|
23
|
+
desc "stage [BRANCH=HEAD]", "pushes current branch, and stages it"
|
24
|
+
def stage branch=Git.current_branch
|
25
|
+
unless @config.servers.key?(:production)
|
20
26
|
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
27
|
end
|
22
28
|
|
23
|
-
branch = Git.current_branch
|
24
|
-
|
25
29
|
run_crucial "git push -u origin #{branch}", true
|
26
|
-
|
30
|
+
command = "git fetch && git checkout -f origin/#{branch} && bin/setup"
|
31
|
+
run_crucial ssh_command(:staging, command)
|
27
32
|
puts green("Stage Succeeded")
|
28
33
|
|
29
|
-
|
30
|
-
puts red("Staging is now down!")
|
31
|
-
end
|
34
|
+
ping :staging
|
32
35
|
end
|
33
36
|
|
34
|
-
method_options %w
|
35
|
-
desc "deploy [
|
36
|
-
def deploy
|
37
|
+
method_options %w[verbose -v] => :boolean, %w[skip-ci] => :boolean, %w[local-ci -l] => :boolean
|
38
|
+
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."
|
39
|
+
def deploy to=nil
|
40
|
+
branch = Git.current_branch
|
41
|
+
|
37
42
|
if branch == "master"
|
38
43
|
run_crucial "git push origin master:master"
|
39
|
-
invoke :ci unless options["skip-ci"]
|
44
|
+
invoke :ci, [branch], options.slice("local-ci") unless options["skip-ci"]
|
40
45
|
|
41
46
|
else
|
42
47
|
run_crucial "git fetch origin master:master"
|
43
48
|
|
44
|
-
|
49
|
+
unless Git.fast_forward_merge?("origin/master", branch)
|
45
50
|
puts "The master branch has advanced. Attempting rebase..."
|
46
51
|
run_crucial "git rebase origin/master"
|
47
52
|
end
|
48
53
|
|
49
54
|
run_crucial "git push -f origin #{branch}:#{branch}"
|
50
55
|
|
51
|
-
invoke :ci unless options["skip-ci"]
|
56
|
+
invoke :ci, [branch], options.slice("local-ci") unless options["skip-ci"]
|
52
57
|
|
53
58
|
run_crucial "git push origin #{branch}:master"
|
54
59
|
run_crucial "git fetch origin master:master"
|
55
60
|
end
|
56
61
|
|
57
|
-
|
62
|
+
if `git remote` =~ /\bgithub\b/
|
63
|
+
run_crucial "git push github"
|
64
|
+
end
|
65
|
+
|
66
|
+
to ||= @config.servers.key?(:production) ? :production : :staging
|
67
|
+
|
68
|
+
command = "git pull origin master && bin/setup"
|
69
|
+
run_crucial ssh_command(to, command)
|
58
70
|
|
59
71
|
puts green("Deploy Succeeded")
|
60
72
|
|
@@ -62,26 +74,20 @@ class Bard::CLI < Thor
|
|
62
74
|
puts "Deleting branch: #{branch}"
|
63
75
|
run_crucial "git push --delete origin #{branch}"
|
64
76
|
|
65
|
-
|
66
|
-
when branch
|
77
|
+
if branch == Git.current_branch
|
67
78
|
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
79
|
end
|
74
|
-
end
|
75
80
|
|
76
|
-
|
77
|
-
puts red("Production is now down!")
|
81
|
+
run_crucial "git branch -D #{branch}"
|
78
82
|
end
|
83
|
+
|
84
|
+
ping to
|
79
85
|
end
|
80
86
|
|
81
|
-
method_options %w
|
87
|
+
method_options %w[verbose -v] => :boolean, %w[local-ci -l] => :boolean
|
82
88
|
desc "ci [BRANCH=HEAD]", "runs ci against BRANCH"
|
83
89
|
def ci branch=Git.current_branch
|
84
|
-
ci = CI.new(project_name, `git rev-parse #{branch}`.chomp)
|
90
|
+
ci = CI.new(project_name, `git rev-parse #{branch}`.chomp, local: options["local-ci"])
|
85
91
|
if ci.exists?
|
86
92
|
puts "Continuous integration: starting build on #{branch}..."
|
87
93
|
|
@@ -100,9 +106,9 @@ class Bard::CLI < Thor
|
|
100
106
|
if success
|
101
107
|
puts
|
102
108
|
puts "Continuous integration: success!"
|
103
|
-
if File.exist?("coverage")
|
109
|
+
if !options["local-ci"] && File.exist?("coverage")
|
104
110
|
puts "Downloading test coverage from CI..."
|
105
|
-
|
111
|
+
download_ci_test_coverage
|
106
112
|
end
|
107
113
|
puts "Deploying..."
|
108
114
|
else
|
@@ -120,6 +126,13 @@ class Bard::CLI < Thor
|
|
120
126
|
end
|
121
127
|
end
|
122
128
|
|
129
|
+
desc "open [SERVER=production]", "opens the url in the web browser."
|
130
|
+
def open server=nil
|
131
|
+
server ||= @config.servers.key?(:production) ? :production : :staging
|
132
|
+
server = @config.servers[server.to_sym]
|
133
|
+
exec "xdg-open #{server.default_ping}"
|
134
|
+
end
|
135
|
+
|
123
136
|
desc "hurt", "reruns a command until it fails"
|
124
137
|
def hurt *args
|
125
138
|
1.upto(Float::INFINITY) do |count|
|
@@ -132,16 +145,16 @@ class Bard::CLI < Thor
|
|
132
145
|
end
|
133
146
|
end
|
134
147
|
|
135
|
-
method_options %w
|
148
|
+
method_options %w[home] => :boolean
|
136
149
|
desc "ssh [TO=production]", "logs into the specified server via SSH"
|
137
|
-
def ssh to
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
command = %(
|
142
|
-
exec command
|
150
|
+
def ssh to=:production
|
151
|
+
command = "exec $SHELL -l"
|
152
|
+
if to == "gubs" && !options["home"]
|
153
|
+
server = @config.servers[:gubs]
|
154
|
+
command = %(bash -lic "exec ./vagrant \\"cd #{server.path} && #{command}\\"")
|
155
|
+
exec ssh_command(to, command, home: true)
|
143
156
|
else
|
144
|
-
exec
|
157
|
+
exec ssh_command(to, command, home: options["home"])
|
145
158
|
end
|
146
159
|
end
|
147
160
|
|
@@ -150,5 +163,39 @@ class Bard::CLI < Thor
|
|
150
163
|
install_files_path = File.expand_path(File.join(__dir__, "../install_files/*"))
|
151
164
|
system "cp #{install_files_path} bin/"
|
152
165
|
end
|
166
|
+
|
167
|
+
desc "ping [SERVER=production]", "hits the server over http to verify that its up."
|
168
|
+
def ping server=:production
|
169
|
+
server = @config.servers[server.to_sym]
|
170
|
+
return false if server.ping == false
|
171
|
+
|
172
|
+
url = server.default_ping
|
173
|
+
if server.ping =~ %r{^/}
|
174
|
+
url += server.ping
|
175
|
+
elsif server.ping.to_s.length > 0
|
176
|
+
url = server.ping
|
177
|
+
end
|
178
|
+
|
179
|
+
command = "curl -sfL #{url} 2>&1 1>/dev/null"
|
180
|
+
unless system command
|
181
|
+
puts "#{server.to_s.capitalize} is down!"
|
182
|
+
exit 1
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
desc "master_key [FROM=production, TO=local]", "copy master key from FROM to TO"
|
187
|
+
def master_key from="production", to="local"
|
188
|
+
if to == "local"
|
189
|
+
copy :from, from, "config/master.key"
|
190
|
+
end
|
191
|
+
if from == "local"
|
192
|
+
copy :to, to, "config/master.key"
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
desc "download_ci_test_coverage", "download latest test coverage information from CI"
|
197
|
+
def download_ci_test_coverage
|
198
|
+
rsync :from, :ci, "coverage"
|
199
|
+
end
|
153
200
|
end
|
154
201
|
|
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,146 @@
|
|
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}/` =~ /\b200 OK\b/
|
42
|
+
end
|
40
43
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
end
|
44
|
+
def console
|
45
|
+
raw = `curl -s #{ci_host}/lastBuild/console`
|
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`
|
55
|
+
response.match(/<duration>(\d+)<\/duration>/)
|
56
|
+
$1 ? $1.to_i / 1000 : nil
|
57
|
+
end
|
58
|
+
|
59
|
+
def auth
|
60
|
+
"botandrose:11cc2ba6ef2e43fbfbedc1f466724f6290"
|
61
|
+
end
|
62
|
+
|
63
|
+
def ci_host
|
64
|
+
"http://#{auth}@ci.botandrose.com/job/#{project_name}"
|
65
|
+
end
|
66
|
+
|
67
|
+
def start
|
68
|
+
command = "curl -s -I -X POST -L '#{ci_host}/buildWithParameters?GIT_REF=#{sha}'"
|
69
|
+
output = `#{command}`
|
70
|
+
@queueId = output[%r{Location: .+/queue/item/(\d+)/}, 1].to_i
|
71
|
+
end
|
72
|
+
|
73
|
+
def started?
|
74
|
+
command = "curl -s -g '#{ci_host}/api/json?depth=1&tree=builds[queueId,number]'"
|
75
|
+
output = `#{command}`
|
76
|
+
JSON.parse(output)["builds"][0]["queueId"] == @queueId
|
57
77
|
end
|
58
|
-
end
|
59
78
|
|
60
|
-
|
61
|
-
|
62
|
-
|
79
|
+
def job_id
|
80
|
+
@job_id ||= begin
|
81
|
+
output = `curl -s -g '#{ci_host}/api/json?depth=1&tree=builds[queueId,number]'`
|
82
|
+
output[/"number":(\d+),"queueId":#{@queueId}\b/, 1].to_i
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def building?
|
87
|
+
self.last_response = `curl -s #{ci_host}/#{job_id}/api/json?tree=building,result`
|
88
|
+
if last_response.blank?
|
89
|
+
sleep(2) # retry
|
90
|
+
self.last_response = `curl -s #{ci_host}/#{job_id}/api/json?tree=building,result`
|
91
|
+
if last_response.blank?
|
92
|
+
raise "Blank response from CI twice in a row. Aborting!"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
last_response.include? '"building":true'
|
96
|
+
end
|
97
|
+
|
98
|
+
def success?
|
99
|
+
last_response.include? '"result":"SUCCESS"'
|
100
|
+
end
|
63
101
|
end
|
64
102
|
|
65
|
-
|
66
|
-
|
103
|
+
class Local < Struct.new(:project_name, :sha)
|
104
|
+
def run
|
105
|
+
start
|
106
|
+
|
107
|
+
start_time = Time.new.to_i
|
108
|
+
while building?
|
109
|
+
elapsed_time = Time.new.to_i - start_time
|
110
|
+
yield elapsed_time, nil
|
111
|
+
sleep(2)
|
112
|
+
end
|
113
|
+
|
114
|
+
@stdin.close
|
115
|
+
@console = @stdout_and_stderr.read
|
116
|
+
@stdout_and_stderr.close
|
117
|
+
|
118
|
+
success?
|
119
|
+
end
|
120
|
+
|
121
|
+
def exists?
|
122
|
+
true
|
123
|
+
end
|
124
|
+
|
125
|
+
def console
|
126
|
+
@console
|
127
|
+
end
|
128
|
+
|
129
|
+
attr_accessor :last_response
|
130
|
+
|
131
|
+
private
|
132
|
+
|
133
|
+
def start
|
134
|
+
@stdin, @stdout_and_stderr, @wait_thread = Open3.popen2e("bin/rake ci")
|
135
|
+
end
|
136
|
+
|
137
|
+
def building?
|
138
|
+
![nil, false].include?(@wait_thread.status)
|
139
|
+
end
|
140
|
+
|
141
|
+
def success?
|
142
|
+
@wait_thread.value.success?
|
143
|
+
end
|
67
144
|
end
|
68
145
|
end
|
69
146
|
end
|
data/lib/bard/config.rb
ADDED
@@ -0,0 +1,91 @@
|
|
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(*args)
|
81
|
+
if args.length == 1
|
82
|
+
self.path = args.first
|
83
|
+
elsif args.length == 0
|
84
|
+
super() || project_name
|
85
|
+
else
|
86
|
+
raise ArgumentError
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
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.44.1
|
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: 2021-04-27 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
|
@@ -201,10 +143,12 @@ files:
|
|
201
143
|
- install_files/specified_node.rb
|
202
144
|
- install_files/specified_ruby.rb
|
203
145
|
- install_files/specified_yarn.rb
|
146
|
+
- install_files/yarn
|
204
147
|
- lib/bard.rb
|
205
148
|
- lib/bard/base.rb
|
206
|
-
- lib/bard/capistrano.rb
|
207
149
|
- lib/bard/ci.rb
|
150
|
+
- lib/bard/config.rb
|
151
|
+
- lib/bard/data.rb
|
208
152
|
- lib/bard/git.rb
|
209
153
|
- lib/bard/version.rb
|
210
154
|
- spec/bard_spec.rb
|
@@ -228,8 +172,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
228
172
|
- !ruby/object:Gem::Version
|
229
173
|
version: '0'
|
230
174
|
requirements: []
|
231
|
-
|
232
|
-
rubygems_version: 2.6.14
|
175
|
+
rubygems_version: 3.0.3
|
233
176
|
signing_key:
|
234
177
|
specification_version: 4
|
235
178
|
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
|