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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: b56dfecf167ffd69e2e99c558c0392fc6265f374
4
- data.tar.gz: 3e05c1a6fb673e9bd03e4ac4960ad91ed558aa22
2
+ SHA256:
3
+ metadata.gz: 86e79ccc420d8bed3ff834d150b9f58da74e34981dc59a244dd92de38fb5e831
4
+ data.tar.gz: 816db71d99a04cb75aff222369e41e719de7da1b608fc518b74e9ec3ba5a5ec0
5
5
  SHA512:
6
- metadata.gz: 3649d61bdf34b2340a1720c7b76ded57c6a0d3a015919f69d45210ed28556fe0d4efb3bed58b0cb1f34ecb4fc8c81543979cf8359afb0a00782e9a2f040cd975
7
- data.tar.gz: e6b78253e0841ddc31e503cd515d1a56202483ba5fd29320343d78d6a1df24ff572f2fe0e80b2f6dd338caa820b29ec1d14a7d90e55b4f685a681942fc9cec58
6
+ metadata.gz: 70fedcdd193a7588a6342a7efee99ddf95598599d80b50d48206fb29674e1dcf002ac3a239c0e836630190a1b5e8552d2bfe8ecc53f600fc031e3d44330631ac
7
+ data.tar.gz: 1c2dc2a6b0f6c6b225b520f7d801f8ae0dfd16b4007302db9ab2b713036f1bf89a4a5e4cd9f0bd422e23c5c3830b45ea94095283a780540fb1e87c88c0559917
data/.gitignore CHANGED
@@ -7,6 +7,7 @@ rdoc
7
7
  tmp
8
8
  .bundle
9
9
  .rvmrc
10
+ .ruby-version
10
11
  Gemfile.lock
