bard 2.0.0.beta → 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 +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} +13 -6
- 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
|
@@ -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
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
require "spec_helper"
|
|
2
|
-
require "bard/github"
|
|
2
|
+
require "bard/plugins/github"
|
|
3
3
|
|
|
4
4
|
describe Bard::Github do
|
|
5
5
|
let(:github) { Bard::Github.new("test-project") }
|
|
6
6
|
|
|
7
7
|
before do
|
|
8
|
-
allow(
|
|
8
|
+
allow(Bard::Secrets).to receive(:fetch).with("github-apikey").and_return("12345")
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
describe "#get" do
|
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
|
|
@@ -40,14 +40,18 @@ describe Bard::SSHServer do
|
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
describe "#ssh_uri" do
|
|
43
|
-
it "returns
|
|
43
|
+
it "returns a URI object" do
|
|
44
44
|
server = described_class.new("deploy@example.com:22")
|
|
45
|
-
expect(server.ssh_uri).to
|
|
45
|
+
expect(server.ssh_uri).to be_a(URI::Generic)
|
|
46
|
+
expect(server.ssh_uri.scheme).to eq("ssh")
|
|
47
|
+
expect(server.ssh_uri.user).to eq("deploy")
|
|
48
|
+
expect(server.ssh_uri.host).to eq("example.com")
|
|
49
|
+
expect(server.ssh_uri.port).to eq(22)
|
|
46
50
|
end
|
|
47
51
|
|
|
48
52
|
it "includes port if non-standard" do
|
|
49
53
|
server = described_class.new("deploy@example.com:2222")
|
|
50
|
-
expect(server.ssh_uri).to eq(
|
|
54
|
+
expect(server.ssh_uri.port).to eq(2222)
|
|
51
55
|
end
|
|
52
56
|
end
|
|
53
57
|
|
|
@@ -69,101 +73,4 @@ describe Bard::SSHServer do
|
|
|
69
73
|
expect(server.connection_string).to eq("deploy@example.com")
|
|
70
74
|
end
|
|
71
75
|
end
|
|
72
|
-
|
|
73
|
-
describe "#run" do
|
|
74
|
-
let(:server) do
|
|
75
|
-
described_class.new("deploy@example.com:22", path: "/app")
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
it "executes command via SSH" do
|
|
79
|
-
expect(Open3).to receive(:capture3)
|
|
80
|
-
.with(/ssh.*deploy@example.com.*cd \/app && ls/)
|
|
81
|
-
.and_return(["output", "", 0])
|
|
82
|
-
|
|
83
|
-
server.run("ls")
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
it "includes environment variables if configured" do
|
|
87
|
-
server_with_env = described_class.new("deploy@example.com:22",
|
|
88
|
-
path: "/app",
|
|
89
|
-
env: "RAILS_ENV=production"
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
expect(Open3).to receive(:capture3)
|
|
93
|
-
.with(/RAILS_ENV=production/)
|
|
94
|
-
.and_return(["output", "", 0])
|
|
95
|
-
|
|
96
|
-
server_with_env.run("ls")
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
describe "#run!" do
|
|
101
|
-
let(:server) do
|
|
102
|
-
described_class.new("deploy@example.com:22", path: "/app")
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
it "executes command via SSH" do
|
|
106
|
-
expect(Open3).to receive(:capture3)
|
|
107
|
-
.with(/ssh.*deploy@example.com.*cd \/app && ls/)
|
|
108
|
-
.and_return(["output", "", 0])
|
|
109
|
-
|
|
110
|
-
server.run!("ls")
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
it "raises error if command fails" do
|
|
114
|
-
expect(Open3).to receive(:capture3)
|
|
115
|
-
.and_return(["", "error", 1])
|
|
116
|
-
|
|
117
|
-
expect { server.run!("false") }.to raise_error(Bard::Command::Error)
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
describe "#exec!" do
|
|
122
|
-
let(:server) do
|
|
123
|
-
described_class.new("deploy@example.com:22", path: "/app")
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
it "replaces current process with SSH command" do
|
|
127
|
-
expect(server).to receive(:exec)
|
|
128
|
-
.with(/ssh.*deploy@example.com.*cd \/app && ls/)
|
|
129
|
-
|
|
130
|
-
server.exec!("ls")
|
|
131
|
-
end
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
describe "path handling" do
|
|
135
|
-
it "uses path in commands if configured" do
|
|
136
|
-
server = described_class.new("deploy@example.com:22", path: "/var/www/app")
|
|
137
|
-
|
|
138
|
-
expect(Open3).to receive(:capture3)
|
|
139
|
-
.with(/cd \/var\/www\/app && ls/)
|
|
140
|
-
.and_return(["output", "", 0])
|
|
141
|
-
|
|
142
|
-
server.run("ls")
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
it "works without path" do
|
|
146
|
-
server = described_class.new("deploy@example.com:22")
|
|
147
|
-
|
|
148
|
-
expect(Open3).to receive(:capture3)
|
|
149
|
-
.with(/ssh.*ls/)
|
|
150
|
-
.and_return(["output", "", 0])
|
|
151
|
-
|
|
152
|
-
server.run("ls")
|
|
153
|
-
end
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
describe "gateway/bastion support" do
|
|
157
|
-
it "uses ProxyJump for gateway" do
|
|
158
|
-
server = described_class.new("deploy@private.example.com:22",
|
|
159
|
-
gateway: "bastion@public.example.com:22"
|
|
160
|
-
)
|
|
161
|
-
|
|
162
|
-
expect(Open3).to receive(:capture3)
|
|
163
|
-
.with(/-o ProxyJump=bastion@public.example.com:22/)
|
|
164
|
-
.and_return(["output", "", 0])
|
|
165
|
-
|
|
166
|
-
server.run("ls")
|
|
167
|
-
end
|
|
168
|
-
end
|
|
169
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
|
|
@@ -37,7 +38,11 @@ describe Bard::Target do
|
|
|
37
38
|
end
|
|
38
39
|
|
|
39
40
|
it "parses SSH URI" do
|
|
40
|
-
expect(target.ssh_uri).to
|
|
41
|
+
expect(target.ssh_uri).to be_a(URI::Generic)
|
|
42
|
+
expect(target.ssh_uri.scheme).to eq("ssh")
|
|
43
|
+
expect(target.ssh_uri.user).to eq("deploy")
|
|
44
|
+
expect(target.ssh_uri.host).to eq("example.com")
|
|
45
|
+
expect(target.ssh_uri.port).to eq(22)
|
|
41
46
|
end
|
|
42
47
|
end
|
|
43
48
|
|
|
@@ -71,50 +76,67 @@ describe Bard::Target do
|
|
|
71
76
|
expect(target.env).to eq("RAILS_ENV=production")
|
|
72
77
|
end
|
|
73
78
|
|
|
74
|
-
it "auto-configures
|
|
75
|
-
expect(target.
|
|
79
|
+
it "auto-configures url from hostname" do
|
|
80
|
+
expect(target.url).to eq("https://example.com")
|
|
76
81
|
end
|
|
77
82
|
end
|
|
78
83
|
|
|
79
|
-
context "
|
|
80
|
-
before { target.ssh(false) }
|
|
81
|
-
|
|
84
|
+
context "without ssh configured" do
|
|
82
85
|
it "does not enable SSH capability" do
|
|
83
86
|
expect(target.has_capability?(:ssh)).to be false
|
|
84
87
|
end
|
|
85
88
|
|
|
86
|
-
it "
|
|
87
|
-
expect(target.
|
|
89
|
+
it "returns nil from ssh getter" do
|
|
90
|
+
expect(target.ssh).to be_nil
|
|
88
91
|
end
|
|
89
92
|
end
|
|
90
93
|
end
|
|
91
94
|
|
|
92
|
-
describe "#
|
|
93
|
-
it "enables
|
|
94
|
-
target.
|
|
95
|
-
expect(target.has_capability?(:
|
|
96
|
-
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")
|
|
97
105
|
end
|
|
98
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
|
|
99
116
|
it "accepts multiple URLs" do
|
|
100
|
-
target.ping("https://example.com", "/health"
|
|
101
|
-
expect(target.
|
|
102
|
-
|
|
103
|
-
|
|
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([])
|
|
104
128
|
end
|
|
105
129
|
|
|
106
130
|
it "disables ping with false" do
|
|
107
131
|
target.ping("https://example.com")
|
|
108
132
|
target.ping(false)
|
|
109
|
-
expect(target.
|
|
110
|
-
expect(target.ping_urls).to be_empty
|
|
133
|
+
expect(target.ping).to be_empty
|
|
111
134
|
end
|
|
112
135
|
end
|
|
113
136
|
|
|
114
137
|
describe "#path" do
|
|
115
|
-
it "
|
|
116
|
-
target.path("
|
|
117
|
-
expect(target.path).to eq("/var/www/app")
|
|
138
|
+
it "defaults to project name" do
|
|
139
|
+
expect(target.path).to eq("testapp")
|
|
118
140
|
end
|
|
119
141
|
|
|
120
142
|
it "can be set via ssh options" do
|
|
@@ -123,108 +145,43 @@ describe Bard::Target do
|
|
|
123
145
|
end
|
|
124
146
|
end
|
|
125
147
|
|
|
126
|
-
describe "
|
|
127
|
-
|
|
128
|
-
|
|
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
|
|
129
155
|
end
|
|
130
156
|
|
|
131
|
-
describe "
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
expect { target_without_ssh.run!("ls") }
|
|
135
|
-
.to raise_error(/SSH not configured/)
|
|
157
|
+
describe "remote (SSH)" do
|
|
158
|
+
before do
|
|
159
|
+
target.ssh("deploy@example.com:22", path: "/app")
|
|
136
160
|
end
|
|
137
161
|
|
|
138
|
-
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")}"
|
|
139
164
|
expect(Bard::Command).to receive(:run!)
|
|
140
|
-
.with(
|
|
165
|
+
.with(expected_cmd, verbose: false, quiet: false)
|
|
141
166
|
target.run!("ls")
|
|
142
167
|
end
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
describe "#run" do
|
|
146
|
-
it "requires SSH capability" do
|
|
147
|
-
target_without_ssh = described_class.new(:local, config)
|
|
148
|
-
expect { target_without_ssh.run("ls") }
|
|
149
|
-
.to raise_error(/SSH not configured/)
|
|
150
|
-
end
|
|
151
168
|
|
|
152
|
-
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")}"
|
|
153
171
|
expect(Bard::Command).to receive(:run)
|
|
154
|
-
.with(
|
|
172
|
+
.with(expected_cmd, verbose: false, quiet: false)
|
|
155
173
|
target.run("ls")
|
|
156
174
|
end
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
describe "#exec!" do
|
|
160
|
-
it "requires SSH capability" do
|
|
161
|
-
target_without_ssh = described_class.new(:local, config)
|
|
162
|
-
expect { target_without_ssh.exec!("ls") }
|
|
163
|
-
.to raise_error(/SSH not configured/)
|
|
164
|
-
end
|
|
165
175
|
|
|
166
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")}"
|
|
167
178
|
expect(Bard::Command).to receive(:exec!)
|
|
168
|
-
.with(
|
|
179
|
+
.with(expected_cmd)
|
|
169
180
|
target.exec!("ls")
|
|
170
181
|
end
|
|
171
182
|
end
|
|
172
183
|
end
|
|
173
184
|
|
|
174
|
-
describe "file transfer" do
|
|
175
|
-
let(:source_target) do
|
|
176
|
-
t = described_class.new(:source, config)
|
|
177
|
-
t.ssh("source@example.com:22", path: "/source")
|
|
178
|
-
t
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
let(:dest_target) do
|
|
182
|
-
t = described_class.new(:dest, config)
|
|
183
|
-
t.ssh("dest@example.com:22", path: "/dest")
|
|
184
|
-
t
|
|
185
|
-
end
|
|
186
|
-
|
|
187
|
-
describe "#copy_file" do
|
|
188
|
-
it "requires SSH capability on source" do
|
|
189
|
-
target_without_ssh = described_class.new(:local, config)
|
|
190
|
-
expect { target_without_ssh.copy_file("test.txt", to: dest_target) }
|
|
191
|
-
.to raise_error(/SSH not configured/)
|
|
192
|
-
end
|
|
193
|
-
|
|
194
|
-
it "requires SSH capability on destination" do
|
|
195
|
-
target_without_ssh = described_class.new(:local, config)
|
|
196
|
-
expect { source_target.copy_file("test.txt", to: target_without_ssh) }
|
|
197
|
-
.to raise_error(/SSH not configured/)
|
|
198
|
-
end
|
|
199
|
-
|
|
200
|
-
it "copies file via SCP" do
|
|
201
|
-
expect(Bard::Copy).to receive(:file)
|
|
202
|
-
.with("test.txt", from: source_target, to: dest_target, verbose: false)
|
|
203
|
-
source_target.copy_file("test.txt", to: dest_target)
|
|
204
|
-
end
|
|
205
|
-
end
|
|
206
|
-
|
|
207
|
-
describe "#copy_dir" do
|
|
208
|
-
it "requires SSH capability on source" do
|
|
209
|
-
target_without_ssh = described_class.new(:local, config)
|
|
210
|
-
expect { target_without_ssh.copy_dir("test/", to: dest_target) }
|
|
211
|
-
.to raise_error(/SSH not configured/)
|
|
212
|
-
end
|
|
213
|
-
|
|
214
|
-
it "requires SSH capability on destination" do
|
|
215
|
-
target_without_ssh = described_class.new(:local, config)
|
|
216
|
-
expect { source_target.copy_dir("test/", to: target_without_ssh) }
|
|
217
|
-
.to raise_error(/SSH not configured/)
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
it "syncs directory via rsync" do
|
|
221
|
-
expect(Bard::Copy).to receive(:dir)
|
|
222
|
-
.with("test/", from: source_target, to: dest_target, verbose: false)
|
|
223
|
-
source_target.copy_dir("test/", to: dest_target)
|
|
224
|
-
end
|
|
225
|
-
end
|
|
226
|
-
end
|
|
227
|
-
|
|
228
185
|
describe "#to_s" do
|
|
229
186
|
it "returns the target key as string" do
|
|
230
187
|
expect(target.to_s).to eq("production")
|