bard 1.9.6 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +2 -2
- data/CLAUDE.md +1 -1
- data/PLUGINS.md +31 -46
- data/bard.gemspec +2 -1
- data/features/ci.feature +1 -0
- data/features/data.feature +1 -0
- data/features/deploy.feature +1 -0
- data/features/deploy_git_workflow.feature +1 -0
- data/features/run.feature +1 -0
- data/features/step_definitions/bard_steps.rb +1 -0
- data/features/support/test_server.rb +3 -3
- data/lib/bard/cli.rb +9 -28
- data/lib/bard/command.rb +9 -88
- data/lib/bard/config.rb +38 -177
- data/lib/bard/copy.rb +28 -82
- data/lib/bard/plugins/data.rb +56 -0
- data/lib/bard/{ci → plugins/deploy/ci}/github_actions.rb +2 -2
- data/lib/bard/{ci → plugins/deploy/ci}/jenkins.rb +1 -1
- data/lib/bard/{ci → plugins/deploy/ci}/local.rb +1 -1
- data/lib/bard/{ci → plugins/deploy/ci}/runner.rb +3 -3
- data/lib/bard/{ci.rb → plugins/deploy/ci.rb} +1 -1
- data/lib/bard/plugins/deploy/ssh_strategy.rb +27 -0
- data/lib/bard/{deploy_strategy.rb → plugins/deploy/strategy.rb} +1 -4
- data/lib/bard/plugins/deploy.rb +240 -0
- data/lib/bard/{git.rb → plugins/git.rb} +6 -3
- data/lib/bard/{github.rb → plugins/github.rb} +2 -2
- data/lib/bard/{deploy_strategy/github_pages.rb → plugins/github_pages/strategy.rb} +3 -4
- data/lib/bard/plugins/github_pages.rb +11 -15
- data/lib/bard/plugins/hurt.rb +12 -4
- data/{install_files → lib/bard/plugins/install}/.github/dependabot.yml +2 -1
- data/{install_files → lib/bard/plugins/install}/.github/workflows/cache-ci.yml +1 -1
- data/{install_files → lib/bard/plugins/install}/.github/workflows/ci.yml +2 -2
- data/lib/bard/plugins/install.rb +7 -3
- data/lib/bard/plugins/open.rb +20 -0
- data/lib/bard/{ping.rb → plugins/ping/check.rb} +4 -4
- data/lib/bard/plugins/ping/target_methods.rb +23 -0
- data/lib/bard/plugins/ping.rb +8 -4
- data/lib/bard/plugins/run.rb +19 -0
- data/lib/bard/plugins/setup.rb +54 -0
- data/lib/bard/plugins/ssh/connection.rb +75 -0
- data/lib/bard/plugins/ssh/copy.rb +95 -0
- data/lib/bard/{ssh_server.rb → plugins/ssh/server.rb} +10 -42
- data/lib/bard/plugins/ssh/target_methods.rb +20 -0
- data/lib/bard/plugins/ssh.rb +10 -0
- data/lib/bard/plugins/url/target_methods.rb +23 -0
- data/lib/bard/plugins/url.rb +1 -0
- data/lib/bard/plugins/vim.rb +5 -4
- data/lib/bard/retryable.rb +25 -0
- data/lib/bard/target.rb +21 -230
- data/lib/bard/version.rb +1 -1
- data/lib/bard.rb +1 -3
- data/spec/acceptance/docker/Dockerfile +1 -1
- data/spec/bard/capability_spec.rb +8 -50
- data/spec/bard/ci/github_actions_spec.rb +1 -1
- data/spec/bard/ci/jenkins_spec.rb +1 -1
- data/spec/bard/ci/runner_spec.rb +3 -3
- data/spec/bard/ci_spec.rb +1 -1
- data/spec/bard/cli/ci_spec.rb +4 -23
- data/spec/bard/cli/data_spec.rb +7 -26
- data/spec/bard/cli/deploy_spec.rb +43 -40
- data/spec/bard/cli/hurt_spec.rb +2 -8
- data/spec/bard/cli/install_spec.rb +4 -10
- data/spec/bard/cli/master_key_spec.rb +5 -19
- data/spec/bard/cli/open_spec.rb +17 -35
- data/spec/bard/cli/ping_spec.rb +10 -25
- data/spec/bard/cli/run_spec.rb +10 -23
- data/spec/bard/cli/setup_spec.rb +10 -27
- data/spec/bard/cli/ssh_spec.rb +10 -25
- data/spec/bard/cli/stage_spec.rb +17 -32
- data/spec/bard/cli/vim_spec.rb +5 -11
- data/spec/bard/command_spec.rb +1 -10
- data/spec/bard/config_spec.rb +68 -116
- data/spec/bard/copy_spec.rb +54 -18
- data/spec/bard/deploy_strategy/ssh_spec.rb +65 -7
- data/spec/bard/deploy_strategy_spec.rb +1 -1
- data/spec/bard/dynamic_dsl_spec.rb +18 -98
- data/spec/bard/git_spec.rb +9 -5
- data/spec/bard/github_spec.rb +1 -1
- data/spec/bard/ping_spec.rb +5 -5
- data/spec/bard/ssh_copy_spec.rb +44 -0
- data/spec/bard/ssh_server_spec.rb +1 -98
- data/spec/bard/target_spec.rb +61 -108
- metadata +50 -124
- data/lib/bard/ci/retryable.rb +0 -27
- data/lib/bard/cli/ci.rb +0 -73
- data/lib/bard/cli/command.rb +0 -26
- data/lib/bard/cli/data.rb +0 -45
- data/lib/bard/cli/deploy.rb +0 -116
- data/lib/bard/cli/hurt.rb +0 -15
- data/lib/bard/cli/install.rb +0 -11
- data/lib/bard/cli/master_key.rb +0 -17
- data/lib/bard/cli/new.rb +0 -101
- data/lib/bard/cli/new_rails_template.rb +0 -197
- data/lib/bard/cli/open.rb +0 -18
- data/lib/bard/cli/ping.rb +0 -12
- data/lib/bard/cli/provision.rb +0 -34
- data/lib/bard/cli/run.rb +0 -26
- data/lib/bard/cli/setup.rb +0 -56
- data/lib/bard/cli/ssh.rb +0 -14
- data/lib/bard/cli/stage.rb +0 -35
- data/lib/bard/cli/vim.rb +0 -8
- data/lib/bard/default_config.rb +0 -35
- data/lib/bard/deploy_strategy/ssh.rb +0 -19
- data/lib/bard/deprecation.rb +0 -19
- data/lib/bard/github_pages.rb +0 -134
- data/lib/bard/plugin.rb +0 -100
- data/lib/bard/plugins/backup.rb +0 -19
- data/lib/bard/plugins/jenkins.rb +0 -6
- data/lib/bard/plugins/new.rb +0 -5
- data/lib/bard/plugins/provision.rb +0 -5
- data/lib/bard/provision/app.rb +0 -10
- data/lib/bard/provision/apt.rb +0 -16
- data/lib/bard/provision/authorizedkeys.rb +0 -25
- data/lib/bard/provision/data.rb +0 -27
- data/lib/bard/provision/deploy.rb +0 -10
- data/lib/bard/provision/http.rb +0 -16
- data/lib/bard/provision/logrotation.rb +0 -30
- data/lib/bard/provision/masterkey.rb +0 -18
- data/lib/bard/provision/mysql.rb +0 -22
- data/lib/bard/provision/passenger.rb +0 -37
- data/lib/bard/provision/repo.rb +0 -72
- data/lib/bard/provision/rvm.rb +0 -22
- data/lib/bard/provision/ssh.rb +0 -79
- data/lib/bard/provision/swapfile.rb +0 -23
- data/lib/bard/provision/user.rb +0 -42
- data/lib/bard/provision.rb +0 -16
- data/lib/bard/server.rb +0 -160
- data/spec/bard/cli/command_spec.rb +0 -50
- data/spec/bard/cli/new_spec.rb +0 -73
- data/spec/bard/cli/provision_spec.rb +0 -42
- data/spec/bard/deprecation_spec.rb +0 -281
- data/spec/bard/github_pages_spec.rb +0 -143
- data/spec/bard/plugin_spec.rb +0 -79
- data/spec/bard/provision/app_spec.rb +0 -33
- data/spec/bard/provision/apt_spec.rb +0 -39
- data/spec/bard/provision/authorizedkeys_spec.rb +0 -40
- data/spec/bard/provision/data_spec.rb +0 -54
- data/spec/bard/provision/deploy_spec.rb +0 -33
- data/spec/bard/provision/http_spec.rb +0 -57
- data/spec/bard/provision/logrotation_spec.rb +0 -34
- data/spec/bard/provision/masterkey_spec.rb +0 -63
- data/spec/bard/provision/mysql_spec.rb +0 -55
- data/spec/bard/provision/passenger_spec.rb +0 -81
- data/spec/bard/provision/repo_spec.rb +0 -208
- data/spec/bard/provision/rvm_spec.rb +0 -49
- data/spec/bard/provision/ssh_spec.rb +0 -242
- data/spec/bard/provision/swapfile_spec.rb +0 -33
- data/spec/bard/provision/user_spec.rb +0 -103
- data/spec/bard/provision_spec.rb +0 -28
- data/spec/bard/server_spec.rb +0 -127
- /data/lib/bard/{ci → plugins/deploy/ci}/state.rb +0 -0
- /data/{install_files → lib/bard/plugins/install}/apt_dependencies.rb +0 -0
- /data/{install_files → lib/bard/plugins/install}/ci +0 -0
- /data/{install_files → lib/bard/plugins/install}/setup +0 -0
- /data/{install_files → lib/bard/plugins/install}/specified_bundler.rb +0 -0
- /data/{install_files → lib/bard/plugins/install}/specified_ruby.rb +0 -0
data/lib/bard/copy.rb
CHANGED
|
@@ -1,99 +1,45 @@
|
|
|
1
|
-
require "uri"
|
|
2
|
-
require "bard/command"
|
|
3
|
-
|
|
4
1
|
module Bard
|
|
5
|
-
class Copy
|
|
6
|
-
|
|
7
|
-
new(path, from, to, verbose).scp
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def self.dir path, from:, to:, verbose: false
|
|
11
|
-
new(path, from, to, verbose).rsync
|
|
12
|
-
end
|
|
2
|
+
class Copy
|
|
3
|
+
@handlers = []
|
|
13
4
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
scp_using_local :from, from
|
|
19
|
-
else
|
|
20
|
-
scp_as_mediator
|
|
5
|
+
class << self
|
|
6
|
+
def inherited(subclass)
|
|
7
|
+
super
|
|
8
|
+
@handlers.unshift(subclass)
|
|
21
9
|
end
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def scp_using_local direction, target_or_server
|
|
25
|
-
# Support both new Target (with server attribute) and old Server
|
|
26
|
-
ssh_server = target_or_server.respond_to?(:server) ? target_or_server.server : target_or_server
|
|
27
10
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
ssh_opts = "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR"
|
|
33
|
-
|
|
34
|
-
# scp uses -P for port (uppercase, unlike ssh's -p)
|
|
35
|
-
port = ssh_server.port
|
|
36
|
-
port_opt = port && port.to_s != "22" ? "-P #{port}" : ""
|
|
37
|
-
|
|
38
|
-
from_and_to = [path, target_or_server.scp_uri(path).to_s]
|
|
39
|
-
from_and_to.reverse! if direction == :from
|
|
40
|
-
|
|
41
|
-
command = ["scp", ssh_opts, gateway, ssh_key, port_opt, *from_and_to].reject(&:empty?).join(" ")
|
|
42
|
-
Bard::Command.run! command, verbose: verbose
|
|
43
|
-
end
|
|
11
|
+
def file(path, from:, to:, verbose: false)
|
|
12
|
+
handler_for!(from, to).new(path, from, to, verbose).file
|
|
13
|
+
end
|
|
44
14
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
15
|
+
def dir(path, from:, to:, verbose: false)
|
|
16
|
+
handler_for!(from, to).new(path, from, to, verbose).dir
|
|
17
|
+
end
|
|
48
18
|
|
|
49
|
-
|
|
50
|
-
command = "scp -o ForwardAgent=yes #{from.scp_uri(path)} #{to.scp_uri(path)}"
|
|
51
|
-
Bard::Command.run! command, verbose: verbose
|
|
52
|
-
end
|
|
19
|
+
private
|
|
53
20
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
rsync_using_local :from, from
|
|
59
|
-
else
|
|
60
|
-
rsync_as_mediator
|
|
21
|
+
def handler_for!(from, to)
|
|
22
|
+
handler = @handlers.find { |h| h.can_handle?(from, to) }
|
|
23
|
+
raise "No copy handler for #{from.key} -> #{to.key}" unless handler
|
|
24
|
+
handler
|
|
61
25
|
end
|
|
62
26
|
end
|
|
63
27
|
|
|
64
|
-
|
|
65
|
-
# Support both new Target (with server attribute) and old Server
|
|
66
|
-
ssh_server = target_or_server.respond_to?(:server) ? target_or_server.server : target_or_server
|
|
67
|
-
|
|
68
|
-
ssh_uri = ssh_server.ssh_uri
|
|
69
|
-
|
|
70
|
-
gateway = ssh_server.gateway ? "-oProxyCommand=\"ssh #{ssh_server.gateway} -W %h:%p\"" : ""
|
|
71
|
-
|
|
72
|
-
ssh_key = ssh_server.ssh_key ? "-i #{ssh_server.ssh_key}" : ""
|
|
73
|
-
ssh = "-e'ssh #{gateway} -p#{ssh_uri.port || 22}'"
|
|
28
|
+
attr_reader :path, :from, :to, :verbose
|
|
74
29
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
Bard::Command.run! command, verbose: verbose
|
|
30
|
+
def initialize(path, from, to, verbose)
|
|
31
|
+
@path = path
|
|
32
|
+
@from = from
|
|
33
|
+
@to = to
|
|
34
|
+
@verbose = verbose
|
|
81
35
|
end
|
|
82
36
|
|
|
83
|
-
def
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
raise NotImplementedError if from_server.gateway || to_server.gateway || from_server.ssh_key || to_server.ssh_key
|
|
88
|
-
|
|
89
|
-
from_uri = from_server.ssh_uri
|
|
90
|
-
to_uri = to_server.ssh_uri
|
|
91
|
-
|
|
92
|
-
from_str = "-p#{from_uri.port || 22} #{from_uri.user}@#{from_uri.host}"
|
|
93
|
-
to_str = to.rsync_uri(path).sub(%r(/[^/]+$), '/')
|
|
37
|
+
def file
|
|
38
|
+
raise NotImplementedError
|
|
39
|
+
end
|
|
94
40
|
|
|
95
|
-
|
|
96
|
-
|
|
41
|
+
def dir
|
|
42
|
+
raise NotImplementedError
|
|
97
43
|
end
|
|
98
44
|
end
|
|
99
45
|
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
require "bard/command"
|
|
2
|
+
require "bard/copy"
|
|
3
|
+
require "bard/plugins/ssh"
|
|
4
|
+
require "bard/plugins/url"
|
|
5
|
+
|
|
6
|
+
class Bard::CLI
|
|
7
|
+
option :from, default: "production"
|
|
8
|
+
option :to, default: "local"
|
|
9
|
+
desc "data --from=production --to=local", "copy database and assets from from to to"
|
|
10
|
+
def data
|
|
11
|
+
from = config[options[:from]]
|
|
12
|
+
to = config[options[:to]]
|
|
13
|
+
|
|
14
|
+
from.require_capability!(:ssh) unless from.key == :local
|
|
15
|
+
to.require_capability!(:ssh) unless to.key == :local
|
|
16
|
+
|
|
17
|
+
if to.key == :production
|
|
18
|
+
url = to.url
|
|
19
|
+
puts yellow "WARNING: You are about to push data to production, overwriting everything that is there!"
|
|
20
|
+
answer = ask("If you really want to do this, please type in the full HTTPS url of the production server:")
|
|
21
|
+
if answer != url
|
|
22
|
+
puts red("!!! ") + "Failed! We expected #{url}. Is this really where you want to overwrite all the data?"
|
|
23
|
+
exit 1
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
puts "Dumping #{from.key} database to file..."
|
|
28
|
+
from.run! "bin/rake db:dump"
|
|
29
|
+
|
|
30
|
+
puts "Transfering file from #{from.key} to #{to.key}..."
|
|
31
|
+
Bard::Copy.file "db/data.sql.gz", from: from, to: to, verbose: true
|
|
32
|
+
|
|
33
|
+
puts "Loading file into #{to.key} database..."
|
|
34
|
+
to.run! "bin/rake db:load"
|
|
35
|
+
|
|
36
|
+
config.data.each do |path|
|
|
37
|
+
puts "Synchronizing files in #{path}..."
|
|
38
|
+
Bard::Copy.dir path, from: from, to: to, verbose: true
|
|
39
|
+
end
|
|
40
|
+
rescue Bard::Command::Error => e
|
|
41
|
+
puts red("!!! ") + "Running command failed: #{yellow(e.message)}"
|
|
42
|
+
exit 1
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
require "bard/config"
|
|
47
|
+
|
|
48
|
+
class Bard::Config
|
|
49
|
+
def data(*paths)
|
|
50
|
+
if paths.empty?
|
|
51
|
+
@data_paths ||= []
|
|
52
|
+
else
|
|
53
|
+
@data_paths = paths
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
require "bard/ci/state"
|
|
2
|
-
require "bard/
|
|
1
|
+
require "bard/plugins/deploy/ci/state"
|
|
2
|
+
require "bard/retryable"
|
|
3
3
|
|
|
4
4
|
module Bard
|
|
5
5
|
class CI
|
|
6
6
|
class Runner < Struct.new(:project_name, :branch, :sha)
|
|
7
|
-
include Retryable
|
|
7
|
+
include Bard::Retryable
|
|
8
8
|
|
|
9
9
|
@runners = {}
|
|
10
10
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require "bard/plugins/deploy/strategy"
|
|
2
|
+
require "bard/copy"
|
|
3
|
+
require "bard/plugins/ssh"
|
|
4
|
+
|
|
5
|
+
module Bard
|
|
6
|
+
class DeployStrategy
|
|
7
|
+
class SSH < DeployStrategy
|
|
8
|
+
def deploy(clone: nil, branch: nil, force: false)
|
|
9
|
+
target.require_capability!(:ssh)
|
|
10
|
+
|
|
11
|
+
if clone
|
|
12
|
+
target.run! "git clone git@github.com:botandrosedesign/#{clone} #{target.path}", home: true
|
|
13
|
+
Bard::Copy.file "config/master.key", from: target.config[:local], to: target
|
|
14
|
+
elsif force
|
|
15
|
+
target.run! "git fetch origin #{branch}"
|
|
16
|
+
target.run! "git checkout -f origin/#{branch}"
|
|
17
|
+
else
|
|
18
|
+
branch ||= target.instance_variable_get(:@branch) || "master"
|
|
19
|
+
target.run! "git pull --ff-only origin #{branch}"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
target.run! "bin/setup"
|
|
23
|
+
target.run! "bard setup" if clone
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -39,7 +39,7 @@ module Bard
|
|
|
39
39
|
@target = target
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
-
def deploy
|
|
42
|
+
def deploy(clone: nil, branch: nil, force: false)
|
|
43
43
|
raise NotImplementedError, "Subclasses must implement #deploy"
|
|
44
44
|
end
|
|
45
45
|
|
|
@@ -58,6 +58,3 @@ module Bard
|
|
|
58
58
|
end
|
|
59
59
|
end
|
|
60
60
|
end
|
|
61
|
-
|
|
62
|
-
# Auto-load all strategy files
|
|
63
|
-
Dir[File.join(__dir__, "deploy_strategy", "*.rb")].each { |f| require f }
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
require "bard/plugins/ping"
|
|
2
|
+
require "bard/plugins/git"
|
|
3
|
+
require "bard/command"
|
|
4
|
+
require "bard/plugins/deploy/strategy"
|
|
5
|
+
require "bard/plugins/deploy/ssh_strategy"
|
|
6
|
+
require "bard/plugins/deploy/ci"
|
|
7
|
+
require "bard/plugins/deploy/ci/jenkins"
|
|
8
|
+
require "bard/plugins/deploy/ci/local"
|
|
9
|
+
require "bard/plugins/deploy/ci/github_actions"
|
|
10
|
+
require "tmpdir"
|
|
11
|
+
|
|
12
|
+
class Bard::CLI
|
|
13
|
+
option :"skip-ci", type: :boolean
|
|
14
|
+
option :"local-ci", type: :boolean
|
|
15
|
+
option :ci, type: :string
|
|
16
|
+
option :clone, type: :boolean
|
|
17
|
+
option :target, type: :string, default: "production"
|
|
18
|
+
desc "deploy [BRANCH]", "deploys branch to target (default: current branch to production)"
|
|
19
|
+
def deploy(branch = nil)
|
|
20
|
+
branch ||= Bard::Git.current_branch
|
|
21
|
+
|
|
22
|
+
if branch == "master"
|
|
23
|
+
if !Bard::Git.up_to_date_with_remote?(branch)
|
|
24
|
+
run! "git push origin #{branch}:#{branch}"
|
|
25
|
+
end
|
|
26
|
+
invoke :ci, [branch], options.slice("local-ci", "ci") unless options["skip-ci"] || config.ci == false
|
|
27
|
+
|
|
28
|
+
else
|
|
29
|
+
run! "git fetch origin"
|
|
30
|
+
if Bard::Git.current_branch != "master" && !Bard::Git.in_linked_worktree?
|
|
31
|
+
run! "git fetch origin master:master"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
unless Bard::Git.fast_forward_merge?("origin/master", branch)
|
|
35
|
+
puts "The master branch has advanced. Attempting rebase..."
|
|
36
|
+
if branch == Bard::Git.current_branch
|
|
37
|
+
run! "git rebase origin/master"
|
|
38
|
+
else
|
|
39
|
+
tmpdir = Dir.mktmpdir("bard-rebase")
|
|
40
|
+
begin
|
|
41
|
+
run! "git worktree add --detach #{tmpdir} #{branch}"
|
|
42
|
+
success = Dir.chdir(tmpdir) { system("git rebase origin/master") }
|
|
43
|
+
rebased_sha = Dir.chdir(tmpdir) { `git rev-parse HEAD`.strip } if success
|
|
44
|
+
run! "git worktree remove #{tmpdir} --force"
|
|
45
|
+
unless success
|
|
46
|
+
puts red("!!! ") + "Rebase failed due to conflicts."
|
|
47
|
+
puts "Please rebase #{branch} manually:"
|
|
48
|
+
puts " git checkout #{branch}"
|
|
49
|
+
puts " git rebase origin/master"
|
|
50
|
+
exit 1
|
|
51
|
+
end
|
|
52
|
+
run! "git branch -f #{branch} #{rebased_sha}"
|
|
53
|
+
ensure
|
|
54
|
+
FileUtils.rm_rf(tmpdir) if Dir.exist?(tmpdir)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
run! "git push -f origin #{branch}:#{branch}"
|
|
60
|
+
|
|
61
|
+
invoke :ci, [branch], options.slice("local-ci", "ci") unless options["skip-ci"] || config.ci == false
|
|
62
|
+
|
|
63
|
+
run! "git push origin #{branch}:master"
|
|
64
|
+
if Bard::Git.current_branch == "master"
|
|
65
|
+
run! "git pull --ff-only origin master"
|
|
66
|
+
elsif Bard::Git.in_linked_worktree?
|
|
67
|
+
run! "git fetch origin master"
|
|
68
|
+
else
|
|
69
|
+
run! "git fetch origin master:master"
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
if `git remote` =~ /\bgithub\b/
|
|
74
|
+
run! "git push github"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
to = options[:target].to_sym
|
|
78
|
+
|
|
79
|
+
target = config[to]
|
|
80
|
+
strategy = target.deploy_strategy_instance
|
|
81
|
+
if options[:clone]
|
|
82
|
+
strategy.deploy(clone: project_name)
|
|
83
|
+
else
|
|
84
|
+
strategy.deploy
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
puts green("Deploy Succeeded")
|
|
88
|
+
|
|
89
|
+
if branch != "master"
|
|
90
|
+
puts "Deleting branch: #{branch}"
|
|
91
|
+
run! "git push --delete origin #{branch}"
|
|
92
|
+
|
|
93
|
+
if branch == Bard::Git.current_branch && Bard::Git.in_linked_worktree?
|
|
94
|
+
worktree_path = Dir.pwd
|
|
95
|
+
main_checkout = File.dirname(File.expand_path(`git rev-parse --git-common-dir`.chomp))
|
|
96
|
+
Dir.chdir(main_checkout)
|
|
97
|
+
run! "git worktree remove #{worktree_path}"
|
|
98
|
+
run! "git branch -D #{branch}"
|
|
99
|
+
puts "Worktree removed. Run: cd #{main_checkout}"
|
|
100
|
+
else
|
|
101
|
+
if branch == Bard::Git.current_branch
|
|
102
|
+
run! "git checkout master"
|
|
103
|
+
end
|
|
104
|
+
run! "git branch -D #{branch}"
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
ping to
|
|
109
|
+
rescue Bard::Command::Error => e
|
|
110
|
+
puts red("!!! ") + "Running command failed: #{yellow(e.message)}"
|
|
111
|
+
exit 1
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
desc "stage [branch=HEAD]", "pushes current branch, and stages it"
|
|
115
|
+
def stage(branch = Bard::Git.current_branch)
|
|
116
|
+
if config[:production] == config[:staging]
|
|
117
|
+
raise Thor::Error.new("`bard stage` is disabled until a production target is defined. Until then, please use `bard deploy` to deploy to the staging target.")
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
run! "git push -u origin #{branch}", verbose: true
|
|
121
|
+
|
|
122
|
+
target = config[:staging]
|
|
123
|
+
strategy = target.deploy_strategy_instance
|
|
124
|
+
strategy.deploy(branch: branch, force: true)
|
|
125
|
+
|
|
126
|
+
puts green("Stage Succeeded")
|
|
127
|
+
|
|
128
|
+
ping :staging
|
|
129
|
+
rescue Bard::Command::Error => e
|
|
130
|
+
puts red("!!! ") + "Running command failed: #{yellow(e.message)}"
|
|
131
|
+
exit 1
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
option :"local-ci", type: :boolean
|
|
135
|
+
option :ci, type: :string
|
|
136
|
+
option :status, type: :boolean
|
|
137
|
+
option :resume, type: :boolean
|
|
138
|
+
desc "ci [branch=HEAD]", "runs ci against BRANCH"
|
|
139
|
+
def ci(branch = Bard::Git.current_branch)
|
|
140
|
+
runner_name = if options["local-ci"]
|
|
141
|
+
:local
|
|
142
|
+
elsif options["ci"]
|
|
143
|
+
options["ci"].to_sym
|
|
144
|
+
else
|
|
145
|
+
config.ci
|
|
146
|
+
end
|
|
147
|
+
ci = Bard::CI.new(project_name, branch, runner_name: runner_name)
|
|
148
|
+
unless ci.exists?
|
|
149
|
+
puts red("No CI found for #{project_name}!")
|
|
150
|
+
puts "Re-run with --skip-ci to bypass CI, if you absolutely must, and know what you're doing."
|
|
151
|
+
exit 1
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
return puts ci.status if options["status"]
|
|
155
|
+
|
|
156
|
+
if options["resume"]
|
|
157
|
+
puts "Continuous integration: resuming build..."
|
|
158
|
+
success = ci.resume do |elapsed_time, last_time|
|
|
159
|
+
if last_time
|
|
160
|
+
percentage = (elapsed_time.to_f / last_time.to_f * 100).to_i
|
|
161
|
+
output = " Estimated completion: #{percentage}%"
|
|
162
|
+
else
|
|
163
|
+
output = " No estimated completion time. Elapsed time: #{elapsed_time} sec"
|
|
164
|
+
end
|
|
165
|
+
print "\x08" * output.length
|
|
166
|
+
print output
|
|
167
|
+
$stdout.flush
|
|
168
|
+
end
|
|
169
|
+
else
|
|
170
|
+
puts "Continuous integration: starting build on #{branch}..."
|
|
171
|
+
|
|
172
|
+
success = ci.run do |elapsed_time, last_time|
|
|
173
|
+
if last_time
|
|
174
|
+
percentage = (elapsed_time.to_f / last_time.to_f * 100).to_i
|
|
175
|
+
output = " Estimated completion: #{percentage}%"
|
|
176
|
+
else
|
|
177
|
+
output = " No estimated completion time. Elapsed time: #{elapsed_time} sec"
|
|
178
|
+
end
|
|
179
|
+
print "\x08" * output.length
|
|
180
|
+
print output
|
|
181
|
+
$stdout.flush
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
if success
|
|
186
|
+
puts
|
|
187
|
+
puts "Continuous integration: success!"
|
|
188
|
+
else
|
|
189
|
+
puts
|
|
190
|
+
puts ci.console
|
|
191
|
+
puts red("Automated tests failed!")
|
|
192
|
+
exit 1
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
option :from, default: "production"
|
|
197
|
+
option :to, default: "local"
|
|
198
|
+
desc "master_key --from=production --to=local", "copy master key from from to to"
|
|
199
|
+
def master_key
|
|
200
|
+
from = config[options[:from]]
|
|
201
|
+
to = config[options[:to]]
|
|
202
|
+
Bard::Copy.file "config/master.key", from: from, to: to
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
require "bard/config"
|
|
207
|
+
|
|
208
|
+
class Bard::Config
|
|
209
|
+
def ci(system = nil)
|
|
210
|
+
if system.nil?
|
|
211
|
+
@ci_system
|
|
212
|
+
else
|
|
213
|
+
@ci_system = system
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
require "bard/target"
|
|
219
|
+
|
|
220
|
+
class Bard::Target
|
|
221
|
+
def deploy_strategy
|
|
222
|
+
@deploy_strategy
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def deploy_strategy_instance
|
|
226
|
+
strategy = @deploy_strategy
|
|
227
|
+
strategy ||= :ssh if has_capability?(:ssh)
|
|
228
|
+
raise "No deployment strategy configured for target #{key}" unless strategy
|
|
229
|
+
|
|
230
|
+
strategy_class = Bard::DeployStrategy[strategy]
|
|
231
|
+
raise "Unknown deployment strategy: #{strategy}" unless strategy_class
|
|
232
|
+
|
|
233
|
+
strategy_class.new(self)
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def strategy_options(strategy_name)
|
|
237
|
+
@strategy_options_hash ||= {}
|
|
238
|
+
@strategy_options_hash[strategy_name] || {}
|
|
239
|
+
end
|
|
240
|
+
end
|
|
@@ -21,12 +21,15 @@ module Bard
|
|
|
21
21
|
|
|
22
22
|
def sha_of ref
|
|
23
23
|
sha = `git rev-parse #{ref} 2>/dev/null`.chomp
|
|
24
|
-
return sha if
|
|
24
|
+
return sha if $?.success?
|
|
25
25
|
nil # Branch doesn't exist
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
def
|
|
29
|
-
|
|
28
|
+
def in_linked_worktree?
|
|
29
|
+
git_dir = `git rev-parse --git-dir 2>/dev/null`.chomp
|
|
30
|
+
common_dir = `git rev-parse --git-common-dir 2>/dev/null`.chomp
|
|
31
|
+
return false if git_dir.empty? || common_dir.empty?
|
|
32
|
+
File.expand_path(git_dir) != File.expand_path(common_dir)
|
|
30
33
|
end
|
|
31
34
|
end
|
|
32
35
|
end
|
|
@@ -2,12 +2,12 @@ require "net/http"
|
|
|
2
2
|
require "json"
|
|
3
3
|
require "base64"
|
|
4
4
|
require "rbnacl"
|
|
5
|
-
require "bard/
|
|
5
|
+
require "bard/retryable"
|
|
6
6
|
require "bard/secrets"
|
|
7
7
|
|
|
8
8
|
module Bard
|
|
9
9
|
class Github < Struct.new(:project_name)
|
|
10
|
-
include
|
|
10
|
+
include Retryable
|
|
11
11
|
|
|
12
12
|
def initialize(project_name, api_key: nil)
|
|
13
13
|
super(project_name)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
require "bard/
|
|
2
|
-
require "bard/git"
|
|
1
|
+
require "bard/plugins/deploy/strategy"
|
|
2
|
+
require "bard/plugins/git"
|
|
3
3
|
require "fileutils"
|
|
4
4
|
require "uri"
|
|
5
5
|
|
|
@@ -11,8 +11,7 @@ module Bard
|
|
|
11
11
|
@url = url || target.github_pages
|
|
12
12
|
@options = options
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
target.ping(url) if url
|
|
14
|
+
target.url(url) if url
|
|
16
15
|
end
|
|
17
16
|
|
|
18
17
|
def deploy
|
|
@@ -1,28 +1,24 @@
|
|
|
1
|
-
require "bard/
|
|
1
|
+
require "bard/plugins/github_pages/strategy"
|
|
2
|
+
require "bard/config"
|
|
3
|
+
require "bard/target"
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
|
|
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 = []
|
|
5
|
+
class Bard::Config
|
|
6
|
+
def github_pages(url)
|
|
10
7
|
uri = url.start_with?("http") ? URI.parse(url) : URI.parse("https://#{url}")
|
|
11
8
|
hostname = uri.hostname.sub(/^www\./, "")
|
|
12
|
-
urls = [hostname]
|
|
13
|
-
urls << "www.#{hostname}" if hostname.count(".") < 2
|
|
14
9
|
|
|
10
|
+
remove_target :production
|
|
15
11
|
target :production do
|
|
16
12
|
github_pages url
|
|
17
|
-
|
|
18
|
-
ping(*urls) if urls.any?
|
|
13
|
+
url(hostname) if hostname
|
|
19
14
|
end
|
|
20
15
|
|
|
21
|
-
backup
|
|
16
|
+
backup(false) if respond_to?(:backup)
|
|
22
17
|
end
|
|
18
|
+
end
|
|
23
19
|
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
class Bard::Target
|
|
21
|
+
def github_pages(url = nil)
|
|
26
22
|
if url.nil?
|
|
27
23
|
@github_pages_url
|
|
28
24
|
else
|
data/lib/bard/plugins/hurt.rb
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
class Bard::CLI
|
|
2
|
+
desc "hurt <command>", "reruns a command until it fails"
|
|
3
|
+
def hurt(*args)
|
|
4
|
+
(1..).each do |count|
|
|
5
|
+
puts "Running attempt #{count}"
|
|
6
|
+
system *args
|
|
7
|
+
unless $?.success?
|
|
8
|
+
puts "Ran #{count-1} times before failing"
|
|
9
|
+
break
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
5
13
|
end
|
|
@@ -12,7 +12,7 @@ permissions:
|
|
|
12
12
|
|
|
13
13
|
jobs:
|
|
14
14
|
test:
|
|
15
|
-
runs-on: ubuntu-
|
|
15
|
+
runs-on: ubuntu-24.04${{ github.actor != 'dependabot[bot]' && '-8core' || '' }}
|
|
16
16
|
timeout-minutes: 30
|
|
17
17
|
env:
|
|
18
18
|
RAILS_ENV: test
|
|
@@ -47,7 +47,7 @@ jobs:
|
|
|
47
47
|
|
|
48
48
|
autodeploy-dependabot-prs:
|
|
49
49
|
needs: test
|
|
50
|
-
runs-on: ubuntu-
|
|
50
|
+
runs-on: ubuntu-24.04
|
|
51
51
|
timeout-minutes: 30
|
|
52
52
|
if: github.actor == 'dependabot[bot]'
|
|
53
53
|
steps:
|