11
12
  pkg/*
12
13
 
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
 
@@ -2,7 +2,6 @@ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib')
2
2
  require 'ruby-debug'
3
3
  require 'grit'
4
4
  require 'spec/expectations'
5
- require 'systemu'
6
5
  gem 'sqlite3-ruby'
7
6
 
8
7
  ENV["PATH"] += ":#{File.dirname(File.expand_path(__FILE__))}/../../bin"
@@ -1,5 +1,6 @@
1
+ require "open3"
1
2
  def type(command)
2
- @status, @stdout, @stderr = systemu command, :env => @env
3
+ @stdout, @stderr, @status = Open3.capture3(@env, command)
3
4
  if ENV['DEBUG']
4
5
  puts '-' * 20
5
6
  puts "Executing command: #{command}"
@@ -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
- "true"
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
- nvm "install" or raise NVMError.new($?.exitstatus)
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 restart
35
- exec %(bash -lc ". #{NVM_PATH}; #{$0}")
19
+ def install_binstub
20
+ system("cd bin && ln -fs ../#{NODE_PATH}")
36
21
  end
37
22
 
38
- def nvm_active?
39
- ENV.key?("NVM_DIR")
23
+ def node_installed?
24
+ File.exist?(NODE_PATH) && `#{NODE_PATH} --version`.chomp == NODE_VERSION
40
25
  end
41
26
 
42
- def nvm command
43
- system(". #{NVM_PATH}; nvm #{command}")
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
- YARN_PATH = "node_modules/yarn/bin/yarn"
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 unless binstub_installed?
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(". ~/.nvm/nvm.sh && npm install yarn@#{version} --no-save")
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 -s ../#{YARN_PATH}")
20
+ system("cd bin && ln -fs ../#{YARN_PATH}")
20
21
  end
21
22
 
22
23
  def yarn_installed?
23
- File.exist?(YARN_PATH) && `#{YARN_PATH} --version`.chomp == version
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
 
@@ -0,0 +1,5 @@
1
+ #!/bin/sh
2
+ argv0=$(echo "$0" | sed -e 's,\\,/,g')
3
+ basedir=$(dirname "$(readlink -f "$0" || echo "$argv0")")
4
+ exec "$basedir/node" "$basedir/yarn.js" "$@"
5
+
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 = "production", to = "local")
12
- exec "cap _2.5.10_ data:pull ROLES=#{from}" if to == "local"
13
- exec "cap _2.5.10_ data:push ROLES=#{to}" if from == "local"
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 File.read("Capfile").include?("role :production")
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
- run_crucial "cap _2.5.10_ stage BRANCH=#{branch}", options.verbose?
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
- unless system("cap _2.5.10_ ping ROLES=staging >/dev/null 2>&1")
30
- puts red("Staging is now down!")
31
- end
34
+ ping :staging
32
35
  end
33
36
 
34
- method_options %w( verbose -v ) => :boolean, %w( skip-ci ) => :boolean
35
- desc "deploy [BRANCH=HEAD]", "checks that branch is a ff with master, checks with ci, and then merges into master and deploys to production, and deletes branch."
36
- def deploy branch=Git.current_branch
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
- if not Git.fast_forward_merge? "origin/master", branch
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
- run_crucial "cap _2.5.10_ deploy", options.verbose?
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
- case Git.current_branch
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
- unless system("cap _2.5.10_ ping ROLES=production >/dev/null 2>&1")
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( verbose -v ) => :boolean
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
- run_crucial "cap _2.5.10_ download_ci_test_coverage"
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( home ) => :boolean
148
+ method_options %w[home] => :boolean
136
149
  desc "ssh [TO=production]", "logs into the specified server via SSH"
137
- def ssh to="production"
138
- if to == "gubs"
139
- command = "exec $SHELL"
140
- command = "cd Sites/#{project_name} && #{command}" unless options["home"]
141
- command = %(ssh -t gubito@gubs.pagekite.me 'bash -l -c "exec ./vagrant \\"#{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 "cap _2.5.10_ ssh ROLES=#{to}#{" NOCD=1" if options["home"]}"
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 "systemu"
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
- status, stdout, stderr = systemu command
16
- fatal "Running command: #{yellow(command)}: #{stderr}" if status.to_i.nonzero?
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 < Struct.new(:project_name, :sha)
3
- def run
4
- last_time_elapsed = get_last_time_elapsed
5
- start
6
- sleep(2) until started?
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
- start_time = Time.new.to_i
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
- success?
16
+ def local?
17
+ @local
16
18
  end
17
19
 
18
- def exists?
19
- `curl -s -I #{ci_host}/?token=botandrose` =~ /\b200 OK\b/
20
- end
20
+ extend Forwardable
21
21
 
22
- def console
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
- attr_accessor :last_response
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
- private
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
- def get_last_time_elapsed
32
- response = `curl -s #{ci_host}/lastStableBuild/api/xml?token=botandrose`
33
- response.match(/<duration>(\d+)<\/duration>/)
34
- $1 ? $1.to_i / 1000 : nil
35
- end
37
+ success?
38
+ end
36
39
 
37
- def ci_host
38
- "http://botandrose:thecakeisalie!@ci.botandrose.com/job/#{project_name}"
39
- end
40
+ def exists?
41
+ `curl -s -I #{ci_host}/` =~ /\b200 OK\b/
42
+ end
40
43
 
41
- def start
42
- command = "curl -s -I -X POST '#{ci_host}/buildWithParameters?token=botandrose&GIT_REF=#{sha}'"
43
- output = `#{command}`
44
- @queueId = output[%r{Location: .+/queue/item/(\d+)/}, 1].to_i
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
- def started?
48
- command = "curl -s -g '#{ci_host}/api/json?depth=1&tree=builds[queueId,number]'"
49
- output = `#{command}`
50
- output =~ /"queueId":#{@queueId}\b/
51
- end
49
+ attr_accessor :last_response
50
+
51
+ private
52
52
 
53
- def job_id
54
- @job_id ||= begin
55
- output = `curl -s -g '#{ci_host}/api/json?depth=1&tree=builds[queueId,number]'`
56
- output[/"number":(\d+),"queueId":#{@queueId}\b/, 1].to_i
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
- def building?
61
- self.last_response = `curl -s #{ci_host}/#{job_id}/api/json?tree=building,result`
62
- last_response.include? '"building":true'
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
- def success?
66
- last_response.include? '"result":"SUCCESS"'
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
@@ -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
@@ -1,4 +1,4 @@
1
1
  module Bard
2
- VERSION = "0.41.2"
2
+ VERSION = "0.44.1"
3
3
  end
4
4
 
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.41.2
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: 2019-08-26 00:00:00.000000000 Z
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
- rubyforge_project:
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
@@ -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