bard 2.0.0.beta → 2.0.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 +4 -4
- data/.github/workflows/ci.yml +6 -1
- data/CLAUDE.md +76 -0
- data/MIGRATION_GUIDE.md +24 -9
- data/PLUGINS.md +99 -0
- data/README.md +14 -6
- data/Rakefile +3 -1
- data/bard.gemspec +2 -1
- data/cucumber.yml +1 -0
- data/features/ci.feature +63 -0
- data/features/data.feature +13 -0
- data/features/deploy.feature +14 -0
- data/features/deploy_git_workflow.feature +89 -0
- data/features/run.feature +14 -0
- data/features/step_definitions/bard_steps.rb +136 -0
- data/features/support/bard-coverage +16 -0
- data/features/support/env.rb +14 -39
- data/features/support/test_server.rb +216 -0
- data/lib/bard/cli.rb +14 -31
- data/lib/bard/command.rb +10 -69
- data/lib/bard/config.rb +40 -183
- data/lib/bard/copy.rb +28 -103
- data/lib/bard/plugins/data.rb +56 -0
- data/lib/bard/{ci → plugins/deploy/ci}/github_actions.rb +3 -4
- data/lib/bard/plugins/deploy/ci/jenkins.rb +176 -0
- data/lib/bard/{ci → plugins/deploy/ci}/local.rb +7 -7
- data/lib/bard/{ci → plugins/deploy/ci}/runner.rb +38 -4
- data/lib/bard/plugins/deploy/ci.rb +38 -0
- data/lib/bard/plugins/deploy/ssh_strategy.rb +27 -0
- data/lib/bard/{deploy_strategy.rb → plugins/deploy/strategy.rb} +1 -1
- data/lib/bard/plugins/deploy.rb +240 -0
- data/lib/bard/{git.rb → plugins/git.rb} +6 -3
- data/lib/bard/{github.rb → plugins/github.rb} +4 -6
- data/lib/bard/{deploy_strategy/github_pages.rb → plugins/github_pages/strategy.rb} +41 -13
- data/lib/bard/plugins/github_pages.rb +30 -0
- data/lib/bard/plugins/hurt.rb +13 -0
- data/{install_files → lib/bard/plugins/install}/.github/dependabot.yml +2 -1
- data/{install_files → lib/bard/plugins/install}/.github/workflows/cache-ci.yml +1 -1
- data/{install_files → lib/bard/plugins/install}/.github/workflows/ci.yml +2 -2
- data/lib/bard/plugins/install.rb +9 -0
- data/lib/bard/plugins/open.rb +20 -0
- data/lib/bard/{ping.rb → plugins/ping/check.rb} +4 -4
- data/lib/bard/plugins/ping/target_methods.rb +23 -0
- data/lib/bard/plugins/ping.rb +10 -0
- data/lib/bard/plugins/run.rb +19 -0
- data/lib/bard/plugins/setup.rb +54 -0
- data/lib/bard/plugins/ssh/connection.rb +75 -0
- data/lib/bard/plugins/ssh/copy.rb +95 -0
- data/lib/bard/{ssh_server.rb → plugins/ssh/server.rb} +17 -42
- data/lib/bard/plugins/ssh/target_methods.rb +20 -0
- data/lib/bard/plugins/ssh.rb +10 -0
- data/lib/bard/plugins/url/target_methods.rb +23 -0
- data/lib/bard/plugins/url.rb +1 -0
- data/lib/bard/plugins/vim.rb +6 -0
- data/lib/bard/retryable.rb +25 -0
- data/lib/bard/secrets.rb +10 -0
- data/lib/bard/target.rb +27 -185
- data/lib/bard/version.rb +1 -1
- data/lib/bard.rb +1 -3
- data/spec/acceptance/docker/Dockerfile +3 -2
- data/spec/bard/capability_spec.rb +8 -50
- data/spec/bard/ci/github_actions_spec.rb +117 -14
- data/spec/bard/ci/jenkins_spec.rb +139 -0
- data/spec/bard/ci/runner_spec.rb +61 -0
- data/spec/bard/ci_spec.rb +1 -1
- data/spec/bard/cli/ci_spec.rb +34 -27
- data/spec/bard/cli/data_spec.rb +7 -26
- data/spec/bard/cli/deploy_spec.rb +87 -46
- data/spec/bard/cli/hurt_spec.rb +3 -9
- data/spec/bard/cli/install_spec.rb +5 -11
- data/spec/bard/cli/master_key_spec.rb +5 -19
- data/spec/bard/cli/open_spec.rb +14 -30
- data/spec/bard/cli/ping_spec.rb +8 -23
- data/spec/bard/cli/run_spec.rb +27 -21
- data/spec/bard/cli/setup_spec.rb +10 -27
- data/spec/bard/cli/ssh_spec.rb +10 -25
- data/spec/bard/cli/stage_spec.rb +28 -23
- data/spec/bard/cli/vim_spec.rb +3 -9
- data/spec/bard/command_spec.rb +1 -8
- data/spec/bard/config_spec.rb +78 -98
- data/spec/bard/copy_spec.rb +54 -18
- data/spec/bard/deploy_strategy/ssh_spec.rb +65 -7
- data/spec/bard/deploy_strategy_spec.rb +1 -1
- data/spec/bard/dynamic_dsl_spec.rb +18 -98
- data/spec/bard/git_spec.rb +9 -5
- data/spec/bard/github_spec.rb +2 -2
- data/spec/bard/ping_spec.rb +5 -5
- data/spec/bard/ssh_copy_spec.rb +44 -0
- data/spec/bard/ssh_server_spec.rb +8 -101
- data/spec/bard/target_spec.rb +66 -109
- data/spec/spec_helper.rb +6 -1
- metadata +79 -143
- data/README.rdoc +0 -15
- data/features/bard_check.feature +0 -94
- data/features/bard_deploy.feature +0 -18
- data/features/bard_pull.feature +0 -112
- data/features/bard_push.feature +0 -112
- data/features/podman_testcontainers.feature +0 -16
- data/features/step_definitions/check_steps.rb +0 -47
- data/features/step_definitions/git_steps.rb +0 -73
- data/features/step_definitions/global_steps.rb +0 -56
- data/features/step_definitions/podman_steps.rb +0 -23
- data/features/step_definitions/rails_steps.rb +0 -44
- data/features/step_definitions/submodule_steps.rb +0 -110
- data/features/support/grit_ext.rb +0 -13
- data/features/support/io.rb +0 -32
- data/features/support/podman.rb +0 -153
- data/lib/bard/ci/jenkins.rb +0 -105
- data/lib/bard/ci/retryable.rb +0 -27
- data/lib/bard/ci.rb +0 -50
- data/lib/bard/cli/ci.rb +0 -66
- data/lib/bard/cli/command.rb +0 -26
- data/lib/bard/cli/data.rb +0 -45
- data/lib/bard/cli/deploy.rb +0 -85
- data/lib/bard/cli/hurt.rb +0 -20
- data/lib/bard/cli/install.rb +0 -16
- data/lib/bard/cli/master_key.rb +0 -17
- data/lib/bard/cli/new.rb +0 -101
- data/lib/bard/cli/new_rails_template.rb +0 -197
- data/lib/bard/cli/open.rb +0 -22
- data/lib/bard/cli/ping.rb +0 -18
- data/lib/bard/cli/provision.rb +0 -34
- data/lib/bard/cli/run.rb +0 -24
- data/lib/bard/cli/setup.rb +0 -56
- data/lib/bard/cli/ssh.rb +0 -14
- data/lib/bard/cli/stage.rb +0 -27
- data/lib/bard/cli/vim.rb +0 -13
- data/lib/bard/default_config.rb +0 -35
- data/lib/bard/deploy_strategy/ssh.rb +0 -19
- data/lib/bard/github_pages.rb +0 -134
- data/lib/bard/provision/app.rb +0 -10
- data/lib/bard/provision/apt.rb +0 -16
- data/lib/bard/provision/authorizedkeys.rb +0 -25
- data/lib/bard/provision/data.rb +0 -27
- data/lib/bard/provision/deploy.rb +0 -10
- data/lib/bard/provision/http.rb +0 -16
- data/lib/bard/provision/logrotation.rb +0 -30
- data/lib/bard/provision/masterkey.rb +0 -18
- data/lib/bard/provision/mysql.rb +0 -22
- data/lib/bard/provision/passenger.rb +0 -37
- data/lib/bard/provision/repo.rb +0 -72
- data/lib/bard/provision/rvm.rb +0 -22
- data/lib/bard/provision/ssh.rb +0 -72
- data/lib/bard/provision/swapfile.rb +0 -21
- data/lib/bard/provision/user.rb +0 -42
- data/lib/bard/provision.rb +0 -16
- data/lib/bard/server.rb +0 -117
- data/spec/bard/cli/command_spec.rb +0 -50
- data/spec/bard/cli/new_spec.rb +0 -73
- data/spec/bard/cli/provision_spec.rb +0 -42
- data/spec/bard/github_pages_spec.rb +0 -143
- data/spec/bard/provision/app_spec.rb +0 -33
- data/spec/bard/provision/apt_spec.rb +0 -39
- data/spec/bard/provision/authorizedkeys_spec.rb +0 -40
- data/spec/bard/provision/data_spec.rb +0 -54
- data/spec/bard/provision/deploy_spec.rb +0 -33
- data/spec/bard/provision/http_spec.rb +0 -57
- data/spec/bard/provision/logrotation_spec.rb +0 -34
- data/spec/bard/provision/masterkey_spec.rb +0 -63
- data/spec/bard/provision/mysql_spec.rb +0 -55
- data/spec/bard/provision/passenger_spec.rb +0 -81
- data/spec/bard/provision/repo_spec.rb +0 -208
- data/spec/bard/provision/rvm_spec.rb +0 -49
- data/spec/bard/provision/ssh_spec.rb +0 -229
- data/spec/bard/provision/swapfile_spec.rb +0 -32
- data/spec/bard/provision/user_spec.rb +0 -103
- data/spec/bard/provision_spec.rb +0 -28
- data/spec/bard/server_spec.rb +0 -127
- /data/lib/bard/{ci → plugins/deploy/ci}/state.rb +0 -0
- /data/{install_files → lib/bard/plugins/install}/apt_dependencies.rb +0 -0
- /data/{install_files → lib/bard/plugins/install}/ci +0 -0
- /data/{install_files → lib/bard/plugins/install}/setup +0 -0
- /data/{install_files → lib/bard/plugins/install}/specified_bundler.rb +0 -0
- /data/{install_files → lib/bard/plugins/install}/specified_ruby.rb +0 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
require "bard/plugins/ping/target_methods"
|
|
2
|
+
|
|
3
|
+
class Bard::CLI
|
|
4
|
+
desc "ping [target=production]", "hits the target over http to verify that its up."
|
|
5
|
+
def ping(target = :production)
|
|
6
|
+
down_urls = Bard::Ping.call(config[target])
|
|
7
|
+
down_urls.each { |url| puts "#{url} is down!" }
|
|
8
|
+
exit 1 if down_urls.any?
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require "bard/command"
|
|
2
|
+
|
|
3
|
+
class Bard::CLI
|
|
4
|
+
# HACK: we don't use Thor::Base#run, so its okay to stomp on it here
|
|
5
|
+
original_verbose, $VERBOSE = $VERBOSE, nil
|
|
6
|
+
Thor::THOR_RESERVED_WORDS -= ["run"]
|
|
7
|
+
$VERBOSE = original_verbose
|
|
8
|
+
|
|
9
|
+
option :target, type: :string, default: "production"
|
|
10
|
+
option :home, type: :boolean
|
|
11
|
+
desc "run <command>", "run the given command on the specified target"
|
|
12
|
+
def run(*args)
|
|
13
|
+
target = config[options[:target].to_sym]
|
|
14
|
+
target.run!(*args.join(" "), verbose: true, home: options[:home])
|
|
15
|
+
rescue Bard::Command::Error => e
|
|
16
|
+
puts red("!!! ") + "Running command failed: #{yellow(e.message)}"
|
|
17
|
+
exit 1
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
require "uri"
|
|
2
|
+
require "bard/plugins/url"
|
|
3
|
+
|
|
4
|
+
class Bard::CLI
|
|
5
|
+
desc "setup", "installs app in nginx"
|
|
6
|
+
def setup
|
|
7
|
+
path = "/etc/nginx/sites-available/#{project_name}"
|
|
8
|
+
system "sudo tee #{path} >/dev/null <<-'EOF'
|
|
9
|
+
upstream puma {
|
|
10
|
+
server 127.0.0.1:3000 fail_timeout=5;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
server {
|
|
14
|
+
listen 80;
|
|
15
|
+
server_name #{nginx_server_name};
|
|
16
|
+
root #{Dir.pwd}/public;
|
|
17
|
+
|
|
18
|
+
try_files $uri @app;
|
|
19
|
+
|
|
20
|
+
location @app {
|
|
21
|
+
proxy_pass http://puma;
|
|
22
|
+
proxy_set_header Host $host;
|
|
23
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
24
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
25
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
location ~* \\-[0-9a-f]\\{64\\}\\.(ico|css|js|gif|jpe?g|png|webp)$ {
|
|
29
|
+
access_log off;
|
|
30
|
+
expires max;
|
|
31
|
+
add_header Cache-Control public;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
gzip_static on;
|
|
35
|
+
}
|
|
36
|
+
EOF"
|
|
37
|
+
|
|
38
|
+
dest_path = path.sub("sites-available", "sites-enabled")
|
|
39
|
+
system "sudo ln -sf #{path} #{dest_path}" if !File.exist?(dest_path)
|
|
40
|
+
|
|
41
|
+
system "sudo service nginx restart"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
no_commands do
|
|
45
|
+
def nginx_server_name
|
|
46
|
+
case ENV["RAILS_ENV"]
|
|
47
|
+
when "production"
|
|
48
|
+
"*.#{URI.parse(config[:production].url).host} _"
|
|
49
|
+
when "staging" then "#{project_name}.botandrose.com"
|
|
50
|
+
else "#{project_name}.localhost"
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
require "uri"
|
|
2
|
+
require "shellwords"
|
|
3
|
+
require "bard/plugins/ssh/server"
|
|
4
|
+
require "bard/command"
|
|
5
|
+
|
|
6
|
+
module Bard
|
|
7
|
+
module SSH
|
|
8
|
+
def server
|
|
9
|
+
@server
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def gateway
|
|
13
|
+
server.gateway
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def ssh_key
|
|
17
|
+
server.ssh_key
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def env
|
|
21
|
+
server.env
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def ssh_uri
|
|
25
|
+
server.ssh_uri
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def scp_uri(file_path = nil)
|
|
29
|
+
full_path = "/#{path}"
|
|
30
|
+
full_path += "/#{file_path}" if file_path
|
|
31
|
+
URI::Generic.build(scheme: "scp", userinfo: server.user, host: server.host, port: server.port.to_i, path: full_path)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def rsync_uri(file_path = nil)
|
|
35
|
+
uri = ssh_uri
|
|
36
|
+
str = "#{uri.user}@#{uri.host}"
|
|
37
|
+
str += ":#{path}"
|
|
38
|
+
str += "/#{file_path}" if file_path
|
|
39
|
+
str
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def run!(command, home: false, verbose: false, quiet: false, capture: false)
|
|
43
|
+
result = Command.run!(ssh_command(command, home:), verbose:, quiet:)
|
|
44
|
+
result if capture
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def run(command, home: false, verbose: false, quiet: false)
|
|
48
|
+
Command.run(ssh_command(command, home:), verbose:, quiet:)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def exec!(command, home: false)
|
|
52
|
+
Command.exec!(ssh_command(command, home:))
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def ssh_command(command, home: false)
|
|
58
|
+
cmd = command
|
|
59
|
+
cmd = "#{env} #{command}" if env
|
|
60
|
+
|
|
61
|
+
unless home
|
|
62
|
+
cmd = "cd #{path} && #{cmd}" if path
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
ssh_opts = ["-tt", "-o StrictHostKeyChecking=no", "-o UserKnownHostsFile=/dev/null", "-o LogLevel=ERROR"]
|
|
66
|
+
ssh_opts << "-i #{ssh_key}" if ssh_key
|
|
67
|
+
ssh_opts << "-p #{server.port}" if server.port && server.port != "22"
|
|
68
|
+
ssh_opts << "-o ProxyJump=#{gateway}" if gateway
|
|
69
|
+
|
|
70
|
+
ssh_target = "#{server.user}@#{server.host}"
|
|
71
|
+
|
|
72
|
+
"ssh #{ssh_opts.join(" ")} #{ssh_target} #{Shellwords.shellescape(cmd)}"
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
require "uri"
|
|
2
|
+
require "bard/copy"
|
|
3
|
+
require "bard/command"
|
|
4
|
+
|
|
5
|
+
module Bard
|
|
6
|
+
module SSH
|
|
7
|
+
class Copy < Bard::Copy
|
|
8
|
+
def self.can_handle?(from, to)
|
|
9
|
+
from.has_capability?(:ssh) || to.has_capability?(:ssh)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def file
|
|
13
|
+
if from.key == :local
|
|
14
|
+
scp_using_local :to, to
|
|
15
|
+
elsif to.key == :local
|
|
16
|
+
scp_using_local :from, from
|
|
17
|
+
else
|
|
18
|
+
scp_as_mediator
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def scp_using_local direction, target
|
|
23
|
+
ssh_server = target.server
|
|
24
|
+
|
|
25
|
+
gateway = ssh_server.gateway ? "-oProxyCommand='ssh #{ssh_server.gateway} -W %h:%p'" : ""
|
|
26
|
+
|
|
27
|
+
ssh_key = ssh_server.ssh_key ? "-i #{ssh_server.ssh_key}" : ""
|
|
28
|
+
|
|
29
|
+
ssh_opts = "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR"
|
|
30
|
+
|
|
31
|
+
port = ssh_server.port
|
|
32
|
+
port_opt = port && port.to_s != "22" ? "-P #{port}" : ""
|
|
33
|
+
|
|
34
|
+
from_and_to = [path, target.scp_uri(path).to_s]
|
|
35
|
+
from_and_to.reverse! if direction == :from
|
|
36
|
+
|
|
37
|
+
command = ["scp", ssh_opts, gateway, ssh_key, port_opt, *from_and_to].reject(&:empty?).join(" ")
|
|
38
|
+
Bard::Command.run! command, verbose: verbose
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def scp_as_mediator
|
|
42
|
+
from_server = from.server
|
|
43
|
+
to_server = to.server
|
|
44
|
+
|
|
45
|
+
raise NotImplementedError if from_server.gateway || to_server.gateway || from_server.ssh_key || to_server.ssh_key
|
|
46
|
+
command = "scp -o ForwardAgent=yes #{from.scp_uri(path)} #{to.scp_uri(path)}"
|
|
47
|
+
Bard::Command.run! command, verbose: verbose
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def dir
|
|
51
|
+
if from.key == :local
|
|
52
|
+
rsync_using_local :to, to
|
|
53
|
+
elsif to.key == :local
|
|
54
|
+
rsync_using_local :from, from
|
|
55
|
+
else
|
|
56
|
+
rsync_as_mediator
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def rsync_using_local direction, target
|
|
61
|
+
ssh_server = target.server
|
|
62
|
+
|
|
63
|
+
ssh_uri = ssh_server.ssh_uri
|
|
64
|
+
|
|
65
|
+
gateway = ssh_server.gateway ? "-oProxyCommand=\"ssh #{ssh_server.gateway} -W %h:%p\"" : ""
|
|
66
|
+
|
|
67
|
+
ssh_key = ssh_server.ssh_key ? "-i #{ssh_server.ssh_key}" : ""
|
|
68
|
+
ssh = "-e'ssh #{gateway} -p#{ssh_uri.port || 22}'"
|
|
69
|
+
|
|
70
|
+
from_and_to = ["./#{path}", target.rsync_uri(path)]
|
|
71
|
+
from_and_to.reverse! if direction == :from
|
|
72
|
+
from_and_to[-1].sub! %r(/[^/]+$), '/'
|
|
73
|
+
|
|
74
|
+
command = "rsync #{ssh} --delete --info=progress2 -az #{from_and_to.join(" ")}"
|
|
75
|
+
Bard::Command.run! command, verbose: verbose
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def rsync_as_mediator
|
|
79
|
+
from_server = from.server
|
|
80
|
+
to_server = to.server
|
|
81
|
+
|
|
82
|
+
raise NotImplementedError if from_server.gateway || to_server.gateway || from_server.ssh_key || to_server.ssh_key
|
|
83
|
+
|
|
84
|
+
from_uri = from_server.ssh_uri
|
|
85
|
+
to_uri = to_server.ssh_uri
|
|
86
|
+
|
|
87
|
+
from_str = "-p#{from_uri.port || 22} #{from_uri.user}@#{from_uri.host}"
|
|
88
|
+
to_str = to.rsync_uri(path).sub(%r(/[^/]+$), '/')
|
|
89
|
+
|
|
90
|
+
command = %(ssh -A #{from_str} 'rsync -e \"ssh -A -p#{to_uri.port || 22} -o StrictHostKeyChecking=no -o LogLevel=ERROR\" --delete --info=progress2 -az #{from.path}/#{path} #{to_str}')
|
|
91
|
+
Bard::Command.run! command, verbose: verbose
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
require "uri"
|
|
2
|
-
require "bard/command"
|
|
3
2
|
|
|
4
3
|
module Bard
|
|
5
4
|
class SSHServer
|
|
@@ -9,13 +8,11 @@ module Bard
|
|
|
9
8
|
@uri_string = uri_string
|
|
10
9
|
@options = options
|
|
11
10
|
|
|
12
|
-
# Parse URI
|
|
13
11
|
uri = parse_uri(uri_string)
|
|
14
12
|
@user = uri.user || ENV['USER']
|
|
15
13
|
@host = uri.host
|
|
16
14
|
@port = uri.port ? uri.port.to_s : "22"
|
|
17
15
|
|
|
18
|
-
# Store options
|
|
19
16
|
@path = options[:path]
|
|
20
17
|
@gateway = options[:gateway]
|
|
21
18
|
@ssh_key = options[:ssh_key]
|
|
@@ -23,39 +20,42 @@ module Bard
|
|
|
23
20
|
end
|
|
24
21
|
|
|
25
22
|
def ssh_uri
|
|
26
|
-
"
|
|
23
|
+
URI("ssh://#{user}@#{host}:#{port}")
|
|
27
24
|
end
|
|
28
25
|
|
|
29
26
|
def hostname
|
|
30
27
|
host
|
|
31
28
|
end
|
|
32
29
|
|
|
30
|
+
def to_s
|
|
31
|
+
str = "#{user}@#{host}"
|
|
32
|
+
str += ":#{port}" if port && port != "22"
|
|
33
|
+
str
|
|
34
|
+
end
|
|
35
|
+
|
|
33
36
|
def connection_string
|
|
34
37
|
"#{user}@#{host}"
|
|
35
38
|
end
|
|
36
39
|
|
|
37
|
-
def
|
|
38
|
-
|
|
39
|
-
|
|
40
|
+
def ==(other)
|
|
41
|
+
return false unless other.is_a?(Bard::SSHServer)
|
|
42
|
+
state == other.state
|
|
40
43
|
end
|
|
44
|
+
alias_method :eql?, :==
|
|
41
45
|
|
|
42
|
-
def
|
|
43
|
-
|
|
44
|
-
if status.to_i.nonzero?
|
|
45
|
-
raise Command::Error, "Command failed: #{command}\n#{error}"
|
|
46
|
-
end
|
|
47
|
-
output
|
|
46
|
+
def hash
|
|
47
|
+
state.hash
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
protected
|
|
51
|
+
|
|
52
|
+
def state
|
|
53
|
+
[user, host, port, path, gateway, ssh_key, env]
|
|
53
54
|
end
|
|
54
55
|
|
|
55
56
|
private
|
|
56
57
|
|
|
57
58
|
def parse_uri(uri_string)
|
|
58
|
-
# Handle user@host:port format
|
|
59
59
|
if uri_string =~ /^([^@]+@)?([^:]+)(?::(\d+))?$/
|
|
60
60
|
user_part = $1&.chomp('@')
|
|
61
61
|
host_part = $2
|
|
@@ -71,30 +71,5 @@ module Bard
|
|
|
71
71
|
URI.parse("ssh://#{uri_string}")
|
|
72
72
|
end
|
|
73
73
|
end
|
|
74
|
-
|
|
75
|
-
def build_command(command)
|
|
76
|
-
cmd = "ssh -tt"
|
|
77
|
-
|
|
78
|
-
# Add port
|
|
79
|
-
cmd += " -p #{port}" if port != "22"
|
|
80
|
-
|
|
81
|
-
# Add gateway
|
|
82
|
-
cmd += " -o ProxyJump=#{gateway}" if gateway
|
|
83
|
-
|
|
84
|
-
# Add SSH key
|
|
85
|
-
cmd += " -i #{ssh_key}" if ssh_key
|
|
86
|
-
|
|
87
|
-
# Add user@host
|
|
88
|
-
cmd += " #{user}@#{host}"
|
|
89
|
-
|
|
90
|
-
# Add command with path and env
|
|
91
|
-
remote_cmd = ""
|
|
92
|
-
remote_cmd += "#{env} " if env
|
|
93
|
-
remote_cmd += "cd #{path} && " if path
|
|
94
|
-
remote_cmd += command
|
|
95
|
-
|
|
96
|
-
cmd += " '#{remote_cmd}'"
|
|
97
|
-
cmd
|
|
98
|
-
end
|
|
99
74
|
end
|
|
100
75
|
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require "bard/target"
|
|
2
|
+
require "bard/plugins/url/target_methods"
|
|
3
|
+
require "bard/plugins/ssh/connection"
|
|
4
|
+
|
|
5
|
+
class Bard::Target
|
|
6
|
+
def ssh(uri = nil, **options)
|
|
7
|
+
if uri.nil?
|
|
8
|
+
return @server
|
|
9
|
+
else
|
|
10
|
+
extend Bard::SSH
|
|
11
|
+
|
|
12
|
+
@server = Bard::SSHServer.new(uri, **options)
|
|
13
|
+
@path = options[:path] if options[:path]
|
|
14
|
+
enable_capability(:ssh)
|
|
15
|
+
|
|
16
|
+
hostname = @server.hostname
|
|
17
|
+
url("https://#{hostname}") if hostname
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
require "bard/plugins/ssh/target_methods"
|
|
2
|
+
require "bard/plugins/ssh/copy"
|
|
3
|
+
|
|
4
|
+
class Bard::CLI
|
|
5
|
+
option :home, type: :boolean
|
|
6
|
+
desc "ssh [to=production]", "logs into the specified server via SSH"
|
|
7
|
+
def ssh(to = :production)
|
|
8
|
+
config[to].exec! "exec $SHELL -l", home: options[:home]
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
require "bard/target"
|
|
2
|
+
|
|
3
|
+
class Bard::Target
|
|
4
|
+
def url(value = nil)
|
|
5
|
+
if value.nil?
|
|
6
|
+
@url
|
|
7
|
+
elsif value == false
|
|
8
|
+
@url = nil
|
|
9
|
+
@capabilities.delete(:url)
|
|
10
|
+
else
|
|
11
|
+
@url = normalize_url(value)
|
|
12
|
+
enable_capability(:url)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def normalize_url(value)
|
|
19
|
+
normalized = value.to_s
|
|
20
|
+
normalized = "https://#{normalized}" unless normalized.start_with?("http")
|
|
21
|
+
normalized
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require "bard/plugins/url/target_methods"
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
class Bard::CLI
|
|
2
|
+
desc "vim [branch=master]", "open all files that have changed since master"
|
|
3
|
+
def vim(branch = "master")
|
|
4
|
+
exec "vim -p `(git diff #{branch} --name-only; git ls-files --others --exclude-standard) | grep -v '^app/assets/images/' | grep -v '^app/assets/stylesheets/' | while read f; do [ -f \"$f\" ] && ! file -b \"$f\" | grep -q \"binary\" && echo \"$f\"; done | tac`"
|
|
5
|
+
end
|
|
6
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Bard
|
|
2
|
+
module Retryable
|
|
3
|
+
MAX_RETRIES = 5
|
|
4
|
+
INITIAL_DELAY = 1
|
|
5
|
+
|
|
6
|
+
def retry_with_backoff(max_retries: MAX_RETRIES)
|
|
7
|
+
retries = 0
|
|
8
|
+
delay = INITIAL_DELAY
|
|
9
|
+
|
|
10
|
+
begin
|
|
11
|
+
yield
|
|
12
|
+
rescue => e
|
|
13
|
+
if retries < max_retries
|
|
14
|
+
retries += 1
|
|
15
|
+
puts " Network error (attempt #{retries}/#{max_retries}): #{e.message}. Retrying in #{delay}s..."
|
|
16
|
+
sleep(delay)
|
|
17
|
+
delay *= 2
|
|
18
|
+
retry
|
|
19
|
+
else
|
|
20
|
+
raise "Network error after #{max_retries} attempts: #{e.message}"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|