bard 0.67.0 → 0.69.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 +4 -4
- data/bard.gemspec +0 -2
- data/bin/bard +1 -2
- data/lib/bard/ci/github_actions.rb +1 -2
- data/lib/bard/ci/jenkins.rb +1 -1
- data/lib/bard/ci/local.rb +1 -1
- data/lib/bard/ci.rb +1 -1
- data/lib/bard/cli.rb +262 -0
- data/lib/bard/command.rb +64 -0
- data/lib/bard/config.rb +31 -0
- data/lib/bard/copy.rb +101 -0
- data/lib/bard/git.rb +23 -21
- data/lib/bard/version.rb +1 -1
- data/lib/bard.rb +1 -251
- data/spec/bard/ci/github_actions_spec.rb +3 -3
- metadata +6 -20
- data/lib/bard/base.rb +0 -119
- data/lib/bard/data.rb +0 -96
- data/lib/bard/remote_command.rb +0 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2605af57059f326785460af9d28344b196ad3f87eae1a672998fed6b0f6956ae
|
4
|
+
data.tar.gz: 666e6f8637a2342e981b19a8e9a2e2a3883be06e1401fb71b7358b5d44db4c68
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 28d95083d05f69bdad245c7d8028d5f509b8d233a6a7d04708db1bfdad3fcf67d919deb011976ce029f95b7a72c24f3ed76c5dfbc4137c6fa204a43fd8329196
|
7
|
+
data.tar.gz: 42255ed7d0f2d959103f5340ad6c5e5289fd1a6713a831be791e6e5849f2caf197a5223db5812b2eddc107be1ba30945137d9cfdb4d7cde96f290910811c0ca2
|
data/bard.gemspec
CHANGED
@@ -9,7 +9,6 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.authors = ["Micah Geisel"]
|
10
10
|
spec.email = ["micah@botandrose.com"]
|
11
11
|
spec.summary = "CLI to automate common development tasks."
|
12
|
-
spec.description = "CLI to automate common development tasks."
|
13
12
|
spec.homepage = "http://github.com/botandrose/bard"
|
14
13
|
spec.license = "MIT"
|
15
14
|
|
@@ -21,7 +20,6 @@ Gem::Specification.new do |spec|
|
|
21
20
|
spec.add_dependency "thor", ">= 0.19.0"
|
22
21
|
spec.add_dependency "rvm"
|
23
22
|
spec.add_dependency "term-ansicolor", ">= 1.0.3"
|
24
|
-
spec.add_dependency "bard-rake", ">= 0.19.0"
|
25
23
|
|
26
24
|
spec.add_development_dependency "byebug"
|
27
25
|
spec.add_development_dependency "rspec"
|
data/bin/bard
CHANGED
data/lib/bard/ci/jenkins.rb
CHANGED
data/lib/bard/ci/local.rb
CHANGED
data/lib/bard/ci.rb
CHANGED
data/lib/bard/cli.rb
ADDED
@@ -0,0 +1,262 @@
|
|
1
|
+
# this file gets loaded in the CLI context, not the Rails boot context
|
2
|
+
|
3
|
+
require "thor"
|
4
|
+
require "bard/git"
|
5
|
+
require "bard/ci"
|
6
|
+
require "bard/copy"
|
7
|
+
require "bard/github"
|
8
|
+
require "bard/ping"
|
9
|
+
require "bard/config"
|
10
|
+
require "bard/command"
|
11
|
+
require "term/ansicolor"
|
12
|
+
require "open3"
|
13
|
+
require "uri"
|
14
|
+
|
15
|
+
module Bard
|
16
|
+
class CLI < Thor
|
17
|
+
include Term::ANSIColor
|
18
|
+
|
19
|
+
class_option :verbose, type: :boolean, aliases: :v
|
20
|
+
|
21
|
+
desc "data --from=production --to=local", "copy database and assets from from to to"
|
22
|
+
option :from, default: "production"
|
23
|
+
option :to, default: "local"
|
24
|
+
def data
|
25
|
+
from = config[options[:from]]
|
26
|
+
to = config[options[:to]]
|
27
|
+
|
28
|
+
if to.key == :production
|
29
|
+
url = to.ping.first
|
30
|
+
puts yellow "WARNING: You are about to push data to production, overwriting everything that is there!"
|
31
|
+
answer = ask("If you really want to do this, please type in the full HTTPS url of the production server:")
|
32
|
+
if answer != url
|
33
|
+
puts red("!!! ") + "Failed! We expected #{url}. Is this really where you want to overwrite all the data?"
|
34
|
+
exit 1
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
puts "Dumping #{from.key} database to file..."
|
39
|
+
from.run! "bin/rake db:dump"
|
40
|
+
|
41
|
+
puts "Transfering file from #{from.key} to #{to.key}..."
|
42
|
+
from.copy_file "db/data.sql.gz", to: to, verbose: true
|
43
|
+
|
44
|
+
puts "Loading file into #{to.key} database..."
|
45
|
+
to.run! "bin/rake db:load"
|
46
|
+
|
47
|
+
config.data.each do |path|
|
48
|
+
puts "Synchronizing files in #{path}..."
|
49
|
+
from.copy_dir path, to: to, verbose: true
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
desc "stage [branch=HEAD]", "pushes current branch, and stages it"
|
54
|
+
def stage branch=Git.current_branch
|
55
|
+
unless config.servers.key?(:production)
|
56
|
+
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.")
|
57
|
+
end
|
58
|
+
|
59
|
+
run! "git push -u origin #{branch}", verbose: true
|
60
|
+
config[:staging].run! "git fetch && git checkout -f origin/#{branch} && bin/setup"
|
61
|
+
puts green("Stage Succeeded")
|
62
|
+
|
63
|
+
ping :staging
|
64
|
+
end
|
65
|
+
|
66
|
+
option :"skip-ci", type: :boolean
|
67
|
+
option :"local-ci", type: :boolean
|
68
|
+
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."
|
69
|
+
def deploy to=:production
|
70
|
+
branch = Git.current_branch
|
71
|
+
|
72
|
+
if branch == "master"
|
73
|
+
if !Git.up_to_date_with_remote?(branch)
|
74
|
+
run! "git push origin #{branch}:#{branch}"
|
75
|
+
end
|
76
|
+
invoke :ci, [branch], options.slice("local-ci") unless options["skip-ci"]
|
77
|
+
|
78
|
+
else
|
79
|
+
run! "git fetch origin master:master"
|
80
|
+
|
81
|
+
unless Git.fast_forward_merge?("origin/master", branch)
|
82
|
+
puts "The master branch has advanced. Attempting rebase..."
|
83
|
+
run! "git rebase origin/master"
|
84
|
+
end
|
85
|
+
|
86
|
+
run! "git push -f origin #{branch}:#{branch}"
|
87
|
+
|
88
|
+
invoke :ci, [branch], options.slice("local-ci") unless options["skip-ci"]
|
89
|
+
|
90
|
+
run! "git push origin #{branch}:master"
|
91
|
+
run! "git fetch origin master:master"
|
92
|
+
end
|
93
|
+
|
94
|
+
if `git remote` =~ /\bgithub\b/
|
95
|
+
run! "git push github"
|
96
|
+
end
|
97
|
+
|
98
|
+
config[to].run! "git pull origin master && bin/setup"
|
99
|
+
|
100
|
+
puts green("Deploy Succeeded")
|
101
|
+
|
102
|
+
if branch != "master"
|
103
|
+
puts "Deleting branch: #{branch}"
|
104
|
+
run! "git push --delete origin #{branch}"
|
105
|
+
|
106
|
+
if branch == Git.current_branch
|
107
|
+
run! "git checkout master"
|
108
|
+
end
|
109
|
+
|
110
|
+
run! "git branch -D #{branch}"
|
111
|
+
end
|
112
|
+
|
113
|
+
ping to
|
114
|
+
end
|
115
|
+
|
116
|
+
option :"local-ci", type: :boolean
|
117
|
+
option :status, type: :boolean
|
118
|
+
desc "ci [branch=HEAD]", "runs ci against BRANCH"
|
119
|
+
def ci branch=Git.current_branch
|
120
|
+
ci = CI.new(project_name, branch, local: options["local-ci"])
|
121
|
+
if ci.exists?
|
122
|
+
return puts ci.status if options["status"]
|
123
|
+
|
124
|
+
puts "Continuous integration: starting build on #{branch}..."
|
125
|
+
|
126
|
+
success = ci.run do |elapsed_time, last_time|
|
127
|
+
if last_time
|
128
|
+
percentage = (elapsed_time.to_f / last_time.to_f * 100).to_i
|
129
|
+
output = " Estimated completion: #{percentage}%"
|
130
|
+
else
|
131
|
+
output = " No estimated completion time. Elapsed time: #{elapsed_time} sec"
|
132
|
+
end
|
133
|
+
print "\x08" * output.length
|
134
|
+
print output
|
135
|
+
$stdout.flush
|
136
|
+
end
|
137
|
+
|
138
|
+
if success
|
139
|
+
puts
|
140
|
+
puts "Continuous integration: success!"
|
141
|
+
puts "Deploying..."
|
142
|
+
else
|
143
|
+
puts
|
144
|
+
puts ci.last_response
|
145
|
+
puts ci.console
|
146
|
+
puts red("Automated tests failed!")
|
147
|
+
exit 1
|
148
|
+
end
|
149
|
+
|
150
|
+
else
|
151
|
+
puts red("No CI found for #{project_name}!")
|
152
|
+
puts "Re-run with --skip-ci to bypass CI, if you absolutely must, and know what you're doing."
|
153
|
+
exit 1
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
desc "open [server=production]", "opens the url in the web browser."
|
158
|
+
def open server=:production
|
159
|
+
exec "xdg-open #{config[server].ping.first}"
|
160
|
+
end
|
161
|
+
|
162
|
+
desc "hurt <command>", "reruns a command until it fails"
|
163
|
+
def hurt *args
|
164
|
+
1.upto(Float::INFINITY) do |count|
|
165
|
+
puts "Running attempt #{count}"
|
166
|
+
system *args
|
167
|
+
unless $?.success?
|
168
|
+
puts "Ran #{count-1} times before failing"
|
169
|
+
break
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
option :home, type: :boolean
|
175
|
+
desc "ssh [to=production]", "logs into the specified server via SSH"
|
176
|
+
def ssh to=:production
|
177
|
+
config[to].exec! "exec $SHELL -l", home: options[:home]
|
178
|
+
end
|
179
|
+
|
180
|
+
desc "install", "copies bin/setup and bin/ci scripts into current project."
|
181
|
+
def install
|
182
|
+
install_files_path = File.expand_path(File.join(__dir__, "../install_files/*"))
|
183
|
+
system "cp -R #{install_files_path} bin/"
|
184
|
+
github_files_path = File.expand_path(File.join(__dir__, "../install_files/.github"))
|
185
|
+
system "cp -R #{github_files_path} ./"
|
186
|
+
end
|
187
|
+
|
188
|
+
desc "setup", "installs app in nginx"
|
189
|
+
def setup
|
190
|
+
path = "/etc/nginx/sites-available/#{project_name}"
|
191
|
+
dest_path = path.sub("sites-available", "sites-enabled")
|
192
|
+
server_name = "#{project_name}.localhost"
|
193
|
+
|
194
|
+
system "sudo tee #{path} >/dev/null <<-EOF
|
195
|
+
server {
|
196
|
+
listen 80;
|
197
|
+
server_name #{server_name};
|
198
|
+
|
199
|
+
root #{Dir.pwd}/public;
|
200
|
+
passenger_enabled on;
|
201
|
+
|
202
|
+
location ~* \\.(ico|css|js|gif|jp?g|png|webp) {
|
203
|
+
access_log off;
|
204
|
+
if (\\$request_filename ~ \"-[0-9a-f]{32}\\.\") {
|
205
|
+
expires max;
|
206
|
+
add_header Cache-Control public;
|
207
|
+
}
|
208
|
+
}
|
209
|
+
gzip_static on;
|
210
|
+
}
|
211
|
+
EOF"
|
212
|
+
system "sudo ln -sf #{path} #{dest_path}" if !File.exist?(dest_path)
|
213
|
+
system "sudo service nginx restart"
|
214
|
+
end
|
215
|
+
|
216
|
+
desc "ping [server=production]", "hits the server over http to verify that its up."
|
217
|
+
def ping server=:production
|
218
|
+
server = config[server]
|
219
|
+
down_urls = Bard::Ping.call(config[server])
|
220
|
+
down_urls.each { |url| puts "#{url} is down!" }
|
221
|
+
exit 1 if down_urls.any?
|
222
|
+
end
|
223
|
+
|
224
|
+
option :on, default: "production"
|
225
|
+
desc "command <command> --on=production", "run the given command on the remote server"
|
226
|
+
def command command
|
227
|
+
server = config[options[:on]]
|
228
|
+
server.run! remote_command, verbose: true
|
229
|
+
end
|
230
|
+
|
231
|
+
desc "master_key --from=production --to=local", "copy master key from from to to"
|
232
|
+
option :from, default: "production"
|
233
|
+
option :to, default: "local"
|
234
|
+
def master_key
|
235
|
+
from = config[options[:from]]
|
236
|
+
to = config[options[:to]]
|
237
|
+
from.copy_file "config/master.key", to:
|
238
|
+
end
|
239
|
+
|
240
|
+
desc "vim [branch=master]", "open all files that have changed since master"
|
241
|
+
def vim branch="master"
|
242
|
+
exec "vim -p `git diff #{branch} --name-only | grep -v sass$ | tac`"
|
243
|
+
end
|
244
|
+
|
245
|
+
def self.exit_on_failure? = true
|
246
|
+
|
247
|
+
private
|
248
|
+
|
249
|
+
def config
|
250
|
+
@config ||= Bard::Config.new(project_name, path: "bard.rb")
|
251
|
+
end
|
252
|
+
|
253
|
+
def project_name
|
254
|
+
@project_name ||= File.expand_path(".").split("/").last
|
255
|
+
end
|
256
|
+
|
257
|
+
def run!(...)
|
258
|
+
Bard::Command.run!(...)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
data/lib/bard/command.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
module Bard
|
2
|
+
class Command < Struct.new(:command, :on, :home)
|
3
|
+
def self.run! command, on: :local, home: false, verbose: false
|
4
|
+
new(command, on, home).run! verbose: verbose
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.exec! command, on: :local, home: false
|
8
|
+
new(command, on, home).exec!
|
9
|
+
end
|
10
|
+
|
11
|
+
def run! verbose: false
|
12
|
+
failed = false
|
13
|
+
|
14
|
+
if verbose
|
15
|
+
failed = !(system full_command)
|
16
|
+
else
|
17
|
+
stdout, stderr, status = Open3.capture3(full_command)
|
18
|
+
failed = status.to_i.nonzero?
|
19
|
+
if failed
|
20
|
+
$stdout.puts stdout
|
21
|
+
$stderr.puts stderr
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
if failed
|
26
|
+
raise "Running command failed: #{full_command}"
|
27
|
+
# puts red("!!! ") + "Running command failed: #{yellow(command)}"
|
28
|
+
# exit 1
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def exec!
|
33
|
+
exec full_command
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def full_command
|
39
|
+
if on.to_sym == :local
|
40
|
+
command
|
41
|
+
else
|
42
|
+
remote_command
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def remote_command
|
47
|
+
uri = URI.parse("ssh://#{on.ssh}")
|
48
|
+
ssh_key = on.ssh_key ? "-i #{on.ssh_key} " : ""
|
49
|
+
cmd = command
|
50
|
+
if on.env
|
51
|
+
cmd = "#{on.env} #{command}"
|
52
|
+
end
|
53
|
+
unless home
|
54
|
+
cmd = "cd #{on.path} && #{cmd}"
|
55
|
+
end
|
56
|
+
cmd = "ssh -tt #{ssh_key}#{"-p#{uri.port} " if uri.port}#{uri.user}@#{uri.host} '#{cmd}'"
|
57
|
+
if on.gateway
|
58
|
+
uri = URI.parse("ssh://#{on.gateway}")
|
59
|
+
cmd = "ssh -tt #{" -p#{uri.port} " if uri.port}#{uri.user}@#{uri.host} \"#{cmd}\""
|
60
|
+
end
|
61
|
+
cmd
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/bard/config.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
require "uri"
|
2
|
+
require "bard/command"
|
3
|
+
require "bard/copy"
|
2
4
|
|
3
5
|
module Bard
|
4
6
|
class Config
|
@@ -48,11 +50,20 @@ module Bard
|
|
48
50
|
attr_reader :project_name, :servers
|
49
51
|
|
50
52
|
def server key, &block
|
53
|
+
key = key.to_sym
|
51
54
|
@servers[key] ||= Server.new(project_name, key)
|
52
55
|
@servers[key].instance_eval &block if block_given?
|
53
56
|
@servers[key]
|
54
57
|
end
|
55
58
|
|
59
|
+
def [] key
|
60
|
+
key = key.to_sym
|
61
|
+
if @servers[key].nil? && key == :production
|
62
|
+
key = :staging
|
63
|
+
end
|
64
|
+
@servers[key]
|
65
|
+
end
|
66
|
+
|
56
67
|
def data *paths
|
57
68
|
if paths.length == 0
|
58
69
|
Array(@data)
|
@@ -123,6 +134,26 @@ module Bard
|
|
123
134
|
raise ArgumentError
|
124
135
|
end
|
125
136
|
end
|
137
|
+
|
138
|
+
def to_sym
|
139
|
+
key
|
140
|
+
end
|
141
|
+
|
142
|
+
def run! command, home: false, verbose: false
|
143
|
+
Bard::Command.run! command, on: self, home:, verbose:
|
144
|
+
end
|
145
|
+
|
146
|
+
def exec! command, home: false
|
147
|
+
Bard::Command.exec! command, on: self, home:
|
148
|
+
end
|
149
|
+
|
150
|
+
def copy_file path, to:, verbose: false
|
151
|
+
Bard::Copy.file path, from: self, to:, verbose:
|
152
|
+
end
|
153
|
+
|
154
|
+
def copy_dir
|
155
|
+
Bard::Copy.dir path, from: self, to:, verbose:
|
156
|
+
end
|
126
157
|
end
|
127
158
|
end
|
128
159
|
end
|
data/lib/bard/copy.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
module Bard
|
2
|
+
class Copy < Struct.new(:path, :from, :to, :verbose)
|
3
|
+
def self.file path, from:, to:, verbose: false
|
4
|
+
new(path, from, to, verbose).scp
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.dir path, from:, to:, verbose: false
|
8
|
+
new(path, from, to, verbose).rsync
|
9
|
+
end
|
10
|
+
|
11
|
+
def scp
|
12
|
+
if from.key == :local
|
13
|
+
scp_using_local :to, to
|
14
|
+
elsif to.key == :local
|
15
|
+
scp_using_local :from, from
|
16
|
+
else
|
17
|
+
scp_as_mediator
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def scp_using_local direction, server
|
22
|
+
uri = URI.parse("ssh://#{server.gateway}")
|
23
|
+
port = uri.port ? "-p#{uri.port}" : ""
|
24
|
+
gateway = server.gateway ? "-oProxyCommand='ssh #{port} #{uri.user}@#{uri.host} -W %h:%p'" : ""
|
25
|
+
|
26
|
+
ssh_key = server.ssh_key ? "-i #{server.ssh_key}" : ""
|
27
|
+
|
28
|
+
uri = URI.parse("ssh://#{server.ssh}")
|
29
|
+
port = uri.port ? "-P#{uri.port}" : ""
|
30
|
+
from_and_to = [path, "#{uri.user}@#{uri.host}:#{server.path}/#{path}"]
|
31
|
+
|
32
|
+
from_and_to.reverse! if direction == :from
|
33
|
+
command = "scp #{gateway} #{ssh_key} #{port} #{from_and_to.join(" ")}"
|
34
|
+
|
35
|
+
Bard::Command.run! command, verbose: verbose
|
36
|
+
end
|
37
|
+
|
38
|
+
def scp_as_mediator
|
39
|
+
raise NotImplementedError if from.gateway || to.gateway || from.ssh_key || to.ssh_key
|
40
|
+
|
41
|
+
from_uri = URI.parse("ssh://#{from.ssh}")
|
42
|
+
from_str = "scp://#{from_uri.user}@#{from_uri.host}:#{from_uri.port || 22}/#{from.path}/#{path}"
|
43
|
+
|
44
|
+
to_uri = URI.parse("ssh://#{to.ssh}")
|
45
|
+
to_str = "scp://#{to_uri.user}@#{to_uri.host}:#{to_uri.port || 22}/#{to.path}/#{path}"
|
46
|
+
|
47
|
+
command = "scp -o ForwardAgent=yes #{from_str} #{to_str}"
|
48
|
+
|
49
|
+
Bard::Command.run! command, verbose: verbose
|
50
|
+
end
|
51
|
+
|
52
|
+
def rsync
|
53
|
+
if from.key == :local
|
54
|
+
rsync_using_local :to, to
|
55
|
+
elsif to.key == :local
|
56
|
+
rsync_using_local :from, from
|
57
|
+
else
|
58
|
+
rsync_as_mediator from, to
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def rsync_using_local direction, server
|
63
|
+
uri = URI.parse("ssh://#{server.gateway}")
|
64
|
+
port = uri.port ? "-p#{uri.port}" : ""
|
65
|
+
gateway = server.gateway ? "-oProxyCommand=\"ssh #{port} #{uri.user}@#{uri.host} -W %h:%p\"" : ""
|
66
|
+
|
67
|
+
ssh_key = server.ssh_key ? "-i #{server.ssh_key}" : ""
|
68
|
+
uri = URI.parse("ssh://#{server.ssh}")
|
69
|
+
port = uri.port ? "-p#{uri.port}" : ""
|
70
|
+
ssh = "-e'ssh #{ssh_key} #{port} #{gateway}'"
|
71
|
+
|
72
|
+
dest_path = path.dup
|
73
|
+
dest_path = "./#{dest_path}"
|
74
|
+
from_and_to = [dest_path, "#{uri.user}@#{uri.host}:#{server.path}/#{path}"]
|
75
|
+
from_and_to.reverse! if direction == :from
|
76
|
+
from_and_to[-1].sub! %r(/[^/]+$), '/'
|
77
|
+
|
78
|
+
command = "rsync #{ssh} --delete --info=progress2 -az #{from_and_to.join(" ")}"
|
79
|
+
|
80
|
+
Bard::Command.run! command, verbose: verbose
|
81
|
+
end
|
82
|
+
|
83
|
+
def rsync_as_mediator from, to
|
84
|
+
raise NotImplementedError if from.gateway || to.gateway || from.ssh_key || to.ssh_key
|
85
|
+
|
86
|
+
dest_path = path.dup
|
87
|
+
dest_path = "./#{dest_path}"
|
88
|
+
|
89
|
+
from_uri = URI.parse("ssh://#{from.ssh}")
|
90
|
+
from_str = "-p#{from_uri.port || 22} #{from_uri.user}@#{from_uri.host}"
|
91
|
+
|
92
|
+
to_uri = URI.parse("ssh://#{to.ssh}")
|
93
|
+
to_str = "#{to_uri.user}@#{to_uri.host}:#{to.path}/#{path}"
|
94
|
+
to_str.sub! %r(/[^/]+$), '/'
|
95
|
+
|
96
|
+
command = %(ssh -A #{from_str} 'rsync -e \"ssh -A -p#{to_uri.port || 22} -o StrictHostKeyChecking=no\" --delete --info=progress2 -az #{from.path}/#{path} #{to_str}')
|
97
|
+
|
98
|
+
Bard::Command.run! command, verbose: verbose
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
data/lib/bard/git.rb
CHANGED
@@ -1,29 +1,31 @@
|
|
1
|
-
module Bard
|
2
|
-
|
1
|
+
module Bard
|
2
|
+
module Git
|
3
|
+
module_function
|
3
4
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
def current_branch
|
6
|
+
ref = `git symbolic-ref HEAD 2>&1`.chomp
|
7
|
+
return false if ref =~ /^fatal:/
|
8
|
+
ref.sub(/refs\/heads\//, '') # refs/heads/master ... we want "master"
|
9
|
+
end
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
11
|
+
def current_sha
|
12
|
+
sha_of("HEAD")
|
13
|
+
end
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
15
|
+
def fast_forward_merge?(root, branch)
|
16
|
+
root_head = sha_of(root)
|
17
|
+
branch_head = sha_of(branch)
|
18
|
+
common_ancestor = `git merge-base #{root_head} #{branch_head}`.chomp
|
19
|
+
common_ancestor == root_head
|
20
|
+
end
|
20
21
|
|
21
|
-
|
22
|
-
|
23
|
-
|
22
|
+
def up_to_date_with_remote? branch
|
23
|
+
sha_of(branch) == sha_of("origin/#{branch}")
|
24
|
+
end
|
24
25
|
|
25
|
-
|
26
|
-
|
26
|
+
def sha_of ref
|
27
|
+
`git rev-parse #{ref}`.chomp
|
28
|
+
end
|
27
29
|
end
|
28
30
|
end
|
29
31
|
|
data/lib/bard/version.rb
CHANGED
data/lib/bard.rb
CHANGED
@@ -1,253 +1,3 @@
|
|
1
|
-
module Bard
|
2
|
-
|
3
|
-
require "bard/base"
|
4
|
-
require "bard/git"
|
5
|
-
require "bard/ci"
|
6
|
-
require "bard/data"
|
7
|
-
require "bard/github"
|
8
|
-
require "bard/ping"
|
9
|
-
require "bard/config"
|
10
|
-
require "bard/remote_command"
|
11
|
-
|
12
|
-
class Bard::CLI < Thor
|
13
|
-
include Thor::Actions
|
14
|
-
|
15
|
-
def initialize(*args, **kwargs, &block)
|
16
|
-
super
|
17
|
-
@config = Bard::Config.new(project_name, path: "bard.rb")
|
18
|
-
end
|
19
|
-
|
20
|
-
desc "data --from=production --to=local", "copy database and assets from from to to"
|
21
|
-
method_options %w[from] => :string, %w[to] => :string
|
22
|
-
def data
|
23
|
-
default_from = @config.servers.key?(:production) ? "production" : "staging"
|
24
|
-
from = options.fetch(:from, default_from)
|
25
|
-
to = options.fetch(:to, "local")
|
26
|
-
Data.new(self, from, to).call
|
27
|
-
end
|
28
|
-
|
29
|
-
method_options %w( verbose -v ) => :boolean
|
30
|
-
desc "stage [BRANCH=HEAD]", "pushes current branch, and stages it"
|
31
|
-
def stage branch=Git.current_branch
|
32
|
-
unless @config.servers.key?(:production)
|
33
|
-
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.")
|
34
|
-
end
|
35
|
-
|
36
|
-
run_crucial "git push -u origin #{branch}", verbose: true
|
37
|
-
command = "git fetch && git checkout -f origin/#{branch} && bin/setup"
|
38
|
-
run_crucial ssh_command(:staging, command)
|
39
|
-
puts green("Stage Succeeded")
|
40
|
-
|
41
|
-
ping :staging
|
42
|
-
end
|
43
|
-
|
44
|
-
method_options %w[verbose -v] => :boolean, %w[skip-ci] => :boolean, %w[local-ci -l] => :boolean
|
45
|
-
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."
|
46
|
-
def deploy to=nil
|
47
|
-
branch = Git.current_branch
|
48
|
-
|
49
|
-
if branch == "master"
|
50
|
-
run_crucial "git push origin #{branch}:#{branch}" if !Git.up_to_date_with_remote?(branch)
|
51
|
-
invoke :ci, [branch], options.slice("local-ci") unless options["skip-ci"]
|
52
|
-
|
53
|
-
else
|
54
|
-
run_crucial "git fetch origin master:master"
|
55
|
-
|
56
|
-
unless Git.fast_forward_merge?("origin/master", branch)
|
57
|
-
puts "The master branch has advanced. Attempting rebase..."
|
58
|
-
run_crucial "git rebase origin/master"
|
59
|
-
end
|
60
|
-
|
61
|
-
run_crucial "git push -f origin #{branch}:#{branch}"
|
62
|
-
|
63
|
-
invoke :ci, [branch], options.slice("local-ci") unless options["skip-ci"]
|
64
|
-
|
65
|
-
run_crucial "git push origin #{branch}:master"
|
66
|
-
run_crucial "git fetch origin master:master"
|
67
|
-
end
|
68
|
-
|
69
|
-
if `git remote` =~ /\bgithub\b/
|
70
|
-
run_crucial "git push github"
|
71
|
-
end
|
72
|
-
|
73
|
-
to ||= @config.servers.key?(:production) ? :production : :staging
|
74
|
-
|
75
|
-
command = "git pull origin master && bin/setup"
|
76
|
-
run_crucial ssh_command(to, command)
|
77
|
-
|
78
|
-
puts green("Deploy Succeeded")
|
79
|
-
|
80
|
-
if branch != "master"
|
81
|
-
puts "Deleting branch: #{branch}"
|
82
|
-
run_crucial "git push --delete origin #{branch}"
|
83
|
-
|
84
|
-
if branch == Git.current_branch
|
85
|
-
run_crucial "git checkout master"
|
86
|
-
end
|
87
|
-
|
88
|
-
run_crucial "git branch -D #{branch}"
|
89
|
-
end
|
90
|
-
|
91
|
-
ping to
|
92
|
-
end
|
93
|
-
|
94
|
-
method_options %w[verbose -v] => :boolean, %w[local-ci -l] => :boolean, %w[status -s] => :boolean
|
95
|
-
desc "ci [BRANCH=HEAD]", "runs ci against BRANCH"
|
96
|
-
def ci branch=Git.current_branch
|
97
|
-
ci = CI.new(project_name, branch, local: options["local-ci"])
|
98
|
-
if ci.exists?
|
99
|
-
return puts ci.status if options["status"]
|
100
|
-
|
101
|
-
puts "Continuous integration: starting build on #{branch}..."
|
102
|
-
|
103
|
-
success = ci.run do |elapsed_time, last_time|
|
104
|
-
if last_time
|
105
|
-
percentage = (elapsed_time.to_f / last_time.to_f * 100).to_i
|
106
|
-
output = " Estimated completion: #{percentage}%"
|
107
|
-
else
|
108
|
-
output = " No estimated completion time. Elapsed time: #{elapsed_time} sec"
|
109
|
-
end
|
110
|
-
print "\x08" * output.length
|
111
|
-
print output
|
112
|
-
$stdout.flush
|
113
|
-
end
|
114
|
-
|
115
|
-
if success
|
116
|
-
puts
|
117
|
-
puts "Continuous integration: success!"
|
118
|
-
if ci.jenkins? && File.exist?("coverage")
|
119
|
-
puts "Downloading test coverage from CI..."
|
120
|
-
download_ci_test_coverage
|
121
|
-
end
|
122
|
-
puts "Deploying..."
|
123
|
-
else
|
124
|
-
puts
|
125
|
-
puts ci.last_response
|
126
|
-
puts ci.console
|
127
|
-
puts red("Automated tests failed!")
|
128
|
-
exit 1
|
129
|
-
end
|
130
|
-
|
131
|
-
else
|
132
|
-
puts red("No CI found for #{project_name}!")
|
133
|
-
puts "Re-run with --skip-ci to bypass CI, if you absolutely must, and know what you're doing."
|
134
|
-
exit 1
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
desc "open [SERVER=production]", "opens the url in the web browser."
|
139
|
-
def open server=nil
|
140
|
-
server ||= @config.servers.key?(:production) ? :production : :staging
|
141
|
-
server = @config.servers[server.to_sym]
|
142
|
-
exec "xdg-open #{server.ping.first}"
|
143
|
-
end
|
144
|
-
|
145
|
-
desc "hurt", "reruns a command until it fails"
|
146
|
-
def hurt *args
|
147
|
-
1.upto(Float::INFINITY) do |count|
|
148
|
-
puts "Running attempt #{count}"
|
149
|
-
system *args
|
150
|
-
unless $?.success?
|
151
|
-
puts "Ran #{count-1} times before failing"
|
152
|
-
break
|
153
|
-
end
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
method_options %w[home] => :boolean
|
158
|
-
desc "ssh [TO=production]", "logs into the specified server via SSH"
|
159
|
-
def ssh to=:production
|
160
|
-
command = "exec $SHELL -l"
|
161
|
-
if to == "theia" && !options["home"]
|
162
|
-
server = @config.servers[:theia]
|
163
|
-
command = %(bash -lic "exec ./vagrant \\"cd #{server.path} && #{command}\\"")
|
164
|
-
exec ssh_command(to, command, home: true)
|
165
|
-
else
|
166
|
-
exec ssh_command(to, command, home: options["home"])
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
desc "install", "copies bin/setup and bin/ci scripts into current project."
|
171
|
-
def install
|
172
|
-
install_files_path = File.expand_path(File.join(__dir__, "../install_files/*"))
|
173
|
-
system "cp -R #{install_files_path} bin/"
|
174
|
-
github_files_path = File.expand_path(File.join(__dir__, "../install_files/.github"))
|
175
|
-
system "cp -R #{github_files_path} ./"
|
176
|
-
end
|
177
|
-
|
178
|
-
desc "setup", "installs app in nginx"
|
179
|
-
def setup
|
180
|
-
path = "/etc/nginx/sites-available/#{project_name}"
|
181
|
-
dest_path = path.sub("sites-available", "sites-enabled")
|
182
|
-
server_name = "#{project_name}.localhost"
|
183
|
-
|
184
|
-
create_file path, <<~NGINX
|
185
|
-
server {
|
186
|
-
listen 80;
|
187
|
-
server_name #{server_name};
|
188
|
-
|
189
|
-
root #{Dir.pwd}/public;
|
190
|
-
passenger_enabled on;
|
191
|
-
|
192
|
-
location ~* \\.(ico|css|js|gif|jp?g|png|webp) {
|
193
|
-
access_log off;
|
194
|
-
if ($request_filename ~ "-[0-9a-f]{32}\\.") {
|
195
|
-
expires max;
|
196
|
-
add_header Cache-Control public;
|
197
|
-
}
|
198
|
-
}
|
199
|
-
gzip_static on;
|
200
|
-
}
|
201
|
-
NGINX
|
202
|
-
|
203
|
-
FileUtils.ln_sf(path, dest_path) if !File.exist?(dest_path)
|
204
|
-
run "service nginx restart"
|
205
|
-
rescue Errno::EACCES
|
206
|
-
raise InvocationError.new("please re-run with sudo")
|
207
|
-
end
|
208
|
-
|
209
|
-
desc "ping [SERVER=production]", "hits the server over http to verify that its up."
|
210
|
-
def ping server=:production
|
211
|
-
server = @config.servers[server.to_sym]
|
212
|
-
down_urls = Bard::Ping.call(server)
|
213
|
-
down_urls.each { |url| puts "#{url} is down!" }
|
214
|
-
exit 1 if down_urls.any?
|
215
|
-
end
|
216
|
-
|
217
|
-
desc "command <command> --on=production", "run the given command on the remote server"
|
218
|
-
method_options %w[on] => :string
|
219
|
-
def command command
|
220
|
-
default_from = @config.servers.key?(:production) ? "production" : "staging"
|
221
|
-
on = options.fetch(:on, default_from)
|
222
|
-
server = @config.servers[on.to_sym]
|
223
|
-
remote_command = Bard::RemoteCommand.new(server, command).local_command
|
224
|
-
run_crucial remote_command, verbose: true
|
225
|
-
end
|
226
|
-
|
227
|
-
desc "master_key --from=production --to=local", "copy master key from from to to"
|
228
|
-
method_options %w[from] => :string, %w[to] => :string
|
229
|
-
def master_key
|
230
|
-
default_from = @config.servers.key?(:production) ? "production" : "staging"
|
231
|
-
from = options.fetch(:from, default_from)
|
232
|
-
to = options.fetch(:to, "local")
|
233
|
-
if to == "local"
|
234
|
-
copy :from, from, "config/master.key"
|
235
|
-
end
|
236
|
-
if from == "local"
|
237
|
-
copy :to, to, "config/master.key"
|
238
|
-
end
|
239
|
-
end
|
240
|
-
|
241
|
-
desc "download_ci_test_coverage", "download latest test coverage information from CI"
|
242
|
-
def download_ci_test_coverage
|
243
|
-
rsync :from, :ci, "coverage"
|
244
|
-
end
|
245
|
-
|
246
|
-
desc "vim", "open all files that have changed since master"
|
247
|
-
def vim branch="master"
|
248
|
-
exec "vim -p `git diff #{branch} --name-only | grep -v sass$ | tac`"
|
249
|
-
end
|
250
|
-
|
251
|
-
def self.exit_on_failure? = true
|
1
|
+
module Bard
|
252
2
|
end
|
253
3
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require "bard/ci/github_actions"
|
2
2
|
|
3
|
-
describe Bard::
|
3
|
+
describe Bard::CI::GithubActions do
|
4
4
|
subject { described_class.new("metrc", "master", "0966308e204b256fdcc11457eb53306d84884c60") }
|
5
5
|
|
6
6
|
xit "works" do
|
@@ -8,7 +8,7 @@ describe Bard::CLI::CI::GithubActions do
|
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
-
describe Bard::
|
11
|
+
describe Bard::CI::GithubActions::API do
|
12
12
|
subject { described_class.new("metrc") }
|
13
13
|
|
14
14
|
describe "#last_successful_run" do
|
@@ -29,7 +29,7 @@ describe Bard::CLI::CI::GithubActions::API do
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
-
describe Bard::
|
32
|
+
describe Bard::Github do
|
33
33
|
subject { described_class.new("metrc") }
|
34
34
|
end
|
35
35
|
|
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.69.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: 2024-08-
|
11
|
+
date: 2024-08-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -52,20 +52,6 @@ dependencies:
|
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: 1.0.3
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: bard-rake
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - ">="
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: 0.19.0
|
62
|
-
type: :runtime
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - ">="
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: 0.19.0
|
69
55
|
- !ruby/object:Gem::Dependency
|
70
56
|
name: byebug
|
71
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -108,7 +94,7 @@ dependencies:
|
|
108
94
|
- - ">="
|
109
95
|
- !ruby/object:Gem::Version
|
110
96
|
version: '0'
|
111
|
-
description:
|
97
|
+
description:
|
112
98
|
email:
|
113
99
|
- micah@botandrose.com
|
114
100
|
executables:
|
@@ -147,17 +133,17 @@ files:
|
|
147
133
|
- install_files/specified_bundler.rb
|
148
134
|
- install_files/specified_ruby.rb
|
149
135
|
- lib/bard.rb
|
150
|
-
- lib/bard/base.rb
|
151
136
|
- lib/bard/ci.rb
|
152
137
|
- lib/bard/ci/github_actions.rb
|
153
138
|
- lib/bard/ci/jenkins.rb
|
154
139
|
- lib/bard/ci/local.rb
|
140
|
+
- lib/bard/cli.rb
|
141
|
+
- lib/bard/command.rb
|
155
142
|
- lib/bard/config.rb
|
156
|
-
- lib/bard/
|
143
|
+
- lib/bard/copy.rb
|
157
144
|
- lib/bard/git.rb
|
158
145
|
- lib/bard/github.rb
|
159
146
|
- lib/bard/ping.rb
|
160
|
-
- lib/bard/remote_command.rb
|
161
147
|
- lib/bard/version.rb
|
162
148
|
- spec/bard/ci/github_actions_spec.rb
|
163
149
|
- spec/bard_spec.rb
|
data/lib/bard/base.rb
DELETED
@@ -1,119 +0,0 @@
|
|
1
|
-
require "thor"
|
2
|
-
require "term/ansicolor"
|
3
|
-
require "open3"
|
4
|
-
require "uri"
|
5
|
-
require "bard/remote_command"
|
6
|
-
|
7
|
-
class Bard::CLI < Thor
|
8
|
-
include Term::ANSIColor
|
9
|
-
|
10
|
-
private
|
11
|
-
|
12
|
-
def run_crucial command, verbose: false
|
13
|
-
failed = false
|
14
|
-
|
15
|
-
if verbose
|
16
|
-
failed = !(system command)
|
17
|
-
else
|
18
|
-
stdout, stderr, status = Open3.capture3(command)
|
19
|
-
failed = status.to_i.nonzero?
|
20
|
-
if failed
|
21
|
-
$stdout.puts stdout
|
22
|
-
$stderr.puts stderr
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
if failed
|
27
|
-
puts red("!!! ") + "Running command failed: #{yellow(command)}"
|
28
|
-
exit 1
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def project_name
|
33
|
-
@project_name ||= File.expand_path(".").split("/").last
|
34
|
-
end
|
35
|
-
|
36
|
-
def ssh_command server_name, command, home: false
|
37
|
-
server = @config.servers.fetch(server_name.to_sym)
|
38
|
-
Bard::RemoteCommand.new(server, command, home).local_command
|
39
|
-
end
|
40
|
-
|
41
|
-
def copy direction, server_name, path, verbose: false
|
42
|
-
server = @config.servers.fetch(server_name.to_sym)
|
43
|
-
|
44
|
-
uri = URI.parse("ssh://#{server.gateway}")
|
45
|
-
port = uri.port ? "-p#{uri.port}" : ""
|
46
|
-
gateway = server.gateway ? "-oProxyCommand='ssh #{port} #{uri.user}@#{uri.host} -W %h:%p'" : ""
|
47
|
-
|
48
|
-
ssh_key = server.ssh_key ? "-i #{server.ssh_key}" : ""
|
49
|
-
|
50
|
-
uri = URI.parse("ssh://#{server.ssh}")
|
51
|
-
port = uri.port ? "-P#{uri.port}" : ""
|
52
|
-
from_and_to = [path, "#{uri.user}@#{uri.host}:#{server.path}/#{path}"]
|
53
|
-
|
54
|
-
from_and_to.reverse! if direction == :from
|
55
|
-
command = "scp #{gateway} #{ssh_key} #{port} #{from_and_to.join(" ")}"
|
56
|
-
|
57
|
-
run_crucial command, verbose: verbose
|
58
|
-
end
|
59
|
-
|
60
|
-
def move from_name, to_name, path, verbose: false
|
61
|
-
from = @config.servers.fecth(from_name.to_sym)
|
62
|
-
to = @config.servers.fetch(to_name.to_sym)
|
63
|
-
raise NotImplementedError if from.gateway || to.gateway || from.ssh_key || to.ssh_key
|
64
|
-
|
65
|
-
from_uri = URI.parse("ssh://#{from.ssh}")
|
66
|
-
from_str = "scp://#{from_uri.user}@#{from_uri.host}:#{from_uri.port || 22}/#{from.path}/#{path}"
|
67
|
-
|
68
|
-
to_uri = URI.parse("ssh://#{to.ssh}")
|
69
|
-
to_str = "scp://#{to_uri.user}@#{to_uri.host}:#{to_uri.port || 22}/#{to.path}/#{path}"
|
70
|
-
|
71
|
-
command = "scp -o ForwardAgent=yes #{from_str} #{to_str}"
|
72
|
-
|
73
|
-
run_crucial command, verbose: verbose
|
74
|
-
end
|
75
|
-
|
76
|
-
def rsync direction, server_name, path, verbose: false
|
77
|
-
server = @config.servers.fetch(server_name.to_sym)
|
78
|
-
|
79
|
-
uri = URI.parse("ssh://#{server.gateway}")
|
80
|
-
port = uri.port ? "-p#{uri.port}" : ""
|
81
|
-
gateway = server.gateway ? "-oProxyCommand=\"ssh #{port} #{uri.user}@#{uri.host} -W %h:%p\"" : ""
|
82
|
-
|
83
|
-
ssh_key = server.ssh_key ? "-i #{server.ssh_key}" : ""
|
84
|
-
uri = URI.parse("ssh://#{server.ssh}")
|
85
|
-
port = uri.port ? "-p#{uri.port}" : ""
|
86
|
-
ssh = "-e'ssh #{ssh_key} #{port} #{gateway}'"
|
87
|
-
|
88
|
-
dest_path = path.dup
|
89
|
-
dest_path = "./#{dest_path}"
|
90
|
-
from_and_to = [dest_path, "#{uri.user}@#{uri.host}:#{server.path}/#{path}"]
|
91
|
-
from_and_to.reverse! if direction == :from
|
92
|
-
from_and_to[-1].sub! %r(/[^/]+$), '/'
|
93
|
-
|
94
|
-
command = "rsync #{ssh} --delete --info=progress2 -az #{from_and_to.join(" ")}"
|
95
|
-
|
96
|
-
run_crucial command, verbose: verbose
|
97
|
-
end
|
98
|
-
|
99
|
-
def rsync_remote from_name, to_name, path, verbose: false
|
100
|
-
from = @config.servers.fetch(from_name.to_sym)
|
101
|
-
to = @config.servers.fetch(to_name.to_sym)
|
102
|
-
raise NotImplementedError if from.gateway || to.gateway || from.ssh_key || to.ssh_key
|
103
|
-
|
104
|
-
dest_path = path.dup
|
105
|
-
dest_path = "./#{dest_path}"
|
106
|
-
|
107
|
-
from_uri = URI.parse("ssh://#{from.ssh}")
|
108
|
-
from_str = "-p#{from_uri.port || 22} #{from_uri.user}@#{from_uri.host}"
|
109
|
-
|
110
|
-
to_uri = URI.parse("ssh://#{to.ssh}")
|
111
|
-
to_str = "#{to_uri.user}@#{to_uri.host}:#{to.path}/#{path}"
|
112
|
-
to_str.sub! %r(/[^/]+$), '/'
|
113
|
-
|
114
|
-
command = %(ssh -A #{from_str} 'rsync -e \"ssh -A -p#{to_uri.port || 22} -o StrictHostKeyChecking=no\" --delete --info=progress2 -az #{from.path}/#{path} #{to_str}')
|
115
|
-
|
116
|
-
run_crucial command, verbose: verbose
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
data/lib/bard/data.rb
DELETED
@@ -1,96 +0,0 @@
|
|
1
|
-
class Bard::CLI < Thor
|
2
|
-
class Data < Struct.new(:bard, :from, :to)
|
3
|
-
def call
|
4
|
-
if to == "production"
|
5
|
-
server = bard.instance_variable_get(:@config).servers[to.to_sym]
|
6
|
-
url = server.ping.first
|
7
|
-
puts bard.yellow("WARNING: You are about to push data to production, overwriting everything that is there!")
|
8
|
-
answer = bard.ask("If you really want to do this, please type in the full HTTPS url of the production server:")
|
9
|
-
if answer != url
|
10
|
-
puts bard.red("!!! ") + "Failed! We expected #{url}. Is this really where you want to overwrite all the data?"
|
11
|
-
exit 1
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
if to == "local"
|
16
|
-
data_pull_db from.to_sym
|
17
|
-
data_pull_assets from.to_sym
|
18
|
-
elsif from == "local"
|
19
|
-
data_push_db to.to_sym
|
20
|
-
data_push_assets to.to_sym
|
21
|
-
else
|
22
|
-
data_move_db from.to_sym, to.to_sym
|
23
|
-
data_move_assets from.to_sym, to.to_sym
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
def data_pull_db server
|
30
|
-
bard.instance_eval do
|
31
|
-
puts "Dumping remote database to file..."
|
32
|
-
run_crucial ssh_command(server, "bin/rake db:dump")
|
33
|
-
|
34
|
-
puts "Downloading file..."
|
35
|
-
copy :from, server, "db/data.sql.gz", verbose: true
|
36
|
-
|
37
|
-
puts "Loading file into local database..."
|
38
|
-
run_crucial "bin/rake db:load"
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
def data_push_db server
|
43
|
-
bard.instance_eval do
|
44
|
-
puts "Dumping local database to file..."
|
45
|
-
run_crucial "bin/rake db:dump"
|
46
|
-
|
47
|
-
puts "Uploading file..."
|
48
|
-
copy :to, server, "db/data.sql.gz", verbose: true
|
49
|
-
|
50
|
-
puts "Loading file into remote database..."
|
51
|
-
run_crucial ssh_command(server, "bin/rake db:load")
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def data_move_db from, to
|
56
|
-
bard.instance_eval do
|
57
|
-
puts "Dumping local database to file..."
|
58
|
-
run_crucial ssh_command(from, "bin/rake db:load")
|
59
|
-
|
60
|
-
puts "Uploading file..."
|
61
|
-
move from, to, "db/data.sql.gz", verbose: true
|
62
|
-
|
63
|
-
puts "Loading file into remote database..."
|
64
|
-
run_crucial ssh_command(to, "bin/rake db:load")
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
def data_pull_assets server
|
69
|
-
bard.instance_eval do
|
70
|
-
@config.data.each do |path|
|
71
|
-
puts "Downloading files..."
|
72
|
-
rsync :from, server, path, verbose: true
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
def data_push_assets server
|
78
|
-
bard.instance_eval do
|
79
|
-
@config.data.each do |path|
|
80
|
-
puts "Uploading files..."
|
81
|
-
rsync :to, server, path, verbose: true
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def data_move_assets from, to
|
87
|
-
bard.instance_eval do
|
88
|
-
@config.data.each do |path|
|
89
|
-
puts "Copying files..."
|
90
|
-
rsync_remote from, to, path, verbose: true
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
data/lib/bard/remote_command.rb
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
module Bard
|
2
|
-
class RemoteCommand < Struct.new(:server, :command, :home)
|
3
|
-
def local_command
|
4
|
-
uri = URI.parse("ssh://#{server.ssh}")
|
5
|
-
ssh_key = server.ssh_key ? "-i #{server.ssh_key} " : ""
|
6
|
-
cmd = command
|
7
|
-
if server.env
|
8
|
-
cmd = "#{server.env} #{command}"
|
9
|
-
end
|
10
|
-
unless home
|
11
|
-
cmd = "cd #{server.path} && #{cmd}"
|
12
|
-
end
|
13
|
-
cmd = "ssh -tt #{ssh_key}#{"-p#{uri.port} " if uri.port}#{uri.user}@#{uri.host} '#{cmd}'"
|
14
|
-
if server.gateway
|
15
|
-
uri = URI.parse("ssh://#{server.gateway}")
|
16
|
-
cmd = "ssh -tt #{" -p#{uri.port} " if uri.port}#{uri.user}@#{uri.host} \"#{cmd}\""
|
17
|
-
end
|
18
|
-
cmd
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|