bard 2.2.0 → 3.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/README.md +7 -487
- data/Rakefile +1 -4
- data/bard.gemspec +2 -11
- data/lib/bard/config.rb +22 -20
- data/lib/bard/plugins/ping/target_methods.rb +0 -9
- data/lib/bard/plugins/ssh/connection.rb +2 -35
- data/lib/bard/target.rb +0 -15
- data/lib/bard/version.rb +1 -1
- data/lib/bard.rb +2 -1
- data/spec/bard/capability_spec.rb +0 -8
- data/spec/bard/config_spec.rb +30 -67
- data/spec/bard/target_spec.rb +1 -36
- metadata +4 -225
- data/ARCHITECTURE.md +0 -957
- data/CUSTOM_STRATEGIES.md +0 -701
- data/MIGRATION_GUIDE.md +0 -513
- data/PLUGINS.md +0 -99
- data/bin/bard +0 -4
- data/features/ci.feature +0 -63
- data/features/data.feature +0 -13
- data/features/deploy.feature +0 -14
- data/features/deploy_git_workflow.feature +0 -89
- data/features/run.feature +0 -14
- data/features/step_definitions/bard_steps.rb +0 -136
- data/features/support/bard-coverage +0 -16
- data/features/support/env.rb +0 -22
- data/features/support/test_server.rb +0 -216
- data/lib/bard/cli.rb +0 -44
- data/lib/bard/command.rb +0 -31
- data/lib/bard/copy.rb +0 -45
- data/lib/bard/plugins/data.rb +0 -56
- data/lib/bard/plugins/deploy/ci/github_actions.rb +0 -202
- data/lib/bard/plugins/deploy/ci/jenkins.rb +0 -176
- data/lib/bard/plugins/deploy/ci/local.rb +0 -57
- data/lib/bard/plugins/deploy/ci/runner.rb +0 -112
- data/lib/bard/plugins/deploy/ci/state.rb +0 -40
- data/lib/bard/plugins/deploy/ci.rb +0 -38
- data/lib/bard/plugins/deploy/ssh_strategy.rb +0 -26
- data/lib/bard/plugins/deploy/strategy.rb +0 -60
- data/lib/bard/plugins/deploy.rb +0 -260
- data/lib/bard/plugins/git.rb +0 -45
- data/lib/bard/plugins/github.rb +0 -152
- data/lib/bard/plugins/github_pages/strategy.rb +0 -170
- data/lib/bard/plugins/github_pages.rb +0 -35
- data/lib/bard/plugins/hurt.rb +0 -13
- data/lib/bard/plugins/install/.github/dependabot.yml +0 -18
- data/lib/bard/plugins/install/.github/workflows/cache-ci.yml +0 -15
- data/lib/bard/plugins/install/.github/workflows/ci.yml +0 -74
- data/lib/bard/plugins/install/apt_dependencies.rb +0 -33
- data/lib/bard/plugins/install/ci +0 -5
- data/lib/bard/plugins/install/setup +0 -13
- data/lib/bard/plugins/install/specified_bundler.rb +0 -15
- data/lib/bard/plugins/install/specified_ruby.rb +0 -59
- data/lib/bard/plugins/install.rb +0 -9
- data/lib/bard/plugins/open.rb +0 -20
- data/lib/bard/plugins/ping/check.rb +0 -66
- data/lib/bard/plugins/ping.rb +0 -10
- data/lib/bard/plugins/run.rb +0 -19
- data/lib/bard/plugins/setup.rb +0 -75
- data/lib/bard/plugins/ssh/copy.rb +0 -95
- data/lib/bard/plugins/ssh.rb +0 -10
- data/lib/bard/plugins/vim.rb +0 -6
- data/lib/bard/retryable.rb +0 -25
- data/lib/bard/secrets.rb +0 -10
- data/spec/acceptance/.gitignore +0 -4
- data/spec/acceptance/docker/Dockerfile +0 -36
- data/spec/acceptance/docker/test_key +0 -27
- data/spec/acceptance/docker/test_key.pub +0 -1
- data/spec/bard/ci/github_actions_spec.rb +0 -138
- data/spec/bard/ci/jenkins_spec.rb +0 -139
- data/spec/bard/ci/runner_spec.rb +0 -61
- data/spec/bard/ci_spec.rb +0 -10
- data/spec/bard/cli/ci_spec.rb +0 -170
- data/spec/bard/cli/data_spec.rb +0 -58
- data/spec/bard/cli/deploy_spec.rb +0 -243
- data/spec/bard/cli/hurt_spec.rb +0 -17
- data/spec/bard/cli/install_spec.rb +0 -19
- data/spec/bard/cli/master_key_spec.rb +0 -38
- data/spec/bard/cli/open_spec.rb +0 -51
- data/spec/bard/cli/ping_spec.rb +0 -32
- data/spec/bard/cli/run_spec.rb +0 -57
- data/spec/bard/cli/setup_spec.rb +0 -99
- data/spec/bard/cli/ssh_spec.rb +0 -40
- data/spec/bard/cli/stage_spec.rb +0 -127
- data/spec/bard/cli/vim_spec.rb +0 -28
- data/spec/bard/command_spec.rb +0 -30
- data/spec/bard/copy_spec.rb +0 -69
- data/spec/bard/deploy_strategy/github_pages_spec.rb +0 -58
- data/spec/bard/deploy_strategy/ssh_spec.rb +0 -135
- data/spec/bard/deploy_strategy_spec.rb +0 -107
- data/spec/bard/dynamic_dsl_spec.rb +0 -46
- data/spec/bard/git_spec.rb +0 -101
- data/spec/bard/github_spec.rb +0 -45
- data/spec/bard/ping_spec.rb +0 -60
- data/spec/bard/ssh_copy_spec.rb +0 -44
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
FROM ubuntu:24.04
|
|
2
|
-
|
|
3
|
-
# Prevent interactive prompts
|
|
4
|
-
ENV DEBIAN_FRONTEND=noninteractive
|
|
5
|
-
|
|
6
|
-
# Install SSH server, git, and basic tools
|
|
7
|
-
RUN apt-get update && \
|
|
8
|
-
apt-get install -y \
|
|
9
|
-
openssh-server \
|
|
10
|
-
git \
|
|
11
|
-
sudo \
|
|
12
|
-
&& rm -rf /var/lib/apt/lists/*
|
|
13
|
-
|
|
14
|
-
# Create deploy user
|
|
15
|
-
RUN useradd -m -s /bin/bash deploy && \
|
|
16
|
-
echo 'deploy:password' | chpasswd
|
|
17
|
-
|
|
18
|
-
# Setup SSH
|
|
19
|
-
RUN mkdir /var/run/sshd && \
|
|
20
|
-
mkdir -p /home/deploy/.ssh && \
|
|
21
|
-
chmod 700 /home/deploy/.ssh
|
|
22
|
-
|
|
23
|
-
# Copy SSH authorized key
|
|
24
|
-
COPY test_key.pub /home/deploy/.ssh/authorized_keys
|
|
25
|
-
RUN chown -R deploy:deploy /home/deploy/.ssh && \
|
|
26
|
-
chmod 600 /home/deploy/.ssh/authorized_keys
|
|
27
|
-
|
|
28
|
-
# Allow SSH login with keys
|
|
29
|
-
RUN sed -i 's/#PubkeyAuthentication yes/PubkeyAuthentication yes/' /etc/ssh/sshd_config && \
|
|
30
|
-
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
|
|
31
|
-
|
|
32
|
-
# Expose SSH port
|
|
33
|
-
EXPOSE 22
|
|
34
|
-
|
|
35
|
-
# Start SSH service
|
|
36
|
-
CMD ["/usr/sbin/sshd", "-D"]
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
-----BEGIN OPENSSH PRIVATE KEY-----
|
|
2
|
-
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
-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
-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
-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
-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
-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
-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
-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
-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
-
Gn+cO/HgQznniVoFQHIabx+BIo6VgYR1YTQoAVWwp+RFbwCJHZG1DkLWEhP/MKYejTVZO0
|
|
12
|
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-
fZg7WyYOQwRYF0b0s8JlpGLkhLDM6nhBzT1M+nrN0axTAvYkBZV5MJPNg31F/iHxEtt+/V
|
|
23
|
-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
-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
-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
-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
|
|
27
|
-
-----END OPENSSH PRIVATE KEY-----
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC6+euShWHoAhIGGpQ62a9N/pMyir8OjqL9rKfVqqz1iZs3nu01WdhcQoFDXkcvluq5jQDgVx/mY7vcvKscZ1Q4OslLq9mjgnzByjITIrE4LZRhXmywJSe2rXd14ThZUd9LKQwSj3PdTtvEVLC3BTwQJhAcotvg4GZBviGn+cO/HgQznniVoFQHIabx+BIo6VgYR1YTQoAVWwp+RFbwCJHZG1DkLWEhP/MKYejTVZO0cym7JmThzGkGVLfHPrCwPrV/pKAyEzRfnvmN9X8zGFsN3cUaTEiTyQzbgO0t8tb2J1hYMPbl19C5crd5BkMne9ygpr7Zbvdd8d18GsAau+Np bard-test-key
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
require "spec_helper"
|
|
2
|
-
require "bard/plugins/deploy/ci/github_actions"
|
|
3
|
-
|
|
4
|
-
BASE_URL = "https://api.github.com/repos/botandrosedesign/metrc"
|
|
5
|
-
|
|
6
|
-
RSpec.shared_context "github actions stubs" do
|
|
7
|
-
let(:run_id) { 123 }
|
|
8
|
-
let(:job_id) { 456 }
|
|
9
|
-
let(:started_at) { "2024-01-15T10:00:00Z" }
|
|
10
|
-
let(:completed_at) { "2024-01-15T10:01:30Z" }
|
|
11
|
-
let(:sha) { "abc123" }
|
|
12
|
-
|
|
13
|
-
let(:run_json) do
|
|
14
|
-
{
|
|
15
|
-
"id" => run_id,
|
|
16
|
-
"status" => "completed",
|
|
17
|
-
"conclusion" => "success",
|
|
18
|
-
"head_branch" => "master",
|
|
19
|
-
"head_sha" => sha,
|
|
20
|
-
"run_started_at" => started_at,
|
|
21
|
-
"updated_at" => completed_at,
|
|
22
|
-
}
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
let(:job_json) do
|
|
26
|
-
{
|
|
27
|
-
"id" => job_id,
|
|
28
|
-
"started_at" => started_at,
|
|
29
|
-
"completed_at" => completed_at,
|
|
30
|
-
}
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
before do
|
|
34
|
-
allow(Bard::Secrets).to receive(:fetch).with("github-apikey").and_return("test-key")
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
describe Bard::CI::GithubActions::API do
|
|
39
|
-
include_context "github actions stubs"
|
|
40
|
-
|
|
41
|
-
subject { described_class.new("metrc") }
|
|
42
|
-
|
|
43
|
-
describe "#last_successful_run" do
|
|
44
|
-
before do
|
|
45
|
-
stub_request(:get, "#{BASE_URL}/actions/runs")
|
|
46
|
-
.with(query: hash_including("status" => "success"))
|
|
47
|
-
.to_return(
|
|
48
|
-
headers: { "Content-Type" => "application/json" },
|
|
49
|
-
body: JSON.dump("workflow_runs" => [run_json]),
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
stub_request(:get, "#{BASE_URL}/actions/runs/#{run_id}/jobs")
|
|
53
|
-
.with(query: hash_including("filter" => "latest"))
|
|
54
|
-
.to_return(
|
|
55
|
-
headers: { "Content-Type" => "application/json" },
|
|
56
|
-
body: JSON.dump("jobs" => [job_json]),
|
|
57
|
-
)
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
it "has #time_elapsed" do
|
|
61
|
-
run = subject.last_successful_run
|
|
62
|
-
expect(run.time_elapsed).to eq 90
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
it "has #console" do
|
|
66
|
-
stub_request(:get, "#{BASE_URL}/actions/jobs/#{job_id}/logs")
|
|
67
|
-
.to_return(
|
|
68
|
-
headers: { "Content-Type" => "text/plain" },
|
|
69
|
-
body: "build log output here",
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
expect(subject.last_successful_run.console).to eq "build log output here"
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
describe "#create_run!" do
|
|
77
|
-
it "returns a run" do
|
|
78
|
-
stub_request(:post, "#{BASE_URL}/actions/workflows/ci.yml/dispatches")
|
|
79
|
-
.to_return(status: 204, body: "")
|
|
80
|
-
|
|
81
|
-
allow(subject).to receive(:`).with("git rev-parse master").and_return("#{sha}\n")
|
|
82
|
-
|
|
83
|
-
stub_request(:get, "#{BASE_URL}/actions/runs")
|
|
84
|
-
.with(query: hash_including("head_sha" => sha))
|
|
85
|
-
.to_return(
|
|
86
|
-
headers: { "Content-Type" => "application/json" },
|
|
87
|
-
body: JSON.dump("workflow_runs" => [run_json]),
|
|
88
|
-
)
|
|
89
|
-
|
|
90
|
-
run = subject.create_run!("master")
|
|
91
|
-
expect(run).to be_a Bard::CI::GithubActions::Run
|
|
92
|
-
expect(run.id).to eq run_id
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
describe Bard::CI::GithubActions do
|
|
98
|
-
include_context "github actions stubs"
|
|
99
|
-
|
|
100
|
-
subject { described_class.new("metrc", "master", sha) }
|
|
101
|
-
|
|
102
|
-
it "returns true on successful run" do
|
|
103
|
-
stub_request(:post, "#{BASE_URL}/actions/workflows/ci.yml/dispatches")
|
|
104
|
-
.to_return(status: 204, body: "")
|
|
105
|
-
|
|
106
|
-
allow_any_instance_of(Bard::CI::GithubActions::API)
|
|
107
|
-
.to receive(:`).with("git rev-parse master").and_return("#{sha}\n")
|
|
108
|
-
|
|
109
|
-
stub_request(:get, "#{BASE_URL}/actions/runs")
|
|
110
|
-
.with(query: hash_including("head_sha" => sha))
|
|
111
|
-
.to_return(
|
|
112
|
-
headers: { "Content-Type" => "application/json" },
|
|
113
|
-
body: JSON.dump("workflow_runs" => [run_json]),
|
|
114
|
-
)
|
|
115
|
-
|
|
116
|
-
stub_request(:get, "#{BASE_URL}/actions/runs")
|
|
117
|
-
.with(query: hash_including("status" => "success"))
|
|
118
|
-
.to_return(
|
|
119
|
-
headers: { "Content-Type" => "application/json" },
|
|
120
|
-
body: JSON.dump("workflow_runs" => [run_json]),
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
stub_request(:get, "#{BASE_URL}/actions/runs/#{run_id}/jobs")
|
|
124
|
-
.with(query: hash_including("filter" => "latest"))
|
|
125
|
-
.to_return(
|
|
126
|
-
headers: { "Content-Type" => "application/json" },
|
|
127
|
-
body: JSON.dump("jobs" => [job_json]),
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
stub_request(:get, "#{BASE_URL}/actions/runs/#{run_id}")
|
|
131
|
-
.to_return(
|
|
132
|
-
headers: { "Content-Type" => "application/json" },
|
|
133
|
-
body: JSON.dump(run_json),
|
|
134
|
-
)
|
|
135
|
-
|
|
136
|
-
expect(subject.run { }).to eq true
|
|
137
|
-
end
|
|
138
|
-
end
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
require "spec_helper"
|
|
2
|
-
require "bard/plugins/deploy/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
|
data/spec/bard/ci/runner_spec.rb
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
require "bard/plugins/deploy/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/plugins/deploy/ci/local"
|
|
13
|
-
require "bard/plugins/deploy/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/ci_spec.rb
DELETED
data/spec/bard/cli/ci_spec.rb
DELETED
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
require "spec_helper"
|
|
2
|
-
require "bard/cli"
|
|
3
|
-
require "ostruct"
|
|
4
|
-
|
|
5
|
-
describe "bard ci" do
|
|
6
|
-
let(:cli) { Bard::CLI.new }
|
|
7
|
-
let(:ci_runner) { double("ci_runner") }
|
|
8
|
-
|
|
9
|
-
before do
|
|
10
|
-
allow(cli).to receive(:config).and_return(OpenStruct.new(ci: nil))
|
|
11
|
-
allow(cli).to receive(:project_name).and_return("test_project")
|
|
12
|
-
allow(cli).to receive(:puts)
|
|
13
|
-
allow(cli).to receive(:print)
|
|
14
|
-
allow(cli).to receive(:exit)
|
|
15
|
-
allow(cli).to receive(:red).and_return("")
|
|
16
|
-
allow($stdout).to receive(:flush)
|
|
17
|
-
allow(Bard::Git).to receive(:current_branch).and_return("feature-branch")
|
|
18
|
-
allow(Bard::CI).to receive(:new).and_return(ci_runner)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
describe "#ci" do
|
|
22
|
-
it "should have a ci command" do
|
|
23
|
-
expect(cli).to respond_to(:ci)
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
context "when CI exists and status option is set" do
|
|
27
|
-
it "prints CI status and returns early" do
|
|
28
|
-
allow(cli).to receive(:options).and_return({ "status" => true })
|
|
29
|
-
allow(ci_runner).to receive(:exists?).and_return(true)
|
|
30
|
-
allow(ci_runner).to receive(:status).and_return("Build #123: SUCCESS")
|
|
31
|
-
|
|
32
|
-
expect(cli).to receive(:puts).with("Build #123: SUCCESS")
|
|
33
|
-
expect(ci_runner).not_to receive(:run)
|
|
34
|
-
|
|
35
|
-
cli.ci
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
context "when CI exists and running normally" do
|
|
40
|
-
before do
|
|
41
|
-
allow(cli).to receive(:options).and_return({})
|
|
42
|
-
allow(ci_runner).to receive(:exists?).and_return(true)
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
it "starts CI build and handles success" do
|
|
46
|
-
allow(ci_runner).to receive(:run).and_yield(30, 60).and_return(true)
|
|
47
|
-
|
|
48
|
-
expect(cli).to receive(:puts).with("Continuous integration: starting build on feature-branch...")
|
|
49
|
-
expect(cli).to receive(:puts).with("Continuous integration: success!")
|
|
50
|
-
|
|
51
|
-
cli.ci
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
it "handles CI failure" do
|
|
55
|
-
allow(ci_runner).to receive(:run).and_yield(30, 60).and_return(false)
|
|
56
|
-
allow(ci_runner).to receive(:console).and_return("Test failed: spec/model_spec.rb")
|
|
57
|
-
|
|
58
|
-
expect(cli).to receive(:puts).with("Continuous integration: starting build on feature-branch...")
|
|
59
|
-
expect(cli).to receive(:puts).with("Test failed: spec/model_spec.rb")
|
|
60
|
-
expect(cli).to receive(:puts) # The puts with newline
|
|
61
|
-
expect(cli).to receive(:puts) # The "Automated tests failed!" message
|
|
62
|
-
expect(cli).to receive(:exit).with(1)
|
|
63
|
-
|
|
64
|
-
cli.ci
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
it "displays progress with estimated completion time" do
|
|
68
|
-
allow(ci_runner).to receive(:run).and_yield(30, 60).and_return(true)
|
|
69
|
-
|
|
70
|
-
expect(cli).to receive(:print).with("\x08" * " Estimated completion: 50%".length)
|
|
71
|
-
expect(cli).to receive(:print).with(" Estimated completion: 50%")
|
|
72
|
-
|
|
73
|
-
cli.ci
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
it "displays progress without estimated completion time" do
|
|
77
|
-
allow(ci_runner).to receive(:run).and_yield(45, nil).and_return(true)
|
|
78
|
-
|
|
79
|
-
expect(cli).to receive(:print).with("\x08" * " No estimated completion time. Elapsed time: 45 sec".length)
|
|
80
|
-
expect(cli).to receive(:print).with(" No estimated completion time. Elapsed time: 45 sec")
|
|
81
|
-
|
|
82
|
-
cli.ci
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
context "when CI doesn't exist" do
|
|
87
|
-
it "shows error message and exits" do
|
|
88
|
-
allow(cli).to receive(:options).and_return({})
|
|
89
|
-
allow(ci_runner).to receive(:exists?).and_return(false)
|
|
90
|
-
allow(cli).to receive(:exit).with(1).and_raise(SystemExit)
|
|
91
|
-
|
|
92
|
-
expect { cli.ci }.to raise_error(SystemExit)
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
context "with custom branch" do
|
|
97
|
-
it "uses the specified branch" do
|
|
98
|
-
allow(cli).to receive(:options).and_return({})
|
|
99
|
-
allow(ci_runner).to receive(:exists?).and_return(true)
|
|
100
|
-
allow(ci_runner).to receive(:run).and_return(true)
|
|
101
|
-
|
|
102
|
-
expect(Bard::CI).to receive(:new).with("test_project", "develop", runner_name: nil)
|
|
103
|
-
expect(cli).to receive(:puts).with("Continuous integration: starting build on develop...")
|
|
104
|
-
|
|
105
|
-
cli.ci("develop")
|
|
106
|
-
end
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
context "with local-ci option" do
|
|
110
|
-
it "passes local runner_name to CI" do
|
|
111
|
-
allow(cli).to receive(:options).and_return({ "local-ci" => true })
|
|
112
|
-
allow(ci_runner).to receive(:exists?).and_return(true)
|
|
113
|
-
allow(ci_runner).to receive(:run).and_return(true)
|
|
114
|
-
|
|
115
|
-
expect(Bard::CI).to receive(:new).with("test_project", "feature-branch", runner_name: :local)
|
|
116
|
-
|
|
117
|
-
cli.ci
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
context "with ci option" do
|
|
122
|
-
it "passes specified runner_name to CI" do
|
|
123
|
-
allow(cli).to receive(:options).and_return({ "ci" => "jenkins" })
|
|
124
|
-
allow(ci_runner).to receive(:exists?).and_return(true)
|
|
125
|
-
allow(ci_runner).to receive(:run).and_return(true)
|
|
126
|
-
|
|
127
|
-
expect(Bard::CI).to receive(:new).with("test_project", "feature-branch", runner_name: :jenkins)
|
|
128
|
-
|
|
129
|
-
cli.ci
|
|
130
|
-
end
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
context "with both local-ci and ci options" do
|
|
134
|
-
it "local-ci takes precedence" do
|
|
135
|
-
allow(cli).to receive(:options).and_return({ "local-ci" => true, "ci" => "jenkins" })
|
|
136
|
-
allow(ci_runner).to receive(:exists?).and_return(true)
|
|
137
|
-
allow(ci_runner).to receive(:run).and_return(true)
|
|
138
|
-
|
|
139
|
-
expect(Bard::CI).to receive(:new).with("test_project", "feature-branch", runner_name: :local)
|
|
140
|
-
|
|
141
|
-
cli.ci
|
|
142
|
-
end
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
context "with resume option" do
|
|
146
|
-
it "calls resume instead of run" do
|
|
147
|
-
allow(cli).to receive(:options).and_return({ "resume" => true })
|
|
148
|
-
allow(ci_runner).to receive(:exists?).and_return(true)
|
|
149
|
-
allow(ci_runner).to receive(:resume).and_yield(30, 60).and_return(true)
|
|
150
|
-
|
|
151
|
-
expect(cli).to receive(:puts).with("Continuous integration: resuming build...")
|
|
152
|
-
expect(cli).to receive(:puts).with("Continuous integration: success!")
|
|
153
|
-
expect(ci_runner).not_to receive(:run)
|
|
154
|
-
|
|
155
|
-
cli.ci
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
it "displays progress when resuming" do
|
|
159
|
-
allow(cli).to receive(:options).and_return({ "resume" => true })
|
|
160
|
-
allow(ci_runner).to receive(:exists?).and_return(true)
|
|
161
|
-
allow(ci_runner).to receive(:resume).and_yield(30, 60).and_return(true)
|
|
162
|
-
|
|
163
|
-
expect(cli).to receive(:print).with("\x08" * " Estimated completion: 50%".length)
|
|
164
|
-
expect(cli).to receive(:print).with(" Estimated completion: 50%")
|
|
165
|
-
|
|
166
|
-
cli.ci
|
|
167
|
-
end
|
|
168
|
-
end
|
|
169
|
-
end
|
|
170
|
-
end
|
data/spec/bard/cli/data_spec.rb
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
require "spec_helper"
|
|
2
|
-
require "bard/cli"
|
|
3
|
-
|
|
4
|
-
describe "bard data" do
|
|
5
|
-
let(:cli) { Bard::CLI.new }
|
|
6
|
-
|
|
7
|
-
it "should have a data command" do
|
|
8
|
-
expect(cli).to respond_to(:data)
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
context "data" do
|
|
12
|
-
let(:from) { double("from", key: :production, run!: nil, require_capability!: nil) }
|
|
13
|
-
let(:to) { double("to", key: :local, run!: nil) }
|
|
14
|
-
|
|
15
|
-
let(:config) do
|
|
16
|
-
double("config", data: [], :[] => nil).tap do |config|
|
|
17
|
-
allow(config).to receive(:[]).with("production").and_return(from)
|
|
18
|
-
allow(config).to receive(:[]).with("local").and_return(to)
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
before do
|
|
23
|
-
allow(cli).to receive(:config).and_return(config)
|
|
24
|
-
allow(cli).to receive(:options).and_return({from: "production", to: "local"})
|
|
25
|
-
allow(cli).to receive(:puts)
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
it "should run the data command" do
|
|
29
|
-
expect(from).to receive(:run!).with("bin/rake db:dump")
|
|
30
|
-
expect(Bard::Copy).to receive(:file).with("db/data.sql.gz", from: from, to: to, verbose: true)
|
|
31
|
-
expect(to).to receive(:run!).with("bin/rake db:load")
|
|
32
|
-
cli.data
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
context "pushing to production" do
|
|
36
|
-
let(:to) { double("to", key: :production, url: "https://example.com", run!: nil, require_capability!: nil) }
|
|
37
|
-
|
|
38
|
-
before do
|
|
39
|
-
allow(cli).to receive(:options).and_return({from: "local", to: "production"})
|
|
40
|
-
allow(config).to receive(:[]).with("production").and_return(to)
|
|
41
|
-
allow(config).to receive(:[]).with("local").and_return(from)
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
it "should prevent pushing to production if the user does not confirm" do
|
|
45
|
-
expect(cli).to receive(:ask).and_return("no")
|
|
46
|
-
expect { cli.data }.to raise_error(SystemExit)
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
it "should allow pushing to production if the user confirms" do
|
|
50
|
-
expect(cli).to receive(:ask).and_return("https://example.com")
|
|
51
|
-
expect(from).to receive(:run!).with("bin/rake db:dump")
|
|
52
|
-
expect(Bard::Copy).to receive(:file).with("db/data.sql.gz", from: from, to: to, verbose: true)
|
|
53
|
-
expect(to).to receive(:run!).with("bin/rake db:load")
|
|
54
|
-
cli.data
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
end
|