bard 2.0.0.beta → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +6 -1
- data/CLAUDE.md +76 -0
- data/MIGRATION_GUIDE.md +24 -9
- data/PLUGINS.md +99 -0
- data/README.md +14 -6
- data/Rakefile +3 -1
- data/bard.gemspec +2 -1
- data/cucumber.yml +1 -0
- data/features/ci.feature +63 -0
- data/features/data.feature +13 -0
- data/features/deploy.feature +14 -0
- data/features/deploy_git_workflow.feature +89 -0
- data/features/run.feature +14 -0
- data/features/step_definitions/bard_steps.rb +136 -0
- data/features/support/bard-coverage +16 -0
- data/features/support/env.rb +14 -39
- data/features/support/test_server.rb +216 -0
- data/lib/bard/cli.rb +14 -31
- data/lib/bard/command.rb +10 -69
- data/lib/bard/config.rb +40 -183
- data/lib/bard/copy.rb +28 -103
- data/lib/bard/plugins/data.rb +56 -0
- data/lib/bard/{ci → plugins/deploy/ci}/github_actions.rb +3 -4
- data/lib/bard/plugins/deploy/ci/jenkins.rb +176 -0
- data/lib/bard/{ci → plugins/deploy/ci}/local.rb +7 -7
- data/lib/bard/{ci → plugins/deploy/ci}/runner.rb +38 -4
- data/lib/bard/plugins/deploy/ci.rb +38 -0
- data/lib/bard/plugins/deploy/ssh_strategy.rb +27 -0
- data/lib/bard/{deploy_strategy.rb → plugins/deploy/strategy.rb} +1 -1
- data/lib/bard/plugins/deploy.rb +240 -0
- data/lib/bard/{git.rb → plugins/git.rb} +6 -3
- data/lib/bard/{github.rb → plugins/github.rb} +4 -6
- data/lib/bard/{deploy_strategy/github_pages.rb → plugins/github_pages/strategy.rb} +13 -6
- data/lib/bard/plugins/github_pages.rb +30 -0
- data/lib/bard/plugins/hurt.rb +13 -0
- data/{install_files → lib/bard/plugins/install}/.github/dependabot.yml +2 -1
- data/{install_files → lib/bard/plugins/install}/.github/workflows/cache-ci.yml +1 -1
- data/{install_files → lib/bard/plugins/install}/.github/workflows/ci.yml +2 -2
- data/lib/bard/plugins/install.rb +9 -0
- data/lib/bard/plugins/open.rb +20 -0
- data/lib/bard/{ping.rb → plugins/ping/check.rb} +4 -4
- data/lib/bard/plugins/ping/target_methods.rb +23 -0
- data/lib/bard/plugins/ping.rb +10 -0
- data/lib/bard/plugins/run.rb +19 -0
- data/lib/bard/plugins/setup.rb +54 -0
- data/lib/bard/plugins/ssh/connection.rb +75 -0
- data/lib/bard/plugins/ssh/copy.rb +95 -0
- data/lib/bard/{ssh_server.rb → plugins/ssh/server.rb} +17 -42
- data/lib/bard/plugins/ssh/target_methods.rb +20 -0
- data/lib/bard/plugins/ssh.rb +10 -0
- data/lib/bard/plugins/url/target_methods.rb +23 -0
- data/lib/bard/plugins/url.rb +1 -0
- data/lib/bard/plugins/vim.rb +6 -0
- data/lib/bard/retryable.rb +25 -0
- data/lib/bard/secrets.rb +10 -0
- data/lib/bard/target.rb +27 -185
- data/lib/bard/version.rb +1 -1
- data/lib/bard.rb +1 -3
- data/spec/acceptance/docker/Dockerfile +3 -2
- data/spec/bard/capability_spec.rb +8 -50
- data/spec/bard/ci/github_actions_spec.rb +117 -14
- data/spec/bard/ci/jenkins_spec.rb +139 -0
- data/spec/bard/ci/runner_spec.rb +61 -0
- data/spec/bard/ci_spec.rb +1 -1
- data/spec/bard/cli/ci_spec.rb +34 -27
- data/spec/bard/cli/data_spec.rb +7 -26
- data/spec/bard/cli/deploy_spec.rb +87 -46
- data/spec/bard/cli/hurt_spec.rb +3 -9
- data/spec/bard/cli/install_spec.rb +5 -11
- data/spec/bard/cli/master_key_spec.rb +5 -19
- data/spec/bard/cli/open_spec.rb +14 -30
- data/spec/bard/cli/ping_spec.rb +8 -23
- data/spec/bard/cli/run_spec.rb +27 -21
- data/spec/bard/cli/setup_spec.rb +10 -27
- data/spec/bard/cli/ssh_spec.rb +10 -25
- data/spec/bard/cli/stage_spec.rb +28 -23
- data/spec/bard/cli/vim_spec.rb +3 -9
- data/spec/bard/command_spec.rb +1 -8
- data/spec/bard/config_spec.rb +78 -98
- data/spec/bard/copy_spec.rb +54 -18
- data/spec/bard/deploy_strategy/ssh_spec.rb +65 -7
- data/spec/bard/deploy_strategy_spec.rb +1 -1
- data/spec/bard/dynamic_dsl_spec.rb +18 -98
- data/spec/bard/git_spec.rb +9 -5
- data/spec/bard/github_spec.rb +2 -2
- data/spec/bard/ping_spec.rb +5 -5
- data/spec/bard/ssh_copy_spec.rb +44 -0
- data/spec/bard/ssh_server_spec.rb +8 -101
- data/spec/bard/target_spec.rb +66 -109
- data/spec/spec_helper.rb +6 -1
- metadata +79 -143
- data/README.rdoc +0 -15
- data/features/bard_check.feature +0 -94
- data/features/bard_deploy.feature +0 -18
- data/features/bard_pull.feature +0 -112
- data/features/bard_push.feature +0 -112
- data/features/podman_testcontainers.feature +0 -16
- data/features/step_definitions/check_steps.rb +0 -47
- data/features/step_definitions/git_steps.rb +0 -73
- data/features/step_definitions/global_steps.rb +0 -56
- data/features/step_definitions/podman_steps.rb +0 -23
- data/features/step_definitions/rails_steps.rb +0 -44
- data/features/step_definitions/submodule_steps.rb +0 -110
- data/features/support/grit_ext.rb +0 -13
- data/features/support/io.rb +0 -32
- data/features/support/podman.rb +0 -153
- data/lib/bard/ci/jenkins.rb +0 -105
- data/lib/bard/ci/retryable.rb +0 -27
- data/lib/bard/ci.rb +0 -50
- data/lib/bard/cli/ci.rb +0 -66
- data/lib/bard/cli/command.rb +0 -26
- data/lib/bard/cli/data.rb +0 -45
- data/lib/bard/cli/deploy.rb +0 -85
- data/lib/bard/cli/hurt.rb +0 -20
- data/lib/bard/cli/install.rb +0 -16
- data/lib/bard/cli/master_key.rb +0 -17
- data/lib/bard/cli/new.rb +0 -101
- data/lib/bard/cli/new_rails_template.rb +0 -197
- data/lib/bard/cli/open.rb +0 -22
- data/lib/bard/cli/ping.rb +0 -18
- data/lib/bard/cli/provision.rb +0 -34
- data/lib/bard/cli/run.rb +0 -24
- data/lib/bard/cli/setup.rb +0 -56
- data/lib/bard/cli/ssh.rb +0 -14
- data/lib/bard/cli/stage.rb +0 -27
- data/lib/bard/cli/vim.rb +0 -13
- data/lib/bard/default_config.rb +0 -35
- data/lib/bard/deploy_strategy/ssh.rb +0 -19
- data/lib/bard/github_pages.rb +0 -134
- data/lib/bard/provision/app.rb +0 -10
- data/lib/bard/provision/apt.rb +0 -16
- data/lib/bard/provision/authorizedkeys.rb +0 -25
- data/lib/bard/provision/data.rb +0 -27
- data/lib/bard/provision/deploy.rb +0 -10
- data/lib/bard/provision/http.rb +0 -16
- data/lib/bard/provision/logrotation.rb +0 -30
- data/lib/bard/provision/masterkey.rb +0 -18
- data/lib/bard/provision/mysql.rb +0 -22
- data/lib/bard/provision/passenger.rb +0 -37
- data/lib/bard/provision/repo.rb +0 -72
- data/lib/bard/provision/rvm.rb +0 -22
- data/lib/bard/provision/ssh.rb +0 -72
- data/lib/bard/provision/swapfile.rb +0 -21
- data/lib/bard/provision/user.rb +0 -42
- data/lib/bard/provision.rb +0 -16
- data/lib/bard/server.rb +0 -117
- data/spec/bard/cli/command_spec.rb +0 -50
- data/spec/bard/cli/new_spec.rb +0 -73
- data/spec/bard/cli/provision_spec.rb +0 -42
- data/spec/bard/github_pages_spec.rb +0 -143
- data/spec/bard/provision/app_spec.rb +0 -33
- data/spec/bard/provision/apt_spec.rb +0 -39
- data/spec/bard/provision/authorizedkeys_spec.rb +0 -40
- data/spec/bard/provision/data_spec.rb +0 -54
- data/spec/bard/provision/deploy_spec.rb +0 -33
- data/spec/bard/provision/http_spec.rb +0 -57
- data/spec/bard/provision/logrotation_spec.rb +0 -34
- data/spec/bard/provision/masterkey_spec.rb +0 -63
- data/spec/bard/provision/mysql_spec.rb +0 -55
- data/spec/bard/provision/passenger_spec.rb +0 -81
- data/spec/bard/provision/repo_spec.rb +0 -208
- data/spec/bard/provision/rvm_spec.rb +0 -49
- data/spec/bard/provision/ssh_spec.rb +0 -229
- data/spec/bard/provision/swapfile_spec.rb +0 -32
- data/spec/bard/provision/user_spec.rb +0 -103
- data/spec/bard/provision_spec.rb +0 -28
- data/spec/bard/server_spec.rb +0 -127
- /data/lib/bard/{ci → plugins/deploy/ci}/state.rb +0 -0
- /data/{install_files → lib/bard/plugins/install}/apt_dependencies.rb +0 -0
- /data/{install_files → lib/bard/plugins/install}/ci +0 -0
- /data/{install_files → lib/bard/plugins/install}/setup +0 -0
- /data/{install_files → lib/bard/plugins/install}/specified_bundler.rb +0 -0
- /data/{install_files → lib/bard/plugins/install}/specified_ruby.rb +0 -0
data/lib/bard/config.rb
CHANGED
|
@@ -1,222 +1,79 @@
|
|
|
1
|
-
require "bard/server"
|
|
2
1
|
require "bard/target"
|
|
2
|
+
require "bard/plugins/ssh/target_methods"
|
|
3
|
+
require "bard/plugins/ping/target_methods"
|
|
3
4
|
|
|
4
5
|
module Bard
|
|
5
6
|
class Config
|
|
6
|
-
def self.current
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
def self.current
|
|
8
|
+
new(detect_project_name, path: "bard.rb")
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.detect_project_name
|
|
12
|
+
git_common_dir = `git rev-parse --git-common-dir 2>/dev/null`.chomp
|
|
13
|
+
dirname = if $?.success? && !git_common_dir.empty?
|
|
14
|
+
File.dirname(File.expand_path(git_common_dir))
|
|
15
|
+
else
|
|
16
|
+
Dir.getwd
|
|
17
|
+
end
|
|
18
|
+
File.basename(dirname)
|
|
10
19
|
end
|
|
11
20
|
|
|
12
21
|
attr_reader :project_name, :targets
|
|
13
22
|
|
|
14
23
|
def initialize(project_name = nil, path: nil, source: nil)
|
|
15
|
-
# Support both positional and keyword argument for project_name
|
|
16
24
|
@project_name = project_name
|
|
17
|
-
@
|
|
18
|
-
@data_paths = []
|
|
19
|
-
@backup = nil
|
|
20
|
-
@ci_system = nil
|
|
21
|
-
|
|
22
|
-
# Load default configuration (creates Server instances for backward compat)
|
|
25
|
+
@targets = {}
|
|
23
26
|
load_defaults if project_name
|
|
24
27
|
|
|
25
|
-
# Load user configuration
|
|
26
28
|
if path && File.exist?(path)
|
|
27
29
|
source = File.read(path)
|
|
28
30
|
end
|
|
29
31
|
if source
|
|
30
|
-
instance_eval(source)
|
|
32
|
+
instance_eval(source, path)
|
|
31
33
|
end
|
|
32
34
|
end
|
|
33
35
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
@servers
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
# New v2.0 accessor (same as servers)
|
|
40
|
-
def targets
|
|
41
|
-
@servers
|
|
36
|
+
def remove_target(key)
|
|
37
|
+
@targets.delete(key.to_sym)
|
|
42
38
|
end
|
|
43
39
|
|
|
44
|
-
# Old v1.x API - creates Server instances
|
|
45
|
-
def server(key, &block)
|
|
46
|
-
key = key.to_sym
|
|
47
|
-
@servers[key] = Server.define(project_name, key, &block)
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
# New v2.0 API - creates Target instances
|
|
51
40
|
def target(key, &block)
|
|
52
41
|
key = key.to_sym
|
|
53
|
-
@
|
|
54
|
-
|
|
55
|
-
@servers[key]
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
# Get a server/target by key
|
|
59
|
-
def [](key)
|
|
60
|
-
key = key.to_sym
|
|
61
|
-
# Fallback to staging if production not defined
|
|
62
|
-
if @servers[key].nil? && key == :production
|
|
63
|
-
key = :staging
|
|
42
|
+
unless @targets[key].is_a?(Target)
|
|
43
|
+
@targets[key] = Target.new(key, self)
|
|
64
44
|
end
|
|
65
|
-
@
|
|
45
|
+
@targets[key].instance_eval(&block) if block
|
|
46
|
+
@targets[key]
|
|
66
47
|
end
|
|
67
48
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if paths.empty?
|
|
71
|
-
@data_paths
|
|
72
|
-
else
|
|
73
|
-
@data_paths = paths
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def data_paths
|
|
78
|
-
@data_paths
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
def backup(value = nil, &block)
|
|
82
|
-
if block_given?
|
|
83
|
-
@backup = BackupConfig.new(&block)
|
|
84
|
-
elsif value == false
|
|
85
|
-
@backup = BackupConfig.new { disabled }
|
|
86
|
-
elsif value.nil? # Getter
|
|
87
|
-
@backup ||= BackupConfig.new { bard }
|
|
88
|
-
else
|
|
89
|
-
raise ArgumentError, "backup accepts false or a block"
|
|
90
|
-
end
|
|
49
|
+
def [](key)
|
|
50
|
+
@targets[key.to_sym]
|
|
91
51
|
end
|
|
92
52
|
|
|
93
|
-
|
|
94
|
-
backup == true
|
|
95
|
-
end
|
|
53
|
+
private
|
|
96
54
|
|
|
97
|
-
def
|
|
98
|
-
|
|
99
|
-
uri = url.start_with?("http") ? URI.parse(url) : URI.parse("https://#{url}")
|
|
100
|
-
hostname = uri.hostname.sub(/^www\./, '')
|
|
101
|
-
urls = [hostname]
|
|
102
|
-
if hostname.count(".") < 2
|
|
103
|
-
urls << "www.#{hostname}"
|
|
104
|
-
end
|
|
55
|
+
def load_defaults
|
|
56
|
+
target :local
|
|
105
57
|
|
|
106
|
-
target :
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
58
|
+
target :gubs do
|
|
59
|
+
ssh "botandrose@cloud.hackett.world:22022",
|
|
60
|
+
path: "Sites/#{config.project_name}"
|
|
61
|
+
url false
|
|
110
62
|
end
|
|
111
63
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
def ci(system = nil)
|
|
117
|
-
if system.nil?
|
|
118
|
-
@ci_system
|
|
119
|
-
else
|
|
120
|
-
@ci_system = system
|
|
64
|
+
target :ci do
|
|
65
|
+
ssh "jenkins@staging.botandrose.com:22022",
|
|
66
|
+
path: "jobs/#{config.project_name}/workspace"
|
|
67
|
+
url false
|
|
121
68
|
end
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
def ci_system
|
|
125
|
-
@ci_system
|
|
126
|
-
end
|
|
127
69
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
require "bard/ci"
|
|
132
|
-
|
|
133
|
-
# Use the existing CI class which handles auto-detection
|
|
134
|
-
case @ci_system
|
|
135
|
-
when :local
|
|
136
|
-
CI.new(project_name, branch, local: true)
|
|
137
|
-
when :github_actions, :jenkins, nil
|
|
138
|
-
# CI class auto-detects between github_actions and jenkins
|
|
139
|
-
CI.new(project_name, branch)
|
|
140
|
-
when false
|
|
141
|
-
nil
|
|
142
|
-
else
|
|
143
|
-
CI.new(project_name, branch)
|
|
70
|
+
staging_defaults = proc do
|
|
71
|
+
ssh "www@staging.botandrose.com:22022"
|
|
72
|
+
url "#{config.project_name}.botandrose.com"
|
|
144
73
|
end
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
private
|
|
148
|
-
|
|
149
|
-
# Load default server configurations (v1.x compatible)
|
|
150
|
-
def load_defaults
|
|
151
|
-
@servers[:local] = Server.new(
|
|
152
|
-
project_name,
|
|
153
|
-
:local,
|
|
154
|
-
false,
|
|
155
|
-
"./",
|
|
156
|
-
["#{project_name}.local"],
|
|
157
|
-
)
|
|
158
|
-
@servers[:gubs] = Server.new(
|
|
159
|
-
project_name,
|
|
160
|
-
:gubs,
|
|
161
|
-
"botandrose@cloud.hackett.world:22022",
|
|
162
|
-
"Sites/#{project_name}",
|
|
163
|
-
false,
|
|
164
|
-
)
|
|
165
|
-
@servers[:ci] = Server.new(
|
|
166
|
-
project_name,
|
|
167
|
-
:ci,
|
|
168
|
-
"jenkins@staging.botandrose.com:22022",
|
|
169
|
-
"jobs/#{project_name}/workspace",
|
|
170
|
-
false,
|
|
171
|
-
)
|
|
172
|
-
@servers[:staging] = Server.new(
|
|
173
|
-
project_name,
|
|
174
|
-
:staging,
|
|
175
|
-
"www@staging.botandrose.com:22022",
|
|
176
|
-
project_name,
|
|
177
|
-
["#{project_name}.botandrose.com"],
|
|
178
|
-
)
|
|
179
|
-
end
|
|
180
|
-
end
|
|
181
|
-
|
|
182
|
-
class BackupConfig
|
|
183
|
-
attr_reader :destinations
|
|
184
|
-
|
|
185
|
-
def initialize(&block)
|
|
186
|
-
@destinations = []
|
|
187
|
-
instance_eval(&block) if block_given?
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
def bard
|
|
191
|
-
@bard = true
|
|
192
|
-
end
|
|
193
|
-
|
|
194
|
-
def bard?
|
|
195
|
-
!!@bard
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
def disabled
|
|
199
|
-
@disabled = true
|
|
200
|
-
end
|
|
201
|
-
|
|
202
|
-
def disabled?
|
|
203
|
-
!!@disabled
|
|
204
|
-
end
|
|
205
|
-
|
|
206
|
-
def enabled?
|
|
207
|
-
!disabled?
|
|
208
|
-
end
|
|
209
|
-
|
|
210
|
-
def s3(name, **kwargs)
|
|
211
|
-
@destinations << {
|
|
212
|
-
name: name,
|
|
213
|
-
type: :s3,
|
|
214
|
-
**kwargs,
|
|
215
|
-
}
|
|
216
|
-
end
|
|
217
74
|
|
|
218
|
-
|
|
219
|
-
|
|
75
|
+
target :staging, &staging_defaults
|
|
76
|
+
target :production, &staging_defaults
|
|
220
77
|
end
|
|
221
78
|
end
|
|
222
79
|
end
|
data/lib/bard/copy.rb
CHANGED
|
@@ -1,120 +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
|
-
|
|
28
|
-
gateway = ssh_server.gateway ? "-oProxyCommand='ssh #{ssh_server.gateway} -W %h:%p'" : ""
|
|
29
10
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
from_and_to = [path, target_or_server.scp_uri(path)]
|
|
33
|
-
from_and_to.reverse! if direction == :from
|
|
34
|
-
|
|
35
|
-
command = ["scp", gateway, ssh_key, *from_and_to].join(" ")
|
|
36
|
-
Bard::Command.run! command, verbose: verbose
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def scp_as_mediator
|
|
40
|
-
from_server = from.respond_to?(:server) ? from.server : from
|
|
41
|
-
to_server = to.respond_to?(:server) ? to.server : to
|
|
42
|
-
|
|
43
|
-
raise NotImplementedError if from_server.gateway || to_server.gateway || from_server.ssh_key || to_server.ssh_key
|
|
44
|
-
command = "scp -o ForwardAgent=yes #{from.scp_uri(path)} #{to.scp_uri(path)}"
|
|
45
|
-
Bard::Command.run! command, verbose: verbose
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def rsync
|
|
49
|
-
if from.key == :local
|
|
50
|
-
rsync_using_local :to, to
|
|
51
|
-
elsif to.key == :local
|
|
52
|
-
rsync_using_local :from, from
|
|
53
|
-
else
|
|
54
|
-
rsync_as_mediator
|
|
11
|
+
def file(path, from:, to:, verbose: false)
|
|
12
|
+
handler_for!(from, to).new(path, from, to, verbose).file
|
|
55
13
|
end
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def rsync_using_local direction, target_or_server
|
|
59
|
-
# Support both new Target (with server attribute) and old Server
|
|
60
|
-
ssh_server = target_or_server.respond_to?(:server) ? target_or_server.server : target_or_server
|
|
61
14
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
if ssh_uri_value.respond_to?(:port)
|
|
65
|
-
# Already a URI-like object (old Server or mock)
|
|
66
|
-
ssh_uri = ssh_uri_value
|
|
67
|
-
elsif ssh_uri_value.is_a?(String)
|
|
68
|
-
# String from new SSHServer
|
|
69
|
-
ssh_uri = URI("ssh://#{ssh_uri_value}")
|
|
70
|
-
else
|
|
71
|
-
# Fallback
|
|
72
|
-
ssh_uri = ssh_uri_value
|
|
15
|
+
def dir(path, from:, to:, verbose: false)
|
|
16
|
+
handler_for!(from, to).new(path, from, to, verbose).dir
|
|
73
17
|
end
|
|
74
18
|
|
|
75
|
-
|
|
19
|
+
private
|
|
76
20
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
from_and_to[-1].sub! %r(/[^/]+$), '/'
|
|
83
|
-
|
|
84
|
-
command = "rsync #{ssh} --delete --info=progress2 -az #{from_and_to.join(" ")}"
|
|
85
|
-
Bard::Command.run! command, verbose: verbose
|
|
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
|
|
25
|
+
end
|
|
86
26
|
end
|
|
87
27
|
|
|
88
|
-
|
|
89
|
-
from_server = from.respond_to?(:server) ? from.server : from
|
|
90
|
-
to_server = to.respond_to?(:server) ? to.server : to
|
|
28
|
+
attr_reader :path, :from, :to, :verbose
|
|
91
29
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
elsif from_uri_value.is_a?(String)
|
|
99
|
-
from_uri = URI("ssh://#{from_uri_value}")
|
|
100
|
-
else
|
|
101
|
-
from_uri = from_uri_value
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
to_uri_value = to_server.respond_to?(:ssh_uri) ? to_server.ssh_uri : nil
|
|
105
|
-
if to_uri_value.respond_to?(:port)
|
|
106
|
-
to_uri = to_uri_value
|
|
107
|
-
elsif to_uri_value.is_a?(String)
|
|
108
|
-
to_uri = URI("ssh://#{to_uri_value}")
|
|
109
|
-
else
|
|
110
|
-
to_uri = to_uri_value
|
|
111
|
-
end
|
|
30
|
+
def initialize(path, from, to, verbose)
|
|
31
|
+
@path = path
|
|
32
|
+
@from = from
|
|
33
|
+
@to = to
|
|
34
|
+
@verbose = verbose
|
|
35
|
+
end
|
|
112
36
|
|
|
113
|
-
|
|
114
|
-
|
|
37
|
+
def file
|
|
38
|
+
raise NotImplementedError
|
|
39
|
+
end
|
|
115
40
|
|
|
116
|
-
|
|
117
|
-
|
|
41
|
+
def dir
|
|
42
|
+
raise NotImplementedError
|
|
118
43
|
end
|
|
119
44
|
end
|
|
120
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,13 +1,12 @@
|
|
|
1
1
|
require "time"
|
|
2
|
-
require "bard/github"
|
|
3
|
-
require "bard/ci/runner"
|
|
2
|
+
require "bard/plugins/github"
|
|
3
|
+
require "bard/plugins/deploy/ci/runner"
|
|
4
4
|
|
|
5
5
|
module Bard
|
|
6
6
|
class CI
|
|
7
7
|
class GithubActions < Runner
|
|
8
|
-
|
|
9
8
|
def exists?
|
|
10
|
-
|
|
9
|
+
File.exist?(".github/workflows/ci.yml")
|
|
11
10
|
end
|
|
12
11
|
|
|
13
12
|
def console
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
require "bard/plugins/deploy/ci/runner"
|
|
3
|
+
require "bard/secrets"
|
|
4
|
+
|
|
5
|
+
module Bard
|
|
6
|
+
class CI
|
|
7
|
+
class Jenkins < Runner
|
|
8
|
+
def exists?
|
|
9
|
+
`curl -s -I #{ci_host}/` =~ /\b200 OK\b/ or create!
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private def create!
|
|
13
|
+
git_url = `git remote get-url origin`.strip
|
|
14
|
+
config = JOB_CONFIG_XML.sub("GIT_URL", git_url)
|
|
15
|
+
if File.exist?("config/master.key")
|
|
16
|
+
master_key = File.read("config/master.key").strip
|
|
17
|
+
master_key_step = <<~XML.chomp
|
|
18
|
+
<hudson.tasks.Shell>
|
|
19
|
+
<command>echo #{master_key} > config/master.key</command>
|
|
20
|
+
</hudson.tasks.Shell>
|
|
21
|
+
|
|
22
|
+
XML
|
|
23
|
+
config = config.sub("<builders>\n", "<builders>\n #{master_key_step}")
|
|
24
|
+
end
|
|
25
|
+
`curl -s -X POST "http://#{auth}@ci.botandrose.com/createItem?name=#{project_name}" -H "Content-Type: application/xml" -d '#{config}'`
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def console
|
|
29
|
+
raw = `curl -s #{ci_host}/lastBuild/console`
|
|
30
|
+
raw[%r{<pre.*?>(.+)</pre>}m, 1]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
attr_accessor :last_response
|
|
34
|
+
|
|
35
|
+
protected
|
|
36
|
+
|
|
37
|
+
def wait_until_started
|
|
38
|
+
sleep(2) until started?
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def start
|
|
42
|
+
command = "curl -s -I -X POST -L '#{ci_host}/buildWithParameters?GIT_REF=#{branch}'"
|
|
43
|
+
output = `#{command}`
|
|
44
|
+
@queueId = output[%r{Location: .+/queue/item/(\d+)/}, 1].to_i
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def building?
|
|
48
|
+
retry_with_backoff do
|
|
49
|
+
self.last_response = `curl -s #{ci_host}/#{job_id}/api/json?tree=building,result`
|
|
50
|
+
raise "Blank response from CI" if last_response.empty?
|
|
51
|
+
end
|
|
52
|
+
last_response.include? '"building":true'
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def success?
|
|
56
|
+
last_response.include? '"result":"SUCCESS"'
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def get_state_data
|
|
60
|
+
{
|
|
61
|
+
"project_name" => project_name,
|
|
62
|
+
"branch" => branch,
|
|
63
|
+
"queue_id" => @queueId,
|
|
64
|
+
"job_id" => @job_id,
|
|
65
|
+
"start_time" => @start_time,
|
|
66
|
+
"last_time_elapsed" => @last_time_elapsed
|
|
67
|
+
}
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def restore_state(data)
|
|
71
|
+
@queueId = data["queue_id"]
|
|
72
|
+
@job_id = data["job_id"]
|
|
73
|
+
@start_time = data["start_time"]
|
|
74
|
+
@last_time_elapsed = data["last_time_elapsed"]
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def get_last_time_elapsed
|
|
80
|
+
response = retry_with_backoff do
|
|
81
|
+
response = `curl -s #{ci_host}/lastStableBuild/api/xml`
|
|
82
|
+
raise "Blank response from CI" if response.empty?
|
|
83
|
+
response
|
|
84
|
+
end
|
|
85
|
+
response.match(/<duration>(\d+)<\/duration>/)
|
|
86
|
+
$1 ? $1.to_i / 1000 : nil
|
|
87
|
+
rescue => e
|
|
88
|
+
puts " Warning: Could not get last build duration: #{e.message}"
|
|
89
|
+
nil
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def auth
|
|
93
|
+
@auth ||= "#{Bard::Secrets.fetch("jenkins-user")}:#{Bard::Secrets.fetch("jenkins-token")}"
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def ci_host
|
|
97
|
+
"http://#{auth}@ci.botandrose.com/job/#{project_name}"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def started?
|
|
101
|
+
retry_with_backoff do
|
|
102
|
+
command = "curl -s -g '#{ci_host}/api/json?depth=1&tree=builds[queueId,number]'"
|
|
103
|
+
output = `#{command}`
|
|
104
|
+
raise "Blank response from CI" if output.empty?
|
|
105
|
+
builds = JSON.parse(output)["builds"]
|
|
106
|
+
raise "Build not found in builds list" if builds.empty?
|
|
107
|
+
builds.first["queueId"] == @queueId
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def job_id
|
|
112
|
+
@job_id ||= begin
|
|
113
|
+
retry_with_backoff do
|
|
114
|
+
output = `curl -s -g '#{ci_host}/api/json?depth=1&tree=builds[queueId,number]'`
|
|
115
|
+
raise "Blank response from CI" if output.empty?
|
|
116
|
+
builds = JSON.parse(output)["builds"]
|
|
117
|
+
build = builds.find { |b| b["queueId"] == @queueId }
|
|
118
|
+
build["number"]
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
JOB_CONFIG_XML = <<~XML
|
|
123
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
124
|
+
<project>
|
|
125
|
+
<actions/>
|
|
126
|
+
<description></description>
|
|
127
|
+
<keepDependencies>false</keepDependencies>
|
|
128
|
+
<properties>
|
|
129
|
+
<hudson.model.ParametersDefinitionProperty>
|
|
130
|
+
<parameterDefinitions>
|
|
131
|
+
<hudson.model.StringParameterDefinition>
|
|
132
|
+
<name>GIT_REF</name>
|
|
133
|
+
<description></description>
|
|
134
|
+
<defaultValue>master</defaultValue>
|
|
135
|
+
</hudson.model.StringParameterDefinition>
|
|
136
|
+
</parameterDefinitions>
|
|
137
|
+
</hudson.model.ParametersDefinitionProperty>
|
|
138
|
+
</properties>
|
|
139
|
+
<scm class="hudson.plugins.git.GitSCM" plugin="git@3.3.0">
|
|
140
|
+
<configVersion>2</configVersion>
|
|
141
|
+
<userRemoteConfigs>
|
|
142
|
+
<hudson.plugins.git.UserRemoteConfig>
|
|
143
|
+
<url>GIT_URL</url>
|
|
144
|
+
</hudson.plugins.git.UserRemoteConfig>
|
|
145
|
+
</userRemoteConfigs>
|
|
146
|
+
<branches>
|
|
147
|
+
<hudson.plugins.git.BranchSpec>
|
|
148
|
+
<name>$GIT_REF</name>
|
|
149
|
+
</hudson.plugins.git.BranchSpec>
|
|
150
|
+
</branches>
|
|
151
|
+
<doGenerateSubmoduleConfigurations>false</doGenerateSubmoduleConfigurations>
|
|
152
|
+
<submoduleCfg class="list"/>
|
|
153
|
+
<extensions/>
|
|
154
|
+
</scm>
|
|
155
|
+
<canRoam>true</canRoam>
|
|
156
|
+
<disabled>false</disabled>
|
|
157
|
+
<blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
|
|
158
|
+
<blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
|
|
159
|
+
<triggers/>
|
|
160
|
+
<concurrentBuild>false</concurrentBuild>
|
|
161
|
+
<builders>
|
|
162
|
+
<hudson.tasks.Shell>
|
|
163
|
+
<command>bash -l -c bin/setup</command>
|
|
164
|
+
</hudson.tasks.Shell>
|
|
165
|
+
<hudson.tasks.Shell>
|
|
166
|
+
<command>bash -l -c bin/ci</command>
|
|
167
|
+
</hudson.tasks.Shell>
|
|
168
|
+
</builders>
|
|
169
|
+
<publishers/>
|
|
170
|
+
<buildWrappers/>
|
|
171
|
+
</project>
|
|
172
|
+
XML
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
require "
|
|
2
|
-
require "bard/ci/runner"
|
|
1
|
+
require "tempfile"
|
|
2
|
+
require "bard/plugins/deploy/ci/runner"
|
|
3
3
|
|
|
4
4
|
module Bard
|
|
5
5
|
class CI
|
|
6
6
|
class Local < Runner
|
|
7
|
-
|
|
8
7
|
def exists?
|
|
9
8
|
true
|
|
10
9
|
end
|
|
@@ -16,7 +15,8 @@ module Bard
|
|
|
16
15
|
protected
|
|
17
16
|
|
|
18
17
|
def start
|
|
19
|
-
@
|
|
18
|
+
@output_file = Tempfile.new("bard-ci")
|
|
19
|
+
@wait_thread = Process.detach(spawn("CLEAN=true bin/rake ci", [:out, :err] => @output_file))
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def building?
|
|
@@ -47,9 +47,9 @@ module Bard
|
|
|
47
47
|
sleep(2)
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
-
@
|
|
51
|
-
@console = @
|
|
52
|
-
@
|
|
50
|
+
@output_file.rewind
|
|
51
|
+
@console = @output_file.read
|
|
52
|
+
@output_file.close!
|
|
53
53
|
end
|
|
54
54
|
end
|
|
55
55
|
end
|