bard 1.9.6 → 2.0.0.beta
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 +0 -5
- data/MIGRATION_GUIDE.md +9 -24
- data/README.md +6 -14
- data/README.rdoc +15 -0
- data/Rakefile +1 -3
- data/features/bard_check.feature +94 -0
- data/features/bard_deploy.feature +18 -0
- data/features/bard_pull.feature +112 -0
- data/features/bard_push.feature +112 -0
- data/features/podman_testcontainers.feature +16 -0
- data/features/step_definitions/check_steps.rb +47 -0
- data/features/step_definitions/git_steps.rb +73 -0
- data/features/step_definitions/global_steps.rb +56 -0
- data/features/step_definitions/podman_steps.rb +23 -0
- data/features/step_definitions/rails_steps.rb +44 -0
- data/features/step_definitions/submodule_steps.rb +110 -0
- data/features/support/env.rb +39 -14
- data/features/support/grit_ext.rb +13 -0
- data/features/support/io.rb +32 -0
- data/features/support/podman.rb +153 -0
- data/lib/bard/ci/github_actions.rb +2 -1
- data/lib/bard/ci/jenkins.rb +11 -82
- data/lib/bard/ci/local.rb +6 -6
- data/lib/bard/ci/runner.rb +1 -35
- data/lib/bard/ci.rb +23 -11
- data/lib/bard/cli/ci.rb +38 -45
- data/lib/bard/cli/deploy.rb +9 -40
- data/lib/bard/cli/hurt.rb +15 -10
- data/lib/bard/cli/install.rb +12 -7
- data/lib/bard/cli/open.rb +16 -12
- data/lib/bard/cli/ping.rb +14 -8
- data/lib/bard/cli/provision.rb +1 -1
- data/lib/bard/cli/run.rb +3 -5
- data/lib/bard/cli/stage.rb +1 -9
- data/lib/bard/cli/vim.rb +10 -5
- data/lib/bard/cli.rb +11 -13
- data/lib/bard/command.rb +13 -33
- data/lib/bard/config.rb +14 -10
- data/lib/bard/copy.rb +33 -12
- data/lib/bard/deploy_strategy/github_pages.rb +2 -10
- data/lib/bard/deploy_strategy.rb +0 -3
- data/lib/bard/github.rb +4 -2
- data/lib/bard/provision/apt.rb +1 -1
- data/lib/bard/provision/logrotation.rb +1 -1
- data/lib/bard/provision/mysql.rb +2 -2
- data/lib/bard/provision/passenger.rb +2 -2
- data/lib/bard/provision/repo.rb +2 -2
- data/lib/bard/provision/ssh.rb +2 -9
- data/lib/bard/provision/swapfile.rb +1 -3
- data/lib/bard/server.rb +3 -46
- data/lib/bard/ssh_server.rb +2 -9
- data/lib/bard/target.rb +16 -67
- data/lib/bard/version.rb +1 -1
- data/spec/acceptance/docker/Dockerfile +1 -2
- data/spec/bard/ci/github_actions_spec.rb +13 -116
- data/spec/bard/cli/ci_spec.rb +8 -34
- data/spec/bard/cli/deploy_spec.rb +8 -46
- 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 +8 -10
- data/spec/bard/cli/ping_spec.rb +5 -5
- data/spec/bard/cli/run_spec.rb +1 -20
- data/spec/bard/cli/stage_spec.rb +0 -20
- data/spec/bard/cli/vim_spec.rb +5 -5
- data/spec/bard/command_spec.rb +1 -3
- data/spec/bard/config_spec.rb +0 -28
- data/spec/bard/copy_spec.rb +3 -3
- data/spec/bard/github_spec.rb +1 -1
- data/spec/bard/provision/apt_spec.rb +1 -1
- data/spec/bard/provision/logrotation_spec.rb +1 -1
- data/spec/bard/provision/passenger_spec.rb +1 -1
- data/spec/bard/provision/repo_spec.rb +1 -1
- data/spec/bard/provision/ssh_spec.rb +4 -17
- data/spec/bard/provision/swapfile_spec.rb +1 -2
- data/spec/bard/ssh_server_spec.rb +8 -12
- data/spec/bard/target_spec.rb +5 -9
- data/spec/spec_helper.rb +1 -6
- metadata +31 -41
- data/CLAUDE.md +0 -76
- data/PLUGINS.md +0 -114
- data/cucumber.yml +0 -1
- data/features/ci.feature +0 -62
- data/features/data.feature +0 -12
- data/features/deploy.feature +0 -13
- data/features/deploy_git_workflow.feature +0 -88
- data/features/run.feature +0 -13
- data/features/step_definitions/bard_steps.rb +0 -135
- data/features/support/bard-coverage +0 -16
- data/features/support/test_server.rb +0 -216
- data/lib/bard/deprecation.rb +0 -19
- data/lib/bard/plugin.rb +0 -100
- data/lib/bard/plugins/backup.rb +0 -19
- data/lib/bard/plugins/github_pages.rb +0 -34
- data/lib/bard/plugins/hurt.rb +0 -5
- data/lib/bard/plugins/install.rb +0 -5
- data/lib/bard/plugins/jenkins.rb +0 -6
- data/lib/bard/plugins/new.rb +0 -5
- data/lib/bard/plugins/ping.rb +0 -6
- data/lib/bard/plugins/provision.rb +0 -5
- data/lib/bard/plugins/vim.rb +0 -5
- data/lib/bard/secrets.rb +0 -10
- data/spec/bard/ci/jenkins_spec.rb +0 -139
- data/spec/bard/ci/runner_spec.rb +0 -61
- data/spec/bard/deprecation_spec.rb +0 -281
- data/spec/bard/plugin_spec.rb +0 -79
data/features/deploy.feature
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
Feature: bard deploy
|
|
2
|
-
Deploy code changes to a remote server.
|
|
3
|
-
|
|
4
|
-
Background:
|
|
5
|
-
Given a test server is running
|
|
6
|
-
|
|
7
|
-
Scenario: deploys code changes to the remote server
|
|
8
|
-
Given I create a file "DEPLOYED.txt" with content "deployed by bard"
|
|
9
|
-
And I commit the changes with message "Add deployed marker"
|
|
10
|
-
When I run: bard deploy --skip-ci
|
|
11
|
-
Then the output should contain "Deploy Succeeded"
|
|
12
|
-
When I run: bard run "cat DEPLOYED.txt"
|
|
13
|
-
Then the output should contain "deployed by bard"
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
Feature: bard deploy git workflow
|
|
2
|
-
Git workflow behaviors during deploy.
|
|
3
|
-
|
|
4
|
-
Background:
|
|
5
|
-
Given a test server is running
|
|
6
|
-
|
|
7
|
-
Scenario: deploy on master pushes unpushed commits
|
|
8
|
-
Given I create a file "local-only.txt" with content "local commit"
|
|
9
|
-
And I commit the changes with message "Add local only file"
|
|
10
|
-
When I run: bard deploy --skip-ci
|
|
11
|
-
Then the output should contain "Deploy Succeeded"
|
|
12
|
-
When I run: bard run "cat local-only.txt"
|
|
13
|
-
Then the output should contain "local commit"
|
|
14
|
-
|
|
15
|
-
Scenario: feature branch fast-forward merge
|
|
16
|
-
Given I create and switch to branch "feature-branch"
|
|
17
|
-
And I create a file "feature.txt" with content "feature content"
|
|
18
|
-
And I commit the changes with message "Add feature"
|
|
19
|
-
When I run: bard deploy --skip-ci
|
|
20
|
-
Then the output should contain "Deploy Succeeded"
|
|
21
|
-
And I should be on branch "master"
|
|
22
|
-
And branch "feature-branch" should not exist locally
|
|
23
|
-
And branch "feature-branch" should not exist on origin
|
|
24
|
-
When I run: bard run "cat feature.txt"
|
|
25
|
-
Then the output should contain "feature content"
|
|
26
|
-
|
|
27
|
-
Scenario: feature branch requires rebase
|
|
28
|
-
Given I create and switch to branch "feature-branch"
|
|
29
|
-
And I create a file "feature.txt" with content "feature content"
|
|
30
|
-
And I commit the changes with message "Add feature"
|
|
31
|
-
And master has an additional commit from another source
|
|
32
|
-
When I run: bard deploy --skip-ci
|
|
33
|
-
Then the output should contain "The master branch has advanced"
|
|
34
|
-
And the output should contain "Attempting rebase"
|
|
35
|
-
And the output should contain "Deploy Succeeded"
|
|
36
|
-
And I should be on branch "master"
|
|
37
|
-
When I run: bard run "cat feature.txt"
|
|
38
|
-
Then the output should contain "feature content"
|
|
39
|
-
When I run: bard run "cat remote-change.txt"
|
|
40
|
-
Then the output should contain "remote change"
|
|
41
|
-
|
|
42
|
-
Scenario: feature branch rebase conflict
|
|
43
|
-
Given I create and switch to branch "feature-branch"
|
|
44
|
-
And I create a file "conflict.txt" with content "feature content"
|
|
45
|
-
And I commit the changes with message "Add conflicting file"
|
|
46
|
-
And master has a conflicting commit to "conflict.txt"
|
|
47
|
-
When I run expecting failure: bard deploy --skip-ci
|
|
48
|
-
Then the output should contain "The master branch has advanced"
|
|
49
|
-
And the output should contain "Attempting rebase"
|
|
50
|
-
And the output should contain "Running command failed"
|
|
51
|
-
|
|
52
|
-
Scenario: branch cleanup after deploy
|
|
53
|
-
Given I create and switch to branch "cleanup-test"
|
|
54
|
-
And I create a file "cleanup.txt" with content "cleanup test"
|
|
55
|
-
And I commit the changes with message "Add cleanup test file"
|
|
56
|
-
And I push branch "cleanup-test" to origin
|
|
57
|
-
When I run: bard deploy --skip-ci
|
|
58
|
-
Then the output should contain "Deleting branch: cleanup-test"
|
|
59
|
-
And the output should contain "Deploy Succeeded"
|
|
60
|
-
And I should be on branch "master"
|
|
61
|
-
And branch "cleanup-test" should not exist locally
|
|
62
|
-
And branch "cleanup-test" should not exist on origin
|
|
63
|
-
|
|
64
|
-
Scenario: deploy a branch without checking it out
|
|
65
|
-
Given I create and switch to branch "feature-branch"
|
|
66
|
-
And I create a file "feature.txt" with content "feature content"
|
|
67
|
-
And I commit the changes with message "Add feature"
|
|
68
|
-
And I switch to branch "master"
|
|
69
|
-
When I run: bard deploy feature-branch --skip-ci
|
|
70
|
-
Then the output should contain "Deploy Succeeded"
|
|
71
|
-
And I should be on branch "master"
|
|
72
|
-
And branch "feature-branch" should not exist locally
|
|
73
|
-
When I run: bard run "cat feature.txt"
|
|
74
|
-
Then the output should contain "feature content"
|
|
75
|
-
|
|
76
|
-
Scenario: deploy a branch that requires rebase without checking it out
|
|
77
|
-
Given I create and switch to branch "feature-branch"
|
|
78
|
-
And I create a file "feature.txt" with content "feature content"
|
|
79
|
-
And I commit the changes with message "Add feature"
|
|
80
|
-
And I switch to branch "master"
|
|
81
|
-
And master has an additional commit from another source
|
|
82
|
-
When I run: bard deploy feature-branch --skip-ci
|
|
83
|
-
Then the output should contain "The master branch has advanced"
|
|
84
|
-
And the output should contain "Attempting rebase"
|
|
85
|
-
And the output should contain "Deploy Succeeded"
|
|
86
|
-
And I should be on branch "master"
|
|
87
|
-
When I run: bard run "cat feature.txt"
|
|
88
|
-
Then the output should contain "feature content"
|
data/features/run.feature
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
Feature: bard run
|
|
2
|
-
Execute commands on a remote server.
|
|
3
|
-
|
|
4
|
-
Background:
|
|
5
|
-
Given a test server is running
|
|
6
|
-
|
|
7
|
-
Scenario: executes a command on the remote server
|
|
8
|
-
When I run: bard run "echo hello"
|
|
9
|
-
Then the output should contain "hello"
|
|
10
|
-
|
|
11
|
-
Scenario: operates in the configured path
|
|
12
|
-
When I run: bard run "pwd"
|
|
13
|
-
Then the output should contain "testproject"
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
Given /^a test server is running$/ do
|
|
2
|
-
raise "Test server failed to start" unless @container && @ssh_port
|
|
3
|
-
end
|
|
4
|
-
|
|
5
|
-
When /^I run: bard (.+)$/ do |command|
|
|
6
|
-
run_bard(command)
|
|
7
|
-
unless @status.success?
|
|
8
|
-
raise "Command failed with status: #{@status}\nOutput: #{@stdout}"
|
|
9
|
-
end
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
When /^I run expecting failure: bard (.+)$/ do |command|
|
|
13
|
-
run_bard(command)
|
|
14
|
-
unless !@status.success?
|
|
15
|
-
raise "Command succeeded but was expected to fail\nOutput: #{@stdout}"
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
Then /^the output should contain "([^\"]+)"$/ do |expected|
|
|
20
|
-
expect(@stdout).to include(expected)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
Given /^I create a file "([^\"]+)" with content "([^\"]+)"$/ do |filename, content|
|
|
24
|
-
Dir.chdir(@test_dir) do
|
|
25
|
-
File.write(filename, content)
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
Given /^I commit the changes with message "([^\"]+)"$/ do |message|
|
|
30
|
-
Dir.chdir(@test_dir) do
|
|
31
|
-
system("git add -A", out: File::NULL, err: File::NULL)
|
|
32
|
-
system("git commit -m '#{message}'", out: File::NULL, err: File::NULL)
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
Then /^a file "([^\"]+)" should exist locally$/ do |filename|
|
|
37
|
-
path = File.join(@test_dir, filename)
|
|
38
|
-
expect(File.exist?(path)).to be(true), "Expected file #{filename} to exist at #{path}"
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
# Branch management
|
|
42
|
-
Given /^I create and switch to branch "([^"]+)"$/ do |branch_name|
|
|
43
|
-
Dir.chdir(@test_dir) do
|
|
44
|
-
system("git checkout -b #{branch_name}", out: File::NULL, err: File::NULL)
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
Then /^I should be on branch "([^"]+)"$/ do |expected_branch|
|
|
49
|
-
Dir.chdir(@test_dir) do
|
|
50
|
-
current = `git rev-parse --abbrev-ref HEAD`.chomp
|
|
51
|
-
expect(current).to eq(expected_branch)
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
Given /^I push branch "([^"]+)" to origin$/ do |branch_name|
|
|
56
|
-
Dir.chdir(@test_dir) do
|
|
57
|
-
system("git push -u origin #{branch_name}", out: File::NULL, err: File::NULL)
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
Then /^branch "([^"]+)" should not exist locally$/ do |branch_name|
|
|
62
|
-
Dir.chdir(@test_dir) do
|
|
63
|
-
result = system("git rev-parse --verify #{branch_name}", out: File::NULL, err: File::NULL)
|
|
64
|
-
expect(result).to be(false), "Expected branch #{branch_name} to not exist locally"
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
Then /^branch "([^"]+)" should not exist on origin$/ do |branch_name|
|
|
69
|
-
Dir.chdir(@test_dir) do
|
|
70
|
-
system("git fetch --prune origin", out: File::NULL, err: File::NULL)
|
|
71
|
-
result = system("git rev-parse --verify origin/#{branch_name}", out: File::NULL, err: File::NULL)
|
|
72
|
-
expect(result).to be(false), "Expected branch #{branch_name} to not exist on origin"
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
# Simulating remote changes
|
|
77
|
-
Given /^master has an additional commit from another source$/ do
|
|
78
|
-
run_ssh "cd ~/testproject && git pull origin master"
|
|
79
|
-
run_ssh "cd ~/testproject && echo 'remote change' > remote-change.txt"
|
|
80
|
-
run_ssh "cd ~/testproject && git add remote-change.txt"
|
|
81
|
-
run_ssh "cd ~/testproject && git commit -m 'Remote commit on master'"
|
|
82
|
-
run_ssh "cd ~/testproject && git push origin master"
|
|
83
|
-
|
|
84
|
-
Dir.chdir(@test_dir) do
|
|
85
|
-
system("git fetch origin", out: File::NULL, err: File::NULL)
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
Given /^master has a conflicting commit to "([^"]+)"$/ do |filename|
|
|
90
|
-
run_ssh "cd ~/testproject && git pull origin master"
|
|
91
|
-
run_ssh "cd ~/testproject && echo 'conflicting content from remote' > #{filename}"
|
|
92
|
-
run_ssh "cd ~/testproject && git add #{filename}"
|
|
93
|
-
run_ssh "cd ~/testproject && git commit -m 'Remote conflicting commit'"
|
|
94
|
-
run_ssh "cd ~/testproject && git push origin master"
|
|
95
|
-
|
|
96
|
-
Dir.chdir(@test_dir) do
|
|
97
|
-
system("git fetch origin", out: File::NULL, err: File::NULL)
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
# CI setup
|
|
102
|
-
Given /^a local CI script that passes$/ do
|
|
103
|
-
Dir.chdir(@test_dir) do
|
|
104
|
-
File.write("bin/rake", <<~'SCRIPT')
|
|
105
|
-
#!/bin/bash
|
|
106
|
-
case "$1" in
|
|
107
|
-
ci) echo "All tests passed!"; exit 0 ;;
|
|
108
|
-
esac
|
|
109
|
-
SCRIPT
|
|
110
|
-
FileUtils.chmod(0o755, "bin/rake")
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
Given /^a local CI script that fails with "([^"]+)"$/ do |error_message|
|
|
115
|
-
Dir.chdir(@test_dir) do
|
|
116
|
-
File.write("bin/rake", <<~SCRIPT)
|
|
117
|
-
#!/bin/bash
|
|
118
|
-
case "$1" in
|
|
119
|
-
ci) echo "#{error_message}"; exit 1 ;;
|
|
120
|
-
esac
|
|
121
|
-
SCRIPT
|
|
122
|
-
FileUtils.chmod(0o755, "bin/rake")
|
|
123
|
-
end
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
Given /^I switch to branch "([^"]+)"$/ do |branch_name|
|
|
127
|
-
Dir.chdir(@test_dir) do
|
|
128
|
-
system("git checkout #{branch_name}", out: File::NULL, err: File::NULL)
|
|
129
|
-
end
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
# Output negation
|
|
133
|
-
Then /^the output should not contain "([^"]+)"$/ do |unexpected|
|
|
134
|
-
expect(@stdout).not_to include(unexpected)
|
|
135
|
-
end
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
root = File.expand_path("../../..", __FILE__)
|
|
3
|
-
|
|
4
|
-
require "simplecov"
|
|
5
|
-
SimpleCov.root(root)
|
|
6
|
-
SimpleCov.start do
|
|
7
|
-
command_name "Cucumber (subprocess #{$$})"
|
|
8
|
-
track_files "lib/**/*.rb"
|
|
9
|
-
add_filter "spec/"
|
|
10
|
-
add_filter "features/"
|
|
11
|
-
coverage_dir "#{root}/coverage"
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
$LOAD_PATH.unshift("#{root}/lib")
|
|
15
|
-
require "bard/cli"
|
|
16
|
-
Bard::CLI.start ARGV
|
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
require "fileutils"
|
|
2
|
-
require "open3"
|
|
3
|
-
require "tmpdir"
|
|
4
|
-
require "docker-api"
|
|
5
|
-
|
|
6
|
-
module TestServerWorld
|
|
7
|
-
class << self
|
|
8
|
-
attr_accessor :server_available, :image_built
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
class PrerequisiteError < StandardError; end
|
|
12
|
-
|
|
13
|
-
def ensure_server_available
|
|
14
|
-
return if TestServerWorld.server_available
|
|
15
|
-
|
|
16
|
-
unless system("command -v podman >/dev/null 2>&1")
|
|
17
|
-
raise PrerequisiteError, "podman is not installed"
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
configure_container_socket
|
|
21
|
-
build_test_image
|
|
22
|
-
FileUtils.chmod(0o600, ssh_key_path)
|
|
23
|
-
|
|
24
|
-
TestServerWorld.server_available = true
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def configure_container_socket
|
|
28
|
-
if ENV["DOCKER_HOST"]
|
|
29
|
-
Docker.url = ENV["DOCKER_HOST"]
|
|
30
|
-
return
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
socket_path = "/run/user/#{Process.uid}/podman/podman.sock"
|
|
34
|
-
unless File.exist?(socket_path)
|
|
35
|
-
system("systemctl --user start podman.socket 2>/dev/null")
|
|
36
|
-
sleep 2
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
unless File.exist?(socket_path)
|
|
40
|
-
raise PrerequisiteError, "Podman socket not available"
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
ENV["DOCKER_HOST"] = "unix://#{socket_path}"
|
|
44
|
-
Docker.url = ENV["DOCKER_HOST"]
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def build_test_image
|
|
48
|
-
return if TestServerWorld.image_built
|
|
49
|
-
|
|
50
|
-
# Check if image already exists (e.g., pre-built in CI)
|
|
51
|
-
if image_exists?("bard-test-server")
|
|
52
|
-
TestServerWorld.image_built = true
|
|
53
|
-
return
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
system("podman pull ubuntu:22.04 >/dev/null 2>&1")
|
|
57
|
-
|
|
58
|
-
docker_dir = File.join(ROOT, "spec/acceptance/docker")
|
|
59
|
-
unless system("podman build -t bard-test-server -f #{docker_dir}/Dockerfile #{docker_dir} 2>&1")
|
|
60
|
-
raise PrerequisiteError, "Failed to build test image"
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
TestServerWorld.image_built = true
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def image_exists?(name)
|
|
67
|
-
Docker::Image.get(name)
|
|
68
|
-
true
|
|
69
|
-
rescue Docker::Error::NotFoundError
|
|
70
|
-
false
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
def start_test_server
|
|
74
|
-
ensure_server_available
|
|
75
|
-
|
|
76
|
-
@container = Docker::Container.create(
|
|
77
|
-
"Image" => "localhost/bard-test-server:latest",
|
|
78
|
-
"ExposedPorts" => { "22/tcp" => {} },
|
|
79
|
-
"HostConfig" => {
|
|
80
|
-
"PortBindings" => { "22/tcp" => [{ "HostPort" => "" }] },
|
|
81
|
-
"PublishAllPorts" => true
|
|
82
|
-
}
|
|
83
|
-
)
|
|
84
|
-
@container.start
|
|
85
|
-
@container.refresh!
|
|
86
|
-
|
|
87
|
-
@ssh_port = @container.info["NetworkSettings"]["Ports"]["22/tcp"].first["HostPort"].to_i
|
|
88
|
-
|
|
89
|
-
wait_for_ssh
|
|
90
|
-
setup_test_directory
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
def wait_for_ssh
|
|
94
|
-
30.times do
|
|
95
|
-
return if system(
|
|
96
|
-
"ssh", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null",
|
|
97
|
-
"-o", "ConnectTimeout=1", "-p", @ssh_port.to_s, "-i", ssh_key_path,
|
|
98
|
-
"deploy@localhost", "true",
|
|
99
|
-
out: File::NULL, err: File::NULL
|
|
100
|
-
)
|
|
101
|
-
sleep 0.5
|
|
102
|
-
end
|
|
103
|
-
raise PrerequisiteError, "SSH not ready"
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
def setup_test_directory
|
|
107
|
-
# Set up git repos on the remote container
|
|
108
|
-
run_ssh "git config --global user.email 'test@example.com'"
|
|
109
|
-
run_ssh "git config --global user.name 'Test User'"
|
|
110
|
-
run_ssh "git config --global init.defaultBranch master"
|
|
111
|
-
run_ssh "mkdir -p ~/repos/testproject.git"
|
|
112
|
-
run_ssh "cd ~/repos/testproject.git && git init --bare"
|
|
113
|
-
run_ssh "git clone ~/repos/testproject.git ~/testproject"
|
|
114
|
-
run_ssh "mkdir -p ~/testproject/bin ~/testproject/db"
|
|
115
|
-
|
|
116
|
-
# bin/setup script
|
|
117
|
-
run_ssh "echo '#!/bin/bash' > ~/testproject/bin/setup"
|
|
118
|
-
run_ssh "echo 'echo Setup complete' >> ~/testproject/bin/setup"
|
|
119
|
-
run_ssh "chmod +x ~/testproject/bin/setup"
|
|
120
|
-
|
|
121
|
-
# bin/rake script for db:dump and db:load
|
|
122
|
-
run_ssh <<~'SETUP'
|
|
123
|
-
cat > ~/testproject/bin/rake << 'SCRIPT'
|
|
124
|
-
#!/bin/bash
|
|
125
|
-
case "$1" in
|
|
126
|
-
db:dump)
|
|
127
|
-
echo "production data" | gzip > db/data.sql.gz
|
|
128
|
-
;;
|
|
129
|
-
db:load)
|
|
130
|
-
gunzip -c db/data.sql.gz > /dev/null
|
|
131
|
-
echo "Data loaded"
|
|
132
|
-
;;
|
|
133
|
-
esac
|
|
134
|
-
SCRIPT
|
|
135
|
-
SETUP
|
|
136
|
-
run_ssh "chmod +x ~/testproject/bin/rake"
|
|
137
|
-
run_ssh "cd ~/testproject && git add . && git commit -m 'Initial commit'"
|
|
138
|
-
run_ssh "cd ~/testproject && git push origin master"
|
|
139
|
-
|
|
140
|
-
# Set up local git repo in isolated temp directory
|
|
141
|
-
setup_local_git_repo
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
def setup_local_git_repo
|
|
145
|
-
@test_dir = Dir.mktmpdir("bard_test")
|
|
146
|
-
@ssh_command = "ssh -i #{ssh_key_path} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
|
|
147
|
-
|
|
148
|
-
Dir.chdir(@test_dir) do
|
|
149
|
-
# Clone directly into the temp directory (pass SSH command via env, not global ENV)
|
|
150
|
-
ssh_url = "ssh://deploy@localhost:#{@ssh_port}/home/deploy/repos/testproject.git"
|
|
151
|
-
system({ "GIT_SSH_COMMAND" => @ssh_command }, "git clone #{ssh_url} .", out: File::NULL, err: File::NULL)
|
|
152
|
-
|
|
153
|
-
# Configure git settings locally in this repo only
|
|
154
|
-
system("git config user.email 'test@example.com'", out: File::NULL, err: File::NULL)
|
|
155
|
-
system("git config user.name 'Test User'", out: File::NULL, err: File::NULL)
|
|
156
|
-
system("git config core.sshCommand '#{@ssh_command}'", out: File::NULL, err: File::NULL)
|
|
157
|
-
|
|
158
|
-
# Ensure db directory exists locally
|
|
159
|
-
FileUtils.mkdir_p("db")
|
|
160
|
-
|
|
161
|
-
# Write bard config in the test directory
|
|
162
|
-
File.write("bard.rb", <<~RUBY)
|
|
163
|
-
target :production do
|
|
164
|
-
ssh "deploy@localhost:#{@ssh_port}",
|
|
165
|
-
path: "testproject",
|
|
166
|
-
ssh_key: "#{ssh_key_path}"
|
|
167
|
-
ping false
|
|
168
|
-
end
|
|
169
|
-
RUBY
|
|
170
|
-
end
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
def run_ssh(command)
|
|
174
|
-
stdout, status = Open3.capture2e(
|
|
175
|
-
"ssh", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null",
|
|
176
|
-
"-p", @ssh_port.to_s, "-i", ssh_key_path,
|
|
177
|
-
"deploy@localhost", command
|
|
178
|
-
)
|
|
179
|
-
unless status.success?
|
|
180
|
-
raise PrerequisiteError, "SSH command failed: #{command}\nOutput: #{stdout}"
|
|
181
|
-
end
|
|
182
|
-
true
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
def run_bard(command)
|
|
186
|
-
Dir.chdir(@test_dir) do
|
|
187
|
-
bard_coverage = File.join(ROOT, "features/support/bard-coverage")
|
|
188
|
-
@stdout, @status = Open3.capture2e("#{bard_coverage} #{command}")
|
|
189
|
-
end
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
def ssh_key_path
|
|
193
|
-
File.join(ROOT, "spec/acceptance/docker/test_key")
|
|
194
|
-
end
|
|
195
|
-
|
|
196
|
-
def stop_test_server
|
|
197
|
-
return unless @container
|
|
198
|
-
@container.stop rescue nil
|
|
199
|
-
@container.delete(force: true) rescue nil
|
|
200
|
-
ensure
|
|
201
|
-
@container = nil
|
|
202
|
-
@ssh_port = nil
|
|
203
|
-
FileUtils.rm_rf(@test_dir) if @test_dir
|
|
204
|
-
@test_dir = nil
|
|
205
|
-
end
|
|
206
|
-
end
|
|
207
|
-
|
|
208
|
-
World(TestServerWorld)
|
|
209
|
-
|
|
210
|
-
Before do
|
|
211
|
-
start_test_server
|
|
212
|
-
end
|
|
213
|
-
|
|
214
|
-
After do
|
|
215
|
-
stop_test_server
|
|
216
|
-
end
|
data/lib/bard/deprecation.rb
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
module Bard
|
|
2
|
-
module Deprecation
|
|
3
|
-
@warned = {}
|
|
4
|
-
|
|
5
|
-
def self.warn(message, callsite: nil)
|
|
6
|
-
callsite ||= caller_locations(2, 1).first
|
|
7
|
-
key = "#{callsite.path}:#{callsite.lineno}:#{message}"
|
|
8
|
-
return if @warned[key]
|
|
9
|
-
|
|
10
|
-
@warned[key] = true
|
|
11
|
-
location = "#{callsite.path}:#{callsite.lineno}"
|
|
12
|
-
Kernel.warn "[DEPRECATION] #{message} (called from #{location})"
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def self.reset!
|
|
16
|
-
@warned = {}
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
end
|
data/lib/bard/plugin.rb
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
module Bard
|
|
2
|
-
class Plugin
|
|
3
|
-
@registry = {}
|
|
4
|
-
|
|
5
|
-
class << self
|
|
6
|
-
attr_reader :registry
|
|
7
|
-
|
|
8
|
-
def register(name, &block)
|
|
9
|
-
plugin = new(name)
|
|
10
|
-
plugin.instance_eval(&block) if block
|
|
11
|
-
@registry[name.to_sym] = plugin
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def [](name)
|
|
15
|
-
@registry[name.to_sym]
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def all
|
|
19
|
-
@registry.values
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def load_all!
|
|
23
|
-
Dir[File.join(__dir__, "plugins", "*.rb")].sort.each { |f| require f }
|
|
24
|
-
Dir[File.join(Dir.pwd, "lib", "bard", "plugins", "*.rb")].sort.each { |f| require f }
|
|
25
|
-
all.each(&:apply!)
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def reset!
|
|
29
|
-
@registry = {}
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
attr_reader :name, :cli_modules
|
|
34
|
-
|
|
35
|
-
def initialize(name)
|
|
36
|
-
@name = name.to_sym
|
|
37
|
-
@cli_modules = []
|
|
38
|
-
@cli_requires = []
|
|
39
|
-
@target_methods = {}
|
|
40
|
-
@config_methods = {}
|
|
41
|
-
@requires = []
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
# DSL methods for defining plugins
|
|
45
|
-
|
|
46
|
-
def require_file(path)
|
|
47
|
-
@requires << path
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def cli(mod, require: nil)
|
|
51
|
-
@cli_requires << require if require
|
|
52
|
-
@cli_modules << mod
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
def target_method(name, &block)
|
|
56
|
-
@target_methods[name] = block
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def config_method(name, &block)
|
|
60
|
-
@config_methods[name] = block
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
# Apply plugin to the system (non-CLI parts)
|
|
64
|
-
def apply!
|
|
65
|
-
@requires.each { |path| require path }
|
|
66
|
-
apply_target_methods!
|
|
67
|
-
apply_config_methods!
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def apply_to_cli(cli_class)
|
|
71
|
-
@cli_requires.each { |path| require path }
|
|
72
|
-
@cli_modules.each do |mod|
|
|
73
|
-
mod = resolve_constant(mod) if mod.is_a?(String)
|
|
74
|
-
mod.setup(cli_class)
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
private
|
|
79
|
-
|
|
80
|
-
def apply_target_methods!
|
|
81
|
-
return if @target_methods.empty?
|
|
82
|
-
require "bard/target"
|
|
83
|
-
@target_methods.each do |method_name, block|
|
|
84
|
-
Target.define_method(method_name, &block)
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
def apply_config_methods!
|
|
89
|
-
return if @config_methods.empty?
|
|
90
|
-
require "bard/config"
|
|
91
|
-
@config_methods.each do |method_name, block|
|
|
92
|
-
Config.define_method(method_name, &block)
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
def resolve_constant(name)
|
|
97
|
-
name.split("::").reduce(Object) { |mod, const| mod.const_get(const) }
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
end
|
data/lib/bard/plugins/backup.rb
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
require "bard/plugin"
|
|
2
|
-
|
|
3
|
-
Bard::Plugin.register :backup do
|
|
4
|
-
config_method :backup do |value = nil, &block|
|
|
5
|
-
if block
|
|
6
|
-
@backup = Bard::BackupConfig.new(&block)
|
|
7
|
-
elsif value == false
|
|
8
|
-
@backup = Bard::BackupConfig.new { disabled }
|
|
9
|
-
elsif value.nil? # Getter
|
|
10
|
-
@backup ||= Bard::BackupConfig.new { bard }
|
|
11
|
-
else
|
|
12
|
-
raise ArgumentError, "backup accepts false or a block"
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
config_method :backup_enabled? do
|
|
17
|
-
backup == true
|
|
18
|
-
end
|
|
19
|
-
end
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
require "bard/plugin"
|
|
2
|
-
|
|
3
|
-
# Load the deploy strategy (auto-registers via inherited hook)
|
|
4
|
-
require "bard/deploy_strategy/github_pages"
|
|
5
|
-
|
|
6
|
-
Bard::Plugin.register :github_pages do
|
|
7
|
-
# Config DSL: github_pages "url" sets up a production target
|
|
8
|
-
config_method :github_pages do |url|
|
|
9
|
-
urls = []
|
|
10
|
-
uri = url.start_with?("http") ? URI.parse(url) : URI.parse("https://#{url}")
|
|
11
|
-
hostname = uri.hostname.sub(/^www\./, "")
|
|
12
|
-
urls = [hostname]
|
|
13
|
-
urls << "www.#{hostname}" if hostname.count(".") < 2
|
|
14
|
-
|
|
15
|
-
target :production do
|
|
16
|
-
github_pages url
|
|
17
|
-
ssh false
|
|
18
|
-
ping(*urls) if urls.any?
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
backup false
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
# Target DSL: github_pages sets deploy strategy
|
|
25
|
-
target_method :github_pages do |url = nil|
|
|
26
|
-
if url.nil?
|
|
27
|
-
@github_pages_url
|
|
28
|
-
else
|
|
29
|
-
@deploy_strategy = :github_pages
|
|
30
|
-
@github_pages_url = url
|
|
31
|
-
enable_capability(:github_pages)
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
end
|
data/lib/bard/plugins/hurt.rb
DELETED
data/lib/bard/plugins/install.rb
DELETED
data/lib/bard/plugins/jenkins.rb
DELETED