bard 1.9.6 → 2.0.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/.github/workflows/ci.yml +2 -2
- data/CLAUDE.md +1 -1
- data/PLUGINS.md +31 -46
- data/bard.gemspec +2 -1
- data/features/ci.feature +1 -0
- data/features/data.feature +1 -0
- data/features/deploy.feature +1 -0
- data/features/deploy_git_workflow.feature +1 -0
- data/features/run.feature +1 -0
- data/features/step_definitions/bard_steps.rb +1 -0
- data/features/support/test_server.rb +3 -3
- data/lib/bard/cli.rb +9 -28
- data/lib/bard/command.rb +9 -88
- data/lib/bard/config.rb +38 -177
- data/lib/bard/copy.rb +28 -82
- data/lib/bard/plugins/data.rb +56 -0
- data/lib/bard/{ci → plugins/deploy/ci}/github_actions.rb +2 -2
- data/lib/bard/{ci → plugins/deploy/ci}/jenkins.rb +1 -1
- data/lib/bard/{ci → plugins/deploy/ci}/local.rb +1 -1
- data/lib/bard/{ci → plugins/deploy/ci}/runner.rb +3 -3
- data/lib/bard/{ci.rb → plugins/deploy/ci.rb} +1 -1
- data/lib/bard/plugins/deploy/ssh_strategy.rb +27 -0
- data/lib/bard/{deploy_strategy.rb → plugins/deploy/strategy.rb} +1 -4
- 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} +2 -2
- data/lib/bard/{deploy_strategy/github_pages.rb → plugins/github_pages/strategy.rb} +3 -4
- data/lib/bard/plugins/github_pages.rb +11 -15
- data/lib/bard/plugins/hurt.rb +12 -4
- 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 +7 -3
- 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 +8 -4
- 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} +10 -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 +5 -4
- data/lib/bard/retryable.rb +25 -0
- data/lib/bard/target.rb +21 -230
- data/lib/bard/version.rb +1 -1
- data/lib/bard.rb +1 -3
- data/spec/acceptance/docker/Dockerfile +1 -1
- data/spec/bard/capability_spec.rb +8 -50
- data/spec/bard/ci/github_actions_spec.rb +1 -1
- data/spec/bard/ci/jenkins_spec.rb +1 -1
- data/spec/bard/ci/runner_spec.rb +3 -3
- data/spec/bard/ci_spec.rb +1 -1
- data/spec/bard/cli/ci_spec.rb +4 -23
- data/spec/bard/cli/data_spec.rb +7 -26
- data/spec/bard/cli/deploy_spec.rb +43 -40
- data/spec/bard/cli/hurt_spec.rb +2 -8
- data/spec/bard/cli/install_spec.rb +4 -10
- data/spec/bard/cli/master_key_spec.rb +5 -19
- data/spec/bard/cli/open_spec.rb +17 -35
- data/spec/bard/cli/ping_spec.rb +10 -25
- data/spec/bard/cli/run_spec.rb +10 -23
- 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 +17 -32
- data/spec/bard/cli/vim_spec.rb +5 -11
- data/spec/bard/command_spec.rb +1 -10
- data/spec/bard/config_spec.rb +68 -116
- 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 +1 -1
- data/spec/bard/ping_spec.rb +5 -5
- data/spec/bard/ssh_copy_spec.rb +44 -0
- data/spec/bard/ssh_server_spec.rb +1 -98
- data/spec/bard/target_spec.rb +61 -108
- metadata +50 -124
- data/lib/bard/ci/retryable.rb +0 -27
- data/lib/bard/cli/ci.rb +0 -73
- data/lib/bard/cli/command.rb +0 -26
- data/lib/bard/cli/data.rb +0 -45
- data/lib/bard/cli/deploy.rb +0 -116
- data/lib/bard/cli/hurt.rb +0 -15
- data/lib/bard/cli/install.rb +0 -11
- 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 -18
- data/lib/bard/cli/ping.rb +0 -12
- data/lib/bard/cli/provision.rb +0 -34
- data/lib/bard/cli/run.rb +0 -26
- data/lib/bard/cli/setup.rb +0 -56
- data/lib/bard/cli/ssh.rb +0 -14
- data/lib/bard/cli/stage.rb +0 -35
- data/lib/bard/cli/vim.rb +0 -8
- data/lib/bard/default_config.rb +0 -35
- data/lib/bard/deploy_strategy/ssh.rb +0 -19
- data/lib/bard/deprecation.rb +0 -19
- data/lib/bard/github_pages.rb +0 -134
- data/lib/bard/plugin.rb +0 -100
- data/lib/bard/plugins/backup.rb +0 -19
- data/lib/bard/plugins/jenkins.rb +0 -6
- data/lib/bard/plugins/new.rb +0 -5
- data/lib/bard/plugins/provision.rb +0 -5
- 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 -79
- data/lib/bard/provision/swapfile.rb +0 -23
- data/lib/bard/provision/user.rb +0 -42
- data/lib/bard/provision.rb +0 -16
- data/lib/bard/server.rb +0 -160
- 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/deprecation_spec.rb +0 -281
- data/spec/bard/github_pages_spec.rb +0 -143
- data/spec/bard/plugin_spec.rb +0 -79
- 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 -242
- data/spec/bard/provision/swapfile_spec.rb +0 -33
- 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
|
@@ -1,118 +1,31 @@
|
|
|
1
1
|
require "spec_helper"
|
|
2
2
|
require "bard/target"
|
|
3
|
-
require "bard/
|
|
3
|
+
require "bard/plugins/deploy"
|
|
4
4
|
|
|
5
|
-
describe "
|
|
5
|
+
describe "Deploy strategy target methods" do
|
|
6
6
|
let(:config) { double("config", project_name: "testapp") }
|
|
7
7
|
let(:target) { Bard::Target.new(:production, config) }
|
|
8
8
|
|
|
9
|
-
before do
|
|
10
|
-
# Register test strategies
|
|
11
|
-
class Bard::DeployStrategy::Jets < Bard::DeployStrategy
|
|
12
|
-
def deploy
|
|
13
|
-
# test implementation
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
class Bard::DeployStrategy::Docker < Bard::DeployStrategy
|
|
18
|
-
def deploy
|
|
19
|
-
# test implementation
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
describe "method_missing for strategies" do
|
|
25
|
-
it "enables strategy when method name matches registered strategy" do
|
|
26
|
-
target.jets("https://api.example.com")
|
|
27
|
-
expect(target.deploy_strategy).to eq(:jets)
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
it "stores strategy options" do
|
|
31
|
-
target.jets("https://api.example.com", run_tests: true, env: "production")
|
|
32
|
-
options = target.strategy_options(:jets)
|
|
33
|
-
expect(options[:run_tests]).to be true
|
|
34
|
-
expect(options[:env]).to eq("production")
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
it "auto-configures ping URL from first argument if it's a URL" do
|
|
38
|
-
target.jets("https://api.example.com")
|
|
39
|
-
expect(target.ping_urls).to include("https://api.example.com")
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
it "works with multiple strategies" do
|
|
43
|
-
target1 = Bard::Target.new(:production, config)
|
|
44
|
-
target2 = Bard::Target.new(:staging, config)
|
|
45
|
-
|
|
46
|
-
target1.jets("https://api.example.com")
|
|
47
|
-
target2.docker("https://app.example.com")
|
|
48
|
-
|
|
49
|
-
expect(target1.deploy_strategy).to eq(:jets)
|
|
50
|
-
expect(target2.deploy_strategy).to eq(:docker)
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
it "raises NoMethodError for unknown methods" do
|
|
54
|
-
expect { target.unknown_method("arg") }
|
|
55
|
-
.to raise_error(NoMethodError)
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
describe "strategy DSL integration" do
|
|
60
|
-
it "allows chaining with other configuration methods" do
|
|
61
|
-
target.jets("https://api.example.com", run_tests: true)
|
|
62
|
-
target.ssh("deploy@example.com:22", path: "app")
|
|
63
|
-
|
|
64
|
-
expect(target.deploy_strategy).to eq(:jets)
|
|
65
|
-
expect(target.has_capability?(:ssh)).to be true
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
it "allows strategy configuration without ping URL" do
|
|
69
|
-
target.docker(skip_build: true)
|
|
70
|
-
options = target.strategy_options(:docker)
|
|
71
|
-
expect(options[:skip_build]).to be true
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
describe "#strategy_options" do
|
|
76
|
-
it "returns options for the specified strategy" do
|
|
77
|
-
target.jets("https://api.example.com", run_tests: true, env: "prod")
|
|
78
|
-
options = target.strategy_options(:jets)
|
|
79
|
-
expect(options[:run_tests]).to be true
|
|
80
|
-
expect(options[:env]).to eq("prod")
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
it "returns empty hash if strategy not configured" do
|
|
84
|
-
options = target.strategy_options(:unknown)
|
|
85
|
-
expect(options).to eq({})
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
it "filters out URL from options" do
|
|
89
|
-
target.jets("https://api.example.com", run_tests: true)
|
|
90
|
-
options = target.strategy_options(:jets)
|
|
91
|
-
expect(options[:run_tests]).to be true
|
|
92
|
-
expect(options).not_to have_key(:url)
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
|
|
96
9
|
describe "#deploy_strategy" do
|
|
97
|
-
it "returns the configured strategy symbol" do
|
|
98
|
-
target.jets("https://api.example.com")
|
|
99
|
-
expect(target.deploy_strategy).to eq(:jets)
|
|
100
|
-
end
|
|
101
|
-
|
|
102
10
|
it "returns nil if no strategy configured" do
|
|
103
11
|
expect(target.deploy_strategy).to be_nil
|
|
104
12
|
end
|
|
13
|
+
|
|
14
|
+
it "returns :ssh after ssh is configured via deploy_strategy_instance" do
|
|
15
|
+
target.ssh("deploy@example.com:22")
|
|
16
|
+
expect(target.deploy_strategy_instance).to be_a(Bard::DeployStrategy::SSH)
|
|
17
|
+
end
|
|
105
18
|
end
|
|
106
19
|
|
|
107
20
|
describe "#deploy_strategy_instance" do
|
|
108
|
-
it "
|
|
109
|
-
target.
|
|
21
|
+
it "defaults to SSH strategy when SSH capability is present" do
|
|
22
|
+
target.ssh("deploy@example.com:22")
|
|
110
23
|
instance = target.deploy_strategy_instance
|
|
111
|
-
expect(instance).to be_a(Bard::DeployStrategy::
|
|
24
|
+
expect(instance).to be_a(Bard::DeployStrategy::SSH)
|
|
112
25
|
expect(instance.target).to eq(target)
|
|
113
26
|
end
|
|
114
27
|
|
|
115
|
-
it "raises error if no strategy configured" do
|
|
28
|
+
it "raises error if no strategy configured and no SSH capability" do
|
|
116
29
|
expect { target.deploy_strategy_instance }
|
|
117
30
|
.to raise_error(/No deployment strategy configured/)
|
|
118
31
|
end
|
|
@@ -123,4 +36,11 @@ describe "Dynamic DSL Methods" do
|
|
|
123
36
|
.to raise_error(/Unknown deployment strategy: unknown/)
|
|
124
37
|
end
|
|
125
38
|
end
|
|
39
|
+
|
|
40
|
+
describe "#strategy_options" do
|
|
41
|
+
it "returns empty hash if strategy not configured" do
|
|
42
|
+
options = target.strategy_options(:unknown)
|
|
43
|
+
expect(options).to eq({})
|
|
44
|
+
end
|
|
45
|
+
end
|
|
126
46
|
end
|
data/spec/bard/git_spec.rb
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
require "spec_helper"
|
|
2
|
-
require "bard/git"
|
|
2
|
+
require "bard/plugins/git"
|
|
3
3
|
|
|
4
4
|
describe Bard::Git do
|
|
5
5
|
describe ".current_branch" do
|
|
@@ -46,14 +46,18 @@ describe Bard::Git do
|
|
|
46
46
|
|
|
47
47
|
describe ".sha_of" do
|
|
48
48
|
it "should return the sha of a ref" do
|
|
49
|
-
allow(Bard::Git).to receive(:`).with("git rev-parse ref 2>/dev/null")
|
|
50
|
-
|
|
49
|
+
allow(Bard::Git).to receive(:`).with("git rev-parse ref 2>/dev/null") {
|
|
50
|
+
`true` # sets $? to success
|
|
51
|
+
"sha\n"
|
|
52
|
+
}
|
|
51
53
|
expect(Bard::Git.sha_of("ref")).to eq("sha")
|
|
52
54
|
end
|
|
53
55
|
|
|
54
56
|
it "should return nil if the ref does not exist" do
|
|
55
|
-
allow(Bard::Git).to receive(:`).with("git rev-parse ref 2>/dev/null")
|
|
56
|
-
|
|
57
|
+
allow(Bard::Git).to receive(:`).with("git rev-parse ref 2>/dev/null") {
|
|
58
|
+
`false` # sets $? to failure
|
|
59
|
+
"ref: fatal: ambiguous argument 'ref': unknown revision or path not in the working tree.\n"
|
|
60
|
+
}
|
|
57
61
|
expect(Bard::Git.sha_of("ref")).to be_nil
|
|
58
62
|
end
|
|
59
63
|
end
|
data/spec/bard/github_spec.rb
CHANGED
data/spec/bard/ping_spec.rb
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
require "spec_helper"
|
|
2
|
-
require "bard/ping"
|
|
2
|
+
require "bard/plugins/ping/check"
|
|
3
3
|
|
|
4
4
|
describe Bard::Ping do
|
|
5
|
-
let(:
|
|
6
|
-
let(:ping) { described_class.new(
|
|
5
|
+
let(:target) { double("target", ping: ["http://example.com"]) }
|
|
6
|
+
let(:ping) { described_class.new(target) }
|
|
7
7
|
|
|
8
8
|
def success_response
|
|
9
9
|
Net::HTTPSuccess.new(1.0, "200", "OK")
|
|
@@ -13,14 +13,14 @@ describe Bard::Ping do
|
|
|
13
13
|
Net::HTTPNotFound.new(1.0, "404", "Not Found")
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
context "when the
|
|
16
|
+
context "when the target is reachable" do
|
|
17
17
|
it "returns an empty array" do
|
|
18
18
|
allow(ping).to receive(:http_get).and_return(success_response)
|
|
19
19
|
expect(ping.call).to be_empty
|
|
20
20
|
end
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
context "when the
|
|
23
|
+
context "when the target is not reachable" do
|
|
24
24
|
it "returns the url" do
|
|
25
25
|
allow(ping).to receive(:http_get).and_return(not_found_response)
|
|
26
26
|
expect(ping.call).to eq(["http://example.com"])
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
require "bard/plugins/ssh/copy"
|
|
3
|
+
|
|
4
|
+
describe Bard::SSH::Copy do
|
|
5
|
+
let(:ssh_server) { double("ssh_server", gateway: nil, ssh_key: nil, port: "22", ssh_uri: double(port: 22, user: "user", host: "example.com")) }
|
|
6
|
+
let(:production) { double("production", key: :production, server: ssh_server, scp_uri: "user@example.com:/path/to/file", rsync_uri: "user@example.com:/path/to/", path: "/path/to", has_capability?: true) }
|
|
7
|
+
let(:local) { double("local", key: :local, has_capability?: false) }
|
|
8
|
+
|
|
9
|
+
describe ".can_handle?" do
|
|
10
|
+
it "handles pairs where at least one target has ssh capability" do
|
|
11
|
+
expect(Bard::SSH::Copy.can_handle?(local, production)).to be true
|
|
12
|
+
expect(Bard::SSH::Copy.can_handle?(production, local)).to be true
|
|
13
|
+
expect(Bard::SSH::Copy.can_handle?(production, production)).to be true
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "does not handle pairs where neither target has ssh capability" do
|
|
17
|
+
expect(Bard::SSH::Copy.can_handle?(local, local)).to be false
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
context ".file via Bard::Copy dispatch" do
|
|
22
|
+
it "should copy a file from a remote server to the local machine" do
|
|
23
|
+
expect(Bard::Command).to receive(:run!).with("scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR user@example.com:/path/to/file path/to/file", verbose: false)
|
|
24
|
+
Bard::Copy.file "path/to/file", from: production, to: local
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "should copy a file from the local machine to a remote server" do
|
|
28
|
+
expect(Bard::Command).to receive(:run!).with("scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR path/to/file user@example.com:/path/to/file", verbose: false)
|
|
29
|
+
Bard::Copy.file "path/to/file", from: local, to: production
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
context ".dir via Bard::Copy dispatch" do
|
|
34
|
+
it "should copy a directory from a remote server to the local machine" do
|
|
35
|
+
expect(Bard::Command).to receive(:run!).with("rsync -e'ssh -p22' --delete --info=progress2 -az user@example.com:/path/to/ ./path/", verbose: false)
|
|
36
|
+
Bard::Copy.dir "path/to", from: production, to: local
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "should copy a directory from the local machine to a remote server" do
|
|
40
|
+
expect(Bard::Command).to receive(:run!).with("rsync -e'ssh -p22' --delete --info=progress2 -az ./path/to user@example.com:/path/to/", verbose: false)
|
|
41
|
+
Bard::Copy.dir "path/to", from: local, to: production
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
require "spec_helper"
|
|
2
|
-
require "bard/
|
|
2
|
+
require "bard/plugins/ssh/server"
|
|
3
3
|
|
|
4
4
|
describe Bard::SSHServer do
|
|
5
5
|
describe "#initialize" do
|
|
@@ -73,101 +73,4 @@ describe Bard::SSHServer do
|
|
|
73
73
|
expect(server.connection_string).to eq("deploy@example.com")
|
|
74
74
|
end
|
|
75
75
|
end
|
|
76
|
-
|
|
77
|
-
describe "#run" do
|
|
78
|
-
let(:server) do
|
|
79
|
-
described_class.new("deploy@example.com:22", path: "/app")
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
it "executes command via SSH" do
|
|
83
|
-
expect(Open3).to receive(:capture3)
|
|
84
|
-
.with(/ssh.*deploy@example.com.*cd.+\/app.+ls/)
|
|
85
|
-
.and_return(["output", "", 0])
|
|
86
|
-
|
|
87
|
-
server.run("ls")
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
it "includes environment variables if configured" do
|
|
91
|
-
server_with_env = described_class.new("deploy@example.com:22",
|
|
92
|
-
path: "/app",
|
|
93
|
-
env: "RAILS_ENV=production"
|
|
94
|
-
)
|
|
95
|
-
|
|
96
|
-
expect(Open3).to receive(:capture3)
|
|
97
|
-
.with(/RAILS_ENV.*production/)
|
|
98
|
-
.and_return(["output", "", 0])
|
|
99
|
-
|
|
100
|
-
server_with_env.run("ls")
|
|
101
|
-
end
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
describe "#run!" do
|
|
105
|
-
let(:server) do
|
|
106
|
-
described_class.new("deploy@example.com:22", path: "/app")
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
it "executes command via SSH" do
|
|
110
|
-
expect(Open3).to receive(:capture3)
|
|
111
|
-
.with(/ssh.*deploy@example.com.*cd.+\/app.+ls/)
|
|
112
|
-
.and_return(["output", "", 0])
|
|
113
|
-
|
|
114
|
-
server.run!("ls")
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
it "raises error if command fails" do
|
|
118
|
-
expect(Open3).to receive(:capture3)
|
|
119
|
-
.and_return(["", "error", 1])
|
|
120
|
-
|
|
121
|
-
expect { server.run!("false") }.to raise_error(Bard::Command::Error)
|
|
122
|
-
end
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
describe "#exec!" do
|
|
126
|
-
let(:server) do
|
|
127
|
-
described_class.new("deploy@example.com:22", path: "/app")
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
it "replaces current process with SSH command" do
|
|
131
|
-
expect(server).to receive(:exec)
|
|
132
|
-
.with(/ssh.*deploy@example.com.*cd.+\/app.+ls/)
|
|
133
|
-
|
|
134
|
-
server.exec!("ls")
|
|
135
|
-
end
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
describe "path handling" do
|
|
139
|
-
it "uses path in commands if configured" do
|
|
140
|
-
server = described_class.new("deploy@example.com:22", path: "/var/www/app")
|
|
141
|
-
|
|
142
|
-
expect(Open3).to receive(:capture3)
|
|
143
|
-
.with(/cd.+\/var\/www\/app.+ls/)
|
|
144
|
-
.and_return(["output", "", 0])
|
|
145
|
-
|
|
146
|
-
server.run("ls")
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
it "works without path" do
|
|
150
|
-
server = described_class.new("deploy@example.com:22")
|
|
151
|
-
|
|
152
|
-
expect(Open3).to receive(:capture3)
|
|
153
|
-
.with(/ssh.*ls/)
|
|
154
|
-
.and_return(["output", "", 0])
|
|
155
|
-
|
|
156
|
-
server.run("ls")
|
|
157
|
-
end
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
describe "gateway/bastion support" do
|
|
161
|
-
it "uses ProxyJump for gateway" do
|
|
162
|
-
server = described_class.new("deploy@private.example.com:22",
|
|
163
|
-
gateway: "bastion@public.example.com:22"
|
|
164
|
-
)
|
|
165
|
-
|
|
166
|
-
expect(Open3).to receive(:capture3)
|
|
167
|
-
.with(/-o ProxyJump=bastion@public.example.com:22/)
|
|
168
|
-
.and_return(["output", "", 0])
|
|
169
|
-
|
|
170
|
-
server.run("ls")
|
|
171
|
-
end
|
|
172
|
-
end
|
|
173
76
|
end
|
data/spec/bard/target_spec.rb
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
require "spec_helper"
|
|
2
|
+
require "shellwords"
|
|
2
3
|
require "bard/target"
|
|
4
|
+
require "bard/plugins/url/target_methods"
|
|
5
|
+
require "bard/plugins/ssh/target_methods"
|
|
6
|
+
require "bard/plugins/ping/target_methods"
|
|
3
7
|
|
|
4
8
|
describe Bard::Target do
|
|
5
9
|
let(:config) { double("config", project_name: "testapp") }
|
|
@@ -16,12 +20,9 @@ describe Bard::Target do
|
|
|
16
20
|
|
|
17
21
|
it "initializes with no capabilities" do
|
|
18
22
|
expect(target.has_capability?(:ssh)).to be false
|
|
19
|
-
expect(target.has_capability?(:
|
|
23
|
+
expect(target.has_capability?(:url)).to be false
|
|
20
24
|
end
|
|
21
25
|
|
|
22
|
-
it "initializes with no deploy strategy" do
|
|
23
|
-
expect(target.deploy_strategy).to be_nil
|
|
24
|
-
end
|
|
25
26
|
end
|
|
26
27
|
|
|
27
28
|
describe "#ssh" do
|
|
@@ -75,50 +76,67 @@ describe Bard::Target do
|
|
|
75
76
|
expect(target.env).to eq("RAILS_ENV=production")
|
|
76
77
|
end
|
|
77
78
|
|
|
78
|
-
it "auto-configures
|
|
79
|
-
expect(target.
|
|
79
|
+
it "auto-configures url from hostname" do
|
|
80
|
+
expect(target.url).to eq("https://example.com")
|
|
80
81
|
end
|
|
81
82
|
end
|
|
82
83
|
|
|
83
|
-
context "
|
|
84
|
-
before { target.ssh(false) }
|
|
85
|
-
|
|
84
|
+
context "without ssh configured" do
|
|
86
85
|
it "does not enable SSH capability" do
|
|
87
86
|
expect(target.has_capability?(:ssh)).to be false
|
|
88
87
|
end
|
|
89
88
|
|
|
90
|
-
it "
|
|
91
|
-
expect(target.
|
|
89
|
+
it "returns nil from ssh getter" do
|
|
90
|
+
expect(target.ssh).to be_nil
|
|
92
91
|
end
|
|
93
92
|
end
|
|
94
93
|
end
|
|
95
94
|
|
|
96
|
-
describe "#
|
|
97
|
-
it "enables
|
|
98
|
-
target.
|
|
99
|
-
expect(target.has_capability?(:
|
|
100
|
-
expect(target.
|
|
95
|
+
describe "#url" do
|
|
96
|
+
it "enables url capability" do
|
|
97
|
+
target.url("https://example.com")
|
|
98
|
+
expect(target.has_capability?(:url)).to be true
|
|
99
|
+
expect(target.url).to eq("https://example.com")
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
it "normalizes URLs without scheme" do
|
|
103
|
+
target.url("example.com")
|
|
104
|
+
expect(target.url).to eq("https://example.com")
|
|
101
105
|
end
|
|
102
106
|
|
|
107
|
+
it "disables url with false" do
|
|
108
|
+
target.url("https://example.com")
|
|
109
|
+
target.url(false)
|
|
110
|
+
expect(target.has_capability?(:url)).to be false
|
|
111
|
+
expect(target.url).to be_nil
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
describe "#ping" do
|
|
103
116
|
it "accepts multiple URLs" do
|
|
104
|
-
target.ping("https://example.com", "/health"
|
|
105
|
-
expect(target.
|
|
106
|
-
|
|
107
|
-
|
|
117
|
+
target.ping("https://example.com", "https://example.com/health")
|
|
118
|
+
expect(target.ping).to eq(["https://example.com", "https://example.com/health"])
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
it "defaults to url when not explicitly set" do
|
|
122
|
+
target.url("https://example.com")
|
|
123
|
+
expect(target.ping).to eq(["https://example.com"])
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
it "returns empty array when no url or ping configured" do
|
|
127
|
+
expect(target.ping).to eq([])
|
|
108
128
|
end
|
|
109
129
|
|
|
110
130
|
it "disables ping with false" do
|
|
111
131
|
target.ping("https://example.com")
|
|
112
132
|
target.ping(false)
|
|
113
|
-
expect(target.
|
|
114
|
-
expect(target.ping_urls).to be_empty
|
|
133
|
+
expect(target.ping).to be_empty
|
|
115
134
|
end
|
|
116
135
|
end
|
|
117
136
|
|
|
118
137
|
describe "#path" do
|
|
119
|
-
it "
|
|
120
|
-
target.path("
|
|
121
|
-
expect(target.path).to eq("/var/www/app")
|
|
138
|
+
it "defaults to project name" do
|
|
139
|
+
expect(target.path).to eq("testapp")
|
|
122
140
|
end
|
|
123
141
|
|
|
124
142
|
it "can be set via ssh options" do
|
|
@@ -127,108 +145,43 @@ describe Bard::Target do
|
|
|
127
145
|
end
|
|
128
146
|
end
|
|
129
147
|
|
|
130
|
-
describe "
|
|
131
|
-
|
|
132
|
-
|
|
148
|
+
describe "command execution" do
|
|
149
|
+
describe "local (base)" do
|
|
150
|
+
it "runs commands locally" do
|
|
151
|
+
expect(Bard::Command).to receive(:run!)
|
|
152
|
+
.with("ls", verbose: false, quiet: false)
|
|
153
|
+
target.run!("ls")
|
|
154
|
+
end
|
|
133
155
|
end
|
|
134
156
|
|
|
135
|
-
describe "
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
expect { target_without_ssh.run!("ls") }
|
|
139
|
-
.to raise_error(/SSH not configured/)
|
|
157
|
+
describe "remote (SSH)" do
|
|
158
|
+
before do
|
|
159
|
+
target.ssh("deploy@example.com:22", path: "/app")
|
|
140
160
|
end
|
|
141
161
|
|
|
142
|
-
it "
|
|
162
|
+
it "runs commands on remote server" do
|
|
163
|
+
expected_cmd = "ssh -tt -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR deploy@example.com #{Shellwords.shellescape("cd /app && ls")}"
|
|
143
164
|
expect(Bard::Command).to receive(:run!)
|
|
144
|
-
.with(
|
|
165
|
+
.with(expected_cmd, verbose: false, quiet: false)
|
|
145
166
|
target.run!("ls")
|
|
146
167
|
end
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
describe "#run" do
|
|
150
|
-
it "requires SSH capability" do
|
|
151
|
-
target_without_ssh = described_class.new(:local, config)
|
|
152
|
-
expect { target_without_ssh.run("ls") }
|
|
153
|
-
.to raise_error(/SSH not configured/)
|
|
154
|
-
end
|
|
155
168
|
|
|
156
|
-
it "
|
|
169
|
+
it "runs commands without raising on remote server" do
|
|
170
|
+
expected_cmd = "ssh -tt -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR deploy@example.com #{Shellwords.shellescape("cd /app && ls")}"
|
|
157
171
|
expect(Bard::Command).to receive(:run)
|
|
158
|
-
.with(
|
|
172
|
+
.with(expected_cmd, verbose: false, quiet: false)
|
|
159
173
|
target.run("ls")
|
|
160
174
|
end
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
describe "#exec!" do
|
|
164
|
-
it "requires SSH capability" do
|
|
165
|
-
target_without_ssh = described_class.new(:local, config)
|
|
166
|
-
expect { target_without_ssh.exec!("ls") }
|
|
167
|
-
.to raise_error(/SSH not configured/)
|
|
168
|
-
end
|
|
169
175
|
|
|
170
176
|
it "replaces process with remote command" do
|
|
177
|
+
expected_cmd = "ssh -tt -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR deploy@example.com #{Shellwords.shellescape("cd /app && ls")}"
|
|
171
178
|
expect(Bard::Command).to receive(:exec!)
|
|
172
|
-
.with(
|
|
179
|
+
.with(expected_cmd)
|
|
173
180
|
target.exec!("ls")
|
|
174
181
|
end
|
|
175
182
|
end
|
|
176
183
|
end
|
|
177
184
|
|
|
178
|
-
describe "file transfer" do
|
|
179
|
-
let(:source_target) do
|
|
180
|
-
t = described_class.new(:source, config)
|
|
181
|
-
t.ssh("source@example.com:22", path: "/source")
|
|
182
|
-
t
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
let(:dest_target) do
|
|
186
|
-
t = described_class.new(:dest, config)
|
|
187
|
-
t.ssh("dest@example.com:22", path: "/dest")
|
|
188
|
-
t
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
describe "#copy_file" do
|
|
192
|
-
it "requires SSH capability on source" do
|
|
193
|
-
target_without_ssh = described_class.new(:local, config)
|
|
194
|
-
expect { target_without_ssh.copy_file("test.txt", to: dest_target) }
|
|
195
|
-
.to raise_error(/SSH not configured/)
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
it "requires SSH capability on destination" do
|
|
199
|
-
target_without_ssh = described_class.new(:local, config)
|
|
200
|
-
expect { source_target.copy_file("test.txt", to: target_without_ssh) }
|
|
201
|
-
.to raise_error(/SSH not configured/)
|
|
202
|
-
end
|
|
203
|
-
|
|
204
|
-
it "copies file via SCP" do
|
|
205
|
-
expect(Bard::Copy).to receive(:file)
|
|
206
|
-
.with("test.txt", from: source_target, to: dest_target, verbose: false)
|
|
207
|
-
source_target.copy_file("test.txt", to: dest_target)
|
|
208
|
-
end
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
describe "#copy_dir" do
|
|
212
|
-
it "requires SSH capability on source" do
|
|
213
|
-
target_without_ssh = described_class.new(:local, config)
|
|
214
|
-
expect { target_without_ssh.copy_dir("test/", to: dest_target) }
|
|
215
|
-
.to raise_error(/SSH not configured/)
|
|
216
|
-
end
|
|
217
|
-
|
|
218
|
-
it "requires SSH capability on destination" do
|
|
219
|
-
target_without_ssh = described_class.new(:local, config)
|
|
220
|
-
expect { source_target.copy_dir("test/", to: target_without_ssh) }
|
|
221
|
-
.to raise_error(/SSH not configured/)
|
|
222
|
-
end
|
|
223
|
-
|
|
224
|
-
it "syncs directory via rsync" do
|
|
225
|
-
expect(Bard::Copy).to receive(:dir)
|
|
226
|
-
.with("test/", from: source_target, to: dest_target, verbose: false)
|
|
227
|
-
source_target.copy_dir("test/", to: dest_target)
|
|
228
|
-
end
|
|
229
|
-
end
|
|
230
|
-
end
|
|
231
|
-
|
|
232
185
|
describe "#to_s" do
|
|
233
186
|
it "returns the target key as string" do
|
|
234
187
|
expect(target.to_s).to eq("production")
|