bard 1.8.0 → 1.9.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/CLAUDE.md +76 -0
- data/PLUGINS.md +114 -0
- data/README.md +14 -6
- data/features/ci.feature +62 -0
- data/features/deploy_git_workflow.feature +88 -0
- data/features/step_definitions/bard_steps.rb +96 -0
- data/features/support/bard-coverage +16 -0
- data/features/support/env.rb +10 -1
- data/features/support/test_server.rb +2 -1
- data/lib/bard/ci/github_actions.rb +1 -2
- data/lib/bard/ci/jenkins.rb +82 -11
- data/lib/bard/ci/local.rb +6 -6
- data/lib/bard/ci/runner.rb +35 -1
- data/lib/bard/ci.rb +11 -23
- data/lib/bard/cli/ci.rb +45 -38
- data/lib/bard/cli/deploy.rb +40 -8
- data/lib/bard/cli/hurt.rb +10 -15
- data/lib/bard/cli/install.rb +7 -12
- data/lib/bard/cli/open.rb +12 -16
- data/lib/bard/cli/ping.rb +8 -14
- data/lib/bard/cli/run.rb +5 -3
- data/lib/bard/cli/stage.rb +10 -1
- data/lib/bard/cli/vim.rb +5 -10
- data/lib/bard/cli.rb +13 -11
- data/lib/bard/command.rb +3 -3
- data/lib/bard/config.rb +4 -14
- data/lib/bard/github.rb +2 -4
- data/lib/bard/plugin.rb +99 -0
- data/lib/bard/plugins/backup.rb +19 -0
- data/lib/bard/plugins/github_pages.rb +34 -0
- data/lib/bard/plugins/hurt.rb +5 -0
- data/lib/bard/plugins/install.rb +5 -0
- data/lib/bard/plugins/jenkins.rb +6 -0
- data/lib/bard/plugins/new.rb +5 -0
- data/lib/bard/plugins/ping.rb +6 -0
- data/lib/bard/plugins/provision.rb +5 -0
- data/lib/bard/plugins/vim.rb +5 -0
- data/lib/bard/provision/ssh.rb +9 -2
- data/lib/bard/secrets.rb +10 -0
- data/lib/bard/server.rb +3 -2
- data/lib/bard/target.rb +3 -2
- data/lib/bard/version.rb +1 -1
- data/spec/bard/ci/github_actions_spec.rb +116 -13
- data/spec/bard/ci/jenkins_spec.rb +139 -0
- data/spec/bard/ci/runner_spec.rb +61 -0
- data/spec/bard/cli/ci_spec.rb +34 -8
- data/spec/bard/cli/deploy_spec.rb +20 -8
- data/spec/bard/cli/hurt_spec.rb +2 -2
- data/spec/bard/cli/install_spec.rb +4 -4
- data/spec/bard/cli/open_spec.rb +10 -8
- data/spec/bard/cli/ping_spec.rb +5 -5
- data/spec/bard/cli/run_spec.rb +20 -1
- data/spec/bard/cli/stage_spec.rb +20 -0
- data/spec/bard/cli/vim_spec.rb +5 -5
- data/spec/bard/config_spec.rb +12 -0
- data/spec/bard/github_spec.rb +1 -1
- data/spec/bard/plugin_spec.rb +79 -0
- data/spec/bard/provision/ssh_spec.rb +17 -4
- data/spec/spec_helper.rb +6 -1
- metadata +27 -3
- data/README.rdoc +0 -15
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
require "bard/ci/jenkins"
|
|
3
|
+
|
|
4
|
+
RSpec.describe Bard::CI::Jenkins do
|
|
5
|
+
let(:jenkins) { described_class.new("test-project", "master", "abc123") }
|
|
6
|
+
|
|
7
|
+
before do
|
|
8
|
+
allow(Bard::Secrets).to receive(:fetch).with("jenkins-user").and_return("micah")
|
|
9
|
+
allow(Bard::Secrets).to receive(:fetch).with("jenkins-token").and_return("fake-token")
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
describe "#get_last_time_elapsed" do
|
|
13
|
+
it "returns the duration in seconds from the last stable build" do
|
|
14
|
+
xml = "<build><duration>120000</duration></build>"
|
|
15
|
+
allow(jenkins).to receive(:`).with("curl -s http://micah:fake-token@ci.botandrose.com/job/test-project/lastStableBuild/api/xml").and_return(xml)
|
|
16
|
+
|
|
17
|
+
result = jenkins.send(:get_last_time_elapsed)
|
|
18
|
+
expect(result).to eq 120
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
describe "#run" do
|
|
23
|
+
let(:ci_url) { "http://micah:fake-token@ci.botandrose.com/job/test-project" }
|
|
24
|
+
|
|
25
|
+
before do
|
|
26
|
+
allow(jenkins).to receive(:sleep)
|
|
27
|
+
state = instance_double(Bard::CI::State, save: nil, delete: nil)
|
|
28
|
+
allow(jenkins).to receive(:state).and_return(state)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "waits until the build has started before polling" do
|
|
32
|
+
allow(jenkins).to receive(:`).with("curl -s -I -X POST -L '#{ci_url}/buildWithParameters?GIT_REF=master'").and_return("Location: http://ci.botandrose.com/queue/item/99/\r\n")
|
|
33
|
+
allow(jenkins).to receive(:`).with("curl -s #{ci_url}/lastStableBuild/api/xml").and_return("<build><duration>60000</duration></build>")
|
|
34
|
+
allow(jenkins).to receive(:`).with("curl -s -g '#{ci_url}/api/json?depth=1&tree=builds[queueId,number]'").and_return(
|
|
35
|
+
'{"builds":[{"queueId":1,"number":1}]}',
|
|
36
|
+
'{"builds":[{"queueId":99,"number":5}]}',
|
|
37
|
+
'{"builds":[{"queueId":99,"number":5}]}'
|
|
38
|
+
)
|
|
39
|
+
allow(jenkins).to receive(:`).with("curl -s #{ci_url}/5/api/json?tree=building,result").and_return('{"building":false,"result":"SUCCESS"}')
|
|
40
|
+
|
|
41
|
+
result = jenkins.run { |elapsed, last_time| }
|
|
42
|
+
expect(result).to eq true
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
describe "#started?" do
|
|
47
|
+
before do
|
|
48
|
+
jenkins.instance_variable_set(:@queueId, 99)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it "retries when builds list is empty (job just created)" do
|
|
52
|
+
allow(jenkins).to receive(:`).with(/api\/json\?depth=1/).and_return(
|
|
53
|
+
'{"builds":[]}',
|
|
54
|
+
'{"builds":[{"queueId":99,"number":1}]}'
|
|
55
|
+
)
|
|
56
|
+
allow(jenkins).to receive(:sleep)
|
|
57
|
+
|
|
58
|
+
expect(jenkins.send(:started?)).to eq true
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
describe "#exists?" do
|
|
63
|
+
it "returns truthy when the job exists" do
|
|
64
|
+
allow(jenkins).to receive(:`).with(/curl -s -I/).and_return("HTTP/1.1 200 OK\r\n")
|
|
65
|
+
|
|
66
|
+
expect(jenkins.exists?).to be_truthy
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it "auto-creates the job when it does not exist" do
|
|
70
|
+
allow(jenkins).to receive(:`).with(/curl -s -I/).and_return("HTTP/1.1 404 Not Found\r\n")
|
|
71
|
+
allow(jenkins).to receive(:`).with("git remote get-url origin").and_return("git@gitlab.com:botandrose/test-project.git\n")
|
|
72
|
+
allow(File).to receive(:exist?).with("config/master.key").and_return(false)
|
|
73
|
+
expect(jenkins).to receive(:`).with(/curl -s -X POST.*createItem\?name=test-project.*Content-Type: application\/xml/)
|
|
74
|
+
|
|
75
|
+
jenkins.exists?
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it "includes a master key build step when config/master.key exists" do
|
|
79
|
+
allow(jenkins).to receive(:`).with(/curl -s -I/).and_return("HTTP/1.1 404 Not Found\r\n")
|
|
80
|
+
allow(jenkins).to receive(:`).with("git remote get-url origin").and_return("git@gitlab.com:botandrose/test-project.git\n")
|
|
81
|
+
allow(File).to receive(:exist?).with("config/master.key").and_return(true)
|
|
82
|
+
allow(File).to receive(:read).with("config/master.key").and_return("abc123secret")
|
|
83
|
+
|
|
84
|
+
config_xml = nil
|
|
85
|
+
allow(jenkins).to receive(:`).with(/createItem/) do |cmd|
|
|
86
|
+
config_xml = cmd
|
|
87
|
+
""
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
jenkins.exists?
|
|
91
|
+
expect(config_xml).to include("abc123secret")
|
|
92
|
+
expect(config_xml).to include("config/master.key")
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
describe "#building? and #success?" do
|
|
97
|
+
before do
|
|
98
|
+
jenkins.instance_variable_set(:@job_id, 42)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
it "detects a successful build" do
|
|
102
|
+
allow(jenkins).to receive(:`).with("curl -s http://micah:fake-token@ci.botandrose.com/job/test-project/42/api/json?tree=building,result").and_return('{"building":false,"result":"SUCCESS"}')
|
|
103
|
+
|
|
104
|
+
expect(jenkins.send(:building?)).to eq false
|
|
105
|
+
expect(jenkins.send(:success?)).to eq true
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
it "detects a failed build" do
|
|
109
|
+
allow(jenkins).to receive(:`).with("curl -s http://micah:fake-token@ci.botandrose.com/job/test-project/42/api/json?tree=building,result").and_return('{"building":false,"result":"FAILURE"}')
|
|
110
|
+
|
|
111
|
+
expect(jenkins.send(:building?)).to eq false
|
|
112
|
+
expect(jenkins.send(:success?)).to eq false
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
it "detects a build in progress" do
|
|
116
|
+
allow(jenkins).to receive(:`).with("curl -s http://micah:fake-token@ci.botandrose.com/job/test-project/42/api/json?tree=building,result").and_return('{"building":true,"result":null}')
|
|
117
|
+
|
|
118
|
+
expect(jenkins.send(:building?)).to eq true
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
it "handles JSON with spaces in keys" do
|
|
122
|
+
allow(jenkins).to receive(:`).with("curl -s http://micah:fake-token@ci.botandrose.com/job/test-project/42/api/json?tree=building,result").and_return('{"_class":"hudson.model.FreeStyleBuild","building":false,"result":"SUCCESS"}')
|
|
123
|
+
|
|
124
|
+
expect(jenkins.send(:building?)).to eq false
|
|
125
|
+
expect(jenkins.send(:success?)).to eq true
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
it "success? reflects the last response from building?" do
|
|
129
|
+
allow(jenkins).to receive(:`).with("curl -s http://micah:fake-token@ci.botandrose.com/job/test-project/42/api/json?tree=building,result").and_return(
|
|
130
|
+
'{"building":true,"result":null}',
|
|
131
|
+
'{"building":false,"result":"SUCCESS"}'
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
jenkins.send(:building?) # first call — still building
|
|
135
|
+
jenkins.send(:building?) # second call — done
|
|
136
|
+
expect(jenkins.send(:success?)).to eq true
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
require "bard/ci/runner"
|
|
2
|
+
|
|
3
|
+
RSpec.describe Bard::CI::Runner do
|
|
4
|
+
describe ".runners" do
|
|
5
|
+
it "is a hash registry" do
|
|
6
|
+
expect(described_class.runners).to be_a(Hash)
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
describe ".[]" do
|
|
11
|
+
before do
|
|
12
|
+
require "bard/ci/local"
|
|
13
|
+
require "bard/ci/github_actions"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "looks up runners by name" do
|
|
17
|
+
expect(described_class[:local]).to eq Bard::CI::Local
|
|
18
|
+
expect(described_class[:github_actions]).to eq Bard::CI::GithubActions
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "returns nil for unknown runners" do
|
|
22
|
+
expect(described_class[:nonexistent]).to be_nil
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
describe ".default" do
|
|
27
|
+
it "returns the last registered runner" do
|
|
28
|
+
# Whatever was registered last in the current test run
|
|
29
|
+
expect(described_class.default).to be_a(Class)
|
|
30
|
+
expect(described_class.default.ancestors).to include(Bard::CI::Runner)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
describe "auto-registration via inherited" do
|
|
35
|
+
it "registers subclasses automatically" do
|
|
36
|
+
eval <<-RUBY
|
|
37
|
+
module Bard
|
|
38
|
+
class CI
|
|
39
|
+
class SpecTestRunner < Runner
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
RUBY
|
|
44
|
+
|
|
45
|
+
expect(described_class[:spec_test_runner]).to eq Bard::CI::SpecTestRunner
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "newly registered runner becomes the default" do
|
|
49
|
+
eval <<-RUBY
|
|
50
|
+
module Bard
|
|
51
|
+
class CI
|
|
52
|
+
class AnotherTestRunner < Runner
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
RUBY
|
|
57
|
+
|
|
58
|
+
expect(described_class.default).to eq Bard::CI::AnotherTestRunner
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
data/spec/bard/cli/ci_spec.rb
CHANGED
|
@@ -2,6 +2,7 @@ require "spec_helper"
|
|
|
2
2
|
require "bard/cli"
|
|
3
3
|
require "bard/cli/ci"
|
|
4
4
|
require "thor"
|
|
5
|
+
require "ostruct"
|
|
5
6
|
|
|
6
7
|
class TestCICLI < Thor
|
|
7
8
|
include Bard::CLI::CI
|
|
@@ -16,6 +17,10 @@ class TestCICLI < Thor
|
|
|
16
17
|
def project_name
|
|
17
18
|
"test_project"
|
|
18
19
|
end
|
|
20
|
+
|
|
21
|
+
def config
|
|
22
|
+
@config ||= OpenStruct.new(ci: nil)
|
|
23
|
+
end
|
|
19
24
|
end
|
|
20
25
|
|
|
21
26
|
describe Bard::CLI::CI do
|
|
@@ -101,12 +106,9 @@ describe Bard::CLI::CI do
|
|
|
101
106
|
it "shows error message and exits" do
|
|
102
107
|
allow(cli).to receive(:options).and_return({})
|
|
103
108
|
allow(ci_runner).to receive(:exists?).and_return(false)
|
|
109
|
+
allow(cli).to receive(:exit).with(1).and_raise(SystemExit)
|
|
104
110
|
|
|
105
|
-
expect
|
|
106
|
-
expect(cli).to receive(:puts) # "Re-run with --skip-ci to bypass CI..."
|
|
107
|
-
expect(cli).to receive(:exit).with(1)
|
|
108
|
-
|
|
109
|
-
cli.ci
|
|
111
|
+
expect { cli.ci }.to raise_error(SystemExit)
|
|
110
112
|
end
|
|
111
113
|
end
|
|
112
114
|
|
|
@@ -116,7 +118,7 @@ describe Bard::CLI::CI do
|
|
|
116
118
|
allow(ci_runner).to receive(:exists?).and_return(true)
|
|
117
119
|
allow(ci_runner).to receive(:run).and_return(true)
|
|
118
120
|
|
|
119
|
-
expect(Bard::CI).to receive(:new).with("test_project", "develop",
|
|
121
|
+
expect(Bard::CI).to receive(:new).with("test_project", "develop", runner_name: nil)
|
|
120
122
|
expect(cli).to receive(:puts).with("Continuous integration: starting build on develop...")
|
|
121
123
|
|
|
122
124
|
cli.ci("develop")
|
|
@@ -124,12 +126,36 @@ describe Bard::CLI::CI do
|
|
|
124
126
|
end
|
|
125
127
|
|
|
126
128
|
context "with local-ci option" do
|
|
127
|
-
it "passes local
|
|
129
|
+
it "passes local runner_name to CI" do
|
|
128
130
|
allow(cli).to receive(:options).and_return({ "local-ci" => true })
|
|
129
131
|
allow(ci_runner).to receive(:exists?).and_return(true)
|
|
130
132
|
allow(ci_runner).to receive(:run).and_return(true)
|
|
131
133
|
|
|
132
|
-
expect(Bard::CI).to receive(:new).with("test_project", "feature-branch",
|
|
134
|
+
expect(Bard::CI).to receive(:new).with("test_project", "feature-branch", runner_name: :local)
|
|
135
|
+
|
|
136
|
+
cli.ci
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
context "with ci option" do
|
|
141
|
+
it "passes specified runner_name to CI" do
|
|
142
|
+
allow(cli).to receive(:options).and_return({ "ci" => "jenkins" })
|
|
143
|
+
allow(ci_runner).to receive(:exists?).and_return(true)
|
|
144
|
+
allow(ci_runner).to receive(:run).and_return(true)
|
|
145
|
+
|
|
146
|
+
expect(Bard::CI).to receive(:new).with("test_project", "feature-branch", runner_name: :jenkins)
|
|
147
|
+
|
|
148
|
+
cli.ci
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
context "with both local-ci and ci options" do
|
|
153
|
+
it "local-ci takes precedence" do
|
|
154
|
+
allow(cli).to receive(:options).and_return({ "local-ci" => true, "ci" => "jenkins" })
|
|
155
|
+
allow(ci_runner).to receive(:exists?).and_return(true)
|
|
156
|
+
allow(ci_runner).to receive(:run).and_return(true)
|
|
157
|
+
|
|
158
|
+
expect(Bard::CI).to receive(:new).with("test_project", "feature-branch", runner_name: :local)
|
|
133
159
|
|
|
134
160
|
cli.ci
|
|
135
161
|
end
|
|
@@ -26,6 +26,7 @@ describe Bard::CLI::Deploy do
|
|
|
26
26
|
|
|
27
27
|
before do
|
|
28
28
|
allow(cli).to receive(:config).and_return(config)
|
|
29
|
+
allow(cli).to receive(:options).and_return({ target: "production" })
|
|
29
30
|
allow(cli).to receive(:puts)
|
|
30
31
|
allow(cli).to receive(:exit)
|
|
31
32
|
allow(cli).to receive(:run!)
|
|
@@ -48,7 +49,7 @@ describe Bard::CLI::Deploy do
|
|
|
48
49
|
context "when on master branch" do
|
|
49
50
|
before do
|
|
50
51
|
allow(Bard::Git).to receive(:current_branch).and_return("master")
|
|
51
|
-
allow(cli).to receive(:options).and_return({})
|
|
52
|
+
allow(cli).to receive(:options).and_return({ target: "production" })
|
|
52
53
|
end
|
|
53
54
|
|
|
54
55
|
context "when up to date with remote" do
|
|
@@ -79,7 +80,7 @@ describe Bard::CLI::Deploy do
|
|
|
79
80
|
|
|
80
81
|
context "with skip-ci option" do
|
|
81
82
|
it "skips CI step" do
|
|
82
|
-
allow(cli).to receive(:options).and_return({ "skip-ci" => true })
|
|
83
|
+
allow(cli).to receive(:options).and_return({ "skip-ci" => true, target: "production" })
|
|
83
84
|
|
|
84
85
|
expect(cli).not_to receive(:invoke).with(:ci, anything, anything)
|
|
85
86
|
expect(production_server).to receive(:run!).with("git pull origin master && bin/setup")
|
|
@@ -91,16 +92,16 @@ describe Bard::CLI::Deploy do
|
|
|
91
92
|
|
|
92
93
|
context "when on feature branch" do
|
|
93
94
|
before do
|
|
94
|
-
allow(cli).to receive(:options).and_return({})
|
|
95
|
+
allow(cli).to receive(:options).and_return({ target: "production" })
|
|
95
96
|
end
|
|
96
97
|
|
|
97
98
|
context "with fast-forward merge possible" do
|
|
98
99
|
it "fetches master, pushes branch, runs CI, merges to master, and deploys" do
|
|
99
|
-
expect(cli).to receive(:run!).with("git fetch origin
|
|
100
|
+
expect(cli).to receive(:run!).with("git fetch origin")
|
|
101
|
+
expect(cli).to receive(:run!).with("git fetch origin master:master").twice
|
|
100
102
|
expect(cli).to receive(:run!).with("git push -f origin feature-branch:feature-branch")
|
|
101
103
|
expect(cli).to receive(:invoke).with(:ci, ["feature-branch"], {})
|
|
102
104
|
expect(cli).to receive(:run!).with("git push origin feature-branch:master")
|
|
103
|
-
expect(cli).to receive(:run!).with("git fetch origin master:master")
|
|
104
105
|
expect(production_server).to receive(:run!).with("git pull origin master && bin/setup")
|
|
105
106
|
|
|
106
107
|
cli.deploy
|
|
@@ -141,7 +142,7 @@ describe Bard::CLI::Deploy do
|
|
|
141
142
|
|
|
142
143
|
context "with clone option" do
|
|
143
144
|
it "clones repository and sets up application" do
|
|
144
|
-
allow(cli).to receive(:options).and_return({ clone: true })
|
|
145
|
+
allow(cli).to receive(:options).and_return({ clone: true, target: "production" })
|
|
145
146
|
|
|
146
147
|
expect(production_server).to receive(:run!).with("git clone git@github.com:botandrosedesign/test_project /var/www/test_project", home: true)
|
|
147
148
|
expect(cli).to receive(:invoke).with(:master_key, [], from: "local", to: :production)
|
|
@@ -168,13 +169,14 @@ describe Bard::CLI::Deploy do
|
|
|
168
169
|
|
|
169
170
|
before do
|
|
170
171
|
allow(config).to receive(:[]).with(:staging).and_return(staging_server)
|
|
172
|
+
allow(cli).to receive(:options).and_return({ target: "staging" })
|
|
171
173
|
end
|
|
172
174
|
|
|
173
175
|
it "deploys to specified target" do
|
|
174
176
|
expect(staging_server).to receive(:run!).with("git pull origin master && bin/setup")
|
|
175
177
|
expect(cli).to receive(:ping).with(:staging)
|
|
176
178
|
|
|
177
|
-
cli.deploy
|
|
179
|
+
cli.deploy
|
|
178
180
|
end
|
|
179
181
|
end
|
|
180
182
|
|
|
@@ -191,12 +193,22 @@ describe Bard::CLI::Deploy do
|
|
|
191
193
|
|
|
192
194
|
context "with local-ci option" do
|
|
193
195
|
it "passes local-ci option to CI invocation" do
|
|
194
|
-
allow(cli).to receive(:options).and_return({ "local-ci" => true })
|
|
196
|
+
allow(cli).to receive(:options).and_return({ "local-ci" => true, target: "production" })
|
|
195
197
|
|
|
196
198
|
expect(cli).to receive(:invoke).with(:ci, ["feature-branch"], { "local-ci" => true })
|
|
197
199
|
|
|
198
200
|
cli.deploy
|
|
199
201
|
end
|
|
200
202
|
end
|
|
203
|
+
|
|
204
|
+
context "with ci option" do
|
|
205
|
+
it "passes ci option to CI invocation" do
|
|
206
|
+
allow(cli).to receive(:options).and_return({ "ci" => "jenkins", target: "production" })
|
|
207
|
+
|
|
208
|
+
expect(cli).to receive(:invoke).with(:ci, ["feature-branch"], { "ci" => "jenkins" })
|
|
209
|
+
|
|
210
|
+
cli.deploy
|
|
211
|
+
end
|
|
212
|
+
end
|
|
201
213
|
end
|
|
202
214
|
end
|
data/spec/bard/cli/hurt_spec.rb
CHANGED
|
@@ -4,7 +4,7 @@ require "bard/cli/hurt"
|
|
|
4
4
|
require "thor"
|
|
5
5
|
|
|
6
6
|
class TestHurtCLI < Thor
|
|
7
|
-
|
|
7
|
+
Bard::CLI::Hurt.setup(self)
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
describe Bard::CLI::Hurt do
|
|
@@ -20,4 +20,4 @@ describe Bard::CLI::Hurt do
|
|
|
20
20
|
expect(cli).to respond_to(:hurt)
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
|
-
end
|
|
23
|
+
end
|
|
@@ -4,7 +4,7 @@ require "bard/cli/install"
|
|
|
4
4
|
require "thor"
|
|
5
5
|
|
|
6
6
|
class TestInstallCLI < Thor
|
|
7
|
-
|
|
7
|
+
Bard::CLI::Install.setup(self)
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
describe Bard::CLI::Install do
|
|
@@ -16,10 +16,10 @@ describe Bard::CLI::Install do
|
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
it "should copy install files to bin directory" do
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
expect_any_instance_of(Bard::CLI::Install).to receive(:system).with(/cp -R .*install_files\/\* bin\//)
|
|
20
|
+
expect_any_instance_of(Bard::CLI::Install).to receive(:system).with(/cp -R .*install_files\/\.github \.\//)
|
|
21
21
|
|
|
22
22
|
cli.install
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
|
-
end
|
|
25
|
+
end
|
data/spec/bard/cli/open_spec.rb
CHANGED
|
@@ -4,7 +4,7 @@ require "bard/cli/open"
|
|
|
4
4
|
require "thor"
|
|
5
5
|
|
|
6
6
|
class TestOpenCLI < Thor
|
|
7
|
-
|
|
7
|
+
Bard::CLI::Open.setup(self)
|
|
8
8
|
|
|
9
9
|
attr_reader :config
|
|
10
10
|
|
|
@@ -25,7 +25,7 @@ describe Bard::CLI::Open do
|
|
|
25
25
|
|
|
26
26
|
before do
|
|
27
27
|
allow(cli).to receive(:config).and_return(config)
|
|
28
|
-
|
|
28
|
+
allow_any_instance_of(Bard::CLI::Open).to receive(:exec)
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
describe "#open" do
|
|
@@ -34,7 +34,7 @@ describe Bard::CLI::Open do
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
it "should open production server URL by default" do
|
|
37
|
-
|
|
37
|
+
expect_any_instance_of(Bard::CLI::Open).to receive(:exec).with("xdg-open https://example.com")
|
|
38
38
|
|
|
39
39
|
cli.open
|
|
40
40
|
end
|
|
@@ -43,13 +43,13 @@ describe Bard::CLI::Open do
|
|
|
43
43
|
staging_server = double("staging", ping: ["https://staging.example.com"])
|
|
44
44
|
allow(config).to receive(:[]).with(:staging).and_return(staging_server)
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
expect_any_instance_of(Bard::CLI::Open).to receive(:exec).with("xdg-open https://staging.example.com")
|
|
47
47
|
|
|
48
48
|
cli.open(:staging)
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
it "should open CI URL when server is ci" do
|
|
52
|
-
|
|
52
|
+
expect_any_instance_of(Bard::CLI::Open).to receive(:exec).with("xdg-open https://github.com/botandrosedesign/test_project/actions/workflows/ci.yml")
|
|
53
53
|
|
|
54
54
|
cli.open(:ci)
|
|
55
55
|
end
|
|
@@ -57,11 +57,13 @@ describe Bard::CLI::Open do
|
|
|
57
57
|
|
|
58
58
|
describe "#open_url" do
|
|
59
59
|
it "returns CI URL for ci server" do
|
|
60
|
-
|
|
60
|
+
command = Bard::CLI::Open.new(cli)
|
|
61
|
+
expect(command.send(:open_url, :ci)).to eq("https://github.com/botandrosedesign/test_project/actions/workflows/ci.yml")
|
|
61
62
|
end
|
|
62
63
|
|
|
63
64
|
it "returns server ping URL for other servers" do
|
|
64
|
-
|
|
65
|
+
command = Bard::CLI::Open.new(cli)
|
|
66
|
+
expect(command.send(:open_url, :production)).to eq("https://example.com")
|
|
65
67
|
end
|
|
66
68
|
end
|
|
67
|
-
end
|
|
69
|
+
end
|
data/spec/bard/cli/ping_spec.rb
CHANGED
|
@@ -4,7 +4,7 @@ require "bard/cli/ping"
|
|
|
4
4
|
require "thor"
|
|
5
5
|
|
|
6
6
|
class TestPingCLI < Thor
|
|
7
|
-
|
|
7
|
+
Bard::CLI::Ping.setup(self)
|
|
8
8
|
|
|
9
9
|
attr_reader :config
|
|
10
10
|
|
|
@@ -21,8 +21,8 @@ describe Bard::CLI::Ping do
|
|
|
21
21
|
|
|
22
22
|
before do
|
|
23
23
|
allow(cli).to receive(:config).and_return(config)
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
allow_any_instance_of(Bard::CLI::Ping).to receive(:puts)
|
|
25
|
+
allow_any_instance_of(Bard::CLI::Ping).to receive(:exit)
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
describe "#ping" do
|
|
@@ -39,9 +39,9 @@ describe Bard::CLI::Ping do
|
|
|
39
39
|
it "should print down URLs when they exist" do
|
|
40
40
|
down_urls = ["https://down.example.com"]
|
|
41
41
|
allow(Bard::Ping).to receive(:call).and_return(down_urls)
|
|
42
|
-
|
|
42
|
+
expect_any_instance_of(Bard::CLI::Ping).to receive(:puts).with("https://down.example.com is down!")
|
|
43
43
|
|
|
44
44
|
cli.ping
|
|
45
45
|
end
|
|
46
46
|
end
|
|
47
|
-
end
|
|
47
|
+
end
|
data/spec/bard/cli/run_spec.rb
CHANGED
|
@@ -25,6 +25,7 @@ describe Bard::CLI::Run do
|
|
|
25
25
|
allow(cli).to receive(:exit)
|
|
26
26
|
allow(cli).to receive(:red).and_return("")
|
|
27
27
|
allow(cli).to receive(:yellow).and_return("")
|
|
28
|
+
allow(cli).to receive(:options).and_return({ target: "production" })
|
|
28
29
|
end
|
|
29
30
|
|
|
30
31
|
describe "#run" do
|
|
@@ -33,7 +34,25 @@ describe Bard::CLI::Run do
|
|
|
33
34
|
end
|
|
34
35
|
|
|
35
36
|
it "should run command on production server" do
|
|
36
|
-
expect(server).to receive(:run!).with("ls -la", verbose: true)
|
|
37
|
+
expect(server).to receive(:run!).with("ls -la", verbose: true, home: nil)
|
|
38
|
+
|
|
39
|
+
cli.run("ls", "-la")
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "should run command on specified target" do
|
|
43
|
+
staging_server = double("staging_server")
|
|
44
|
+
allow(cli).to receive(:config).and_return(config.merge(staging: staging_server))
|
|
45
|
+
allow(cli).to receive(:options).and_return({ target: "staging" })
|
|
46
|
+
|
|
47
|
+
expect(staging_server).to receive(:run!).with("ls -la", verbose: true, home: nil)
|
|
48
|
+
|
|
49
|
+
cli.run("ls", "-la")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "should pass home option to target" do
|
|
53
|
+
allow(cli).to receive(:options).and_return({ target: "production", home: true })
|
|
54
|
+
|
|
55
|
+
expect(server).to receive(:run!).with("ls -la", verbose: true, home: true)
|
|
37
56
|
|
|
38
57
|
cli.run("ls", "-la")
|
|
39
58
|
end
|
data/spec/bard/cli/stage_spec.rb
CHANGED
|
@@ -55,6 +55,26 @@ describe Bard::CLI::Stage do
|
|
|
55
55
|
end
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
+
context "when staging target has a deploy strategy" do
|
|
59
|
+
let(:strategy_instance) { double("strategy") }
|
|
60
|
+
let(:staging_server) { double("staging", deploy_strategy: :fake) }
|
|
61
|
+
|
|
62
|
+
before do
|
|
63
|
+
allow(staging_server).to receive(:respond_to?).with(:deploy_strategy).and_return(true)
|
|
64
|
+
allow(staging_server).to receive(:deploy_strategy_instance).and_return(strategy_instance)
|
|
65
|
+
allow(cli).to receive(:require).with("bard/deploy_strategy/fake").and_return(true)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it "uses the deploy strategy instead of SSH" do
|
|
69
|
+
expect(cli).to receive(:run!).with("git push -u origin main", verbose: true)
|
|
70
|
+
expect(strategy_instance).to receive(:deploy)
|
|
71
|
+
expect(staging_server).not_to receive(:run!)
|
|
72
|
+
expect(cli).to receive(:ping).with(:staging)
|
|
73
|
+
|
|
74
|
+
cli.stage
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
58
78
|
context "when production server is not defined" do
|
|
59
79
|
let(:servers) { { staging: staging_server } }
|
|
60
80
|
|
data/spec/bard/cli/vim_spec.rb
CHANGED
|
@@ -4,14 +4,14 @@ require "bard/cli/vim"
|
|
|
4
4
|
require "thor"
|
|
5
5
|
|
|
6
6
|
class TestVimCLI < Thor
|
|
7
|
-
|
|
7
|
+
Bard::CLI::Vim.setup(self)
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
describe Bard::CLI::Vim do
|
|
11
11
|
let(:cli) { TestVimCLI.new }
|
|
12
12
|
|
|
13
13
|
before do
|
|
14
|
-
|
|
14
|
+
allow_any_instance_of(Bard::CLI::Vim).to receive(:exec)
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
describe "#vim" do
|
|
@@ -20,15 +20,15 @@ describe Bard::CLI::Vim do
|
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
it "should exec vim with git diff files by default" do
|
|
23
|
-
|
|
23
|
+
expect_any_instance_of(Bard::CLI::Vim).to receive(:exec).with("vim -p `(git diff master --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`")
|
|
24
24
|
|
|
25
25
|
cli.vim
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
it "should exec vim with specified branch" do
|
|
29
|
-
|
|
29
|
+
expect_any_instance_of(Bard::CLI::Vim).to receive(:exec).with("vim -p `(git diff develop --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`")
|
|
30
30
|
|
|
31
31
|
cli.vim("develop")
|
|
32
32
|
end
|
|
33
33
|
end
|
|
34
|
-
end
|
|
34
|
+
end
|
data/spec/bard/config_spec.rb
CHANGED
|
@@ -148,6 +148,18 @@ describe Bard::Config do
|
|
|
148
148
|
end
|
|
149
149
|
end
|
|
150
150
|
|
|
151
|
+
context "with target overriding default server" do
|
|
152
|
+
subject { described_class.new("tracker", source: <<~SOURCE) }
|
|
153
|
+
target :staging do
|
|
154
|
+
ssh false
|
|
155
|
+
end
|
|
156
|
+
SOURCE
|
|
157
|
+
|
|
158
|
+
it "replaces the default Server with a Target" do
|
|
159
|
+
expect(subject[:staging]).to be_a(Bard::Target)
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
151
163
|
context "with github_pages directive" do
|
|
152
164
|
subject { described_class.new("test", source: "github_pages 'example.com'") }
|
|
153
165
|
|
data/spec/bard/github_spec.rb
CHANGED
|
@@ -5,7 +5,7 @@ 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
|