bard 2.0.0.beta → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +6 -1
  3. data/CLAUDE.md +76 -0
  4. data/MIGRATION_GUIDE.md +24 -9
  5. data/PLUGINS.md +99 -0
  6. data/README.md +14 -6
  7. data/Rakefile +3 -1
  8. data/bard.gemspec +2 -1
  9. data/cucumber.yml +1 -0
  10. data/features/ci.feature +63 -0
  11. data/features/data.feature +13 -0
  12. data/features/deploy.feature +14 -0
  13. data/features/deploy_git_workflow.feature +89 -0
  14. data/features/run.feature +14 -0
  15. data/features/step_definitions/bard_steps.rb +136 -0
  16. data/features/support/bard-coverage +16 -0
  17. data/features/support/env.rb +14 -39
  18. data/features/support/test_server.rb +216 -0
  19. data/lib/bard/cli.rb +14 -31
  20. data/lib/bard/command.rb +10 -69
  21. data/lib/bard/config.rb +40 -183
  22. data/lib/bard/copy.rb +28 -103
  23. data/lib/bard/plugins/data.rb +56 -0
  24. data/lib/bard/{ci → plugins/deploy/ci}/github_actions.rb +3 -4
  25. data/lib/bard/plugins/deploy/ci/jenkins.rb +176 -0
  26. data/lib/bard/{ci → plugins/deploy/ci}/local.rb +7 -7
  27. data/lib/bard/{ci → plugins/deploy/ci}/runner.rb +38 -4
  28. data/lib/bard/plugins/deploy/ci.rb +38 -0
  29. data/lib/bard/plugins/deploy/ssh_strategy.rb +27 -0
  30. data/lib/bard/{deploy_strategy.rb → plugins/deploy/strategy.rb} +1 -1
  31. data/lib/bard/plugins/deploy.rb +240 -0
  32. data/lib/bard/{git.rb → plugins/git.rb} +6 -3
  33. data/lib/bard/{github.rb → plugins/github.rb} +4 -6
  34. data/lib/bard/{deploy_strategy/github_pages.rb → plugins/github_pages/strategy.rb} +41 -13
  35. data/lib/bard/plugins/github_pages.rb +30 -0
  36. data/lib/bard/plugins/hurt.rb +13 -0
  37. data/{install_files → lib/bard/plugins/install}/.github/dependabot.yml +2 -1
  38. data/{install_files → lib/bard/plugins/install}/.github/workflows/cache-ci.yml +1 -1
  39. data/{install_files → lib/bard/plugins/install}/.github/workflows/ci.yml +2 -2
  40. data/lib/bard/plugins/install.rb +9 -0
  41. data/lib/bard/plugins/open.rb +20 -0
  42. data/lib/bard/{ping.rb → plugins/ping/check.rb} +4 -4
  43. data/lib/bard/plugins/ping/target_methods.rb +23 -0
  44. data/lib/bard/plugins/ping.rb +10 -0
  45. data/lib/bard/plugins/run.rb +19 -0
  46. data/lib/bard/plugins/setup.rb +54 -0
  47. data/lib/bard/plugins/ssh/connection.rb +75 -0
  48. data/lib/bard/plugins/ssh/copy.rb +95 -0
  49. data/lib/bard/{ssh_server.rb → plugins/ssh/server.rb} +17 -42
  50. data/lib/bard/plugins/ssh/target_methods.rb +20 -0
  51. data/lib/bard/plugins/ssh.rb +10 -0
  52. data/lib/bard/plugins/url/target_methods.rb +23 -0
  53. data/lib/bard/plugins/url.rb +1 -0
  54. data/lib/bard/plugins/vim.rb +6 -0
  55. data/lib/bard/retryable.rb +25 -0
  56. data/lib/bard/secrets.rb +10 -0
  57. data/lib/bard/target.rb +27 -185
  58. data/lib/bard/version.rb +1 -1
  59. data/lib/bard.rb +1 -3
  60. data/spec/acceptance/docker/Dockerfile +3 -2
  61. data/spec/bard/capability_spec.rb +8 -50
  62. data/spec/bard/ci/github_actions_spec.rb +117 -14
  63. data/spec/bard/ci/jenkins_spec.rb +139 -0
  64. data/spec/bard/ci/runner_spec.rb +61 -0
  65. data/spec/bard/ci_spec.rb +1 -1
  66. data/spec/bard/cli/ci_spec.rb +34 -27
  67. data/spec/bard/cli/data_spec.rb +7 -26
  68. data/spec/bard/cli/deploy_spec.rb +87 -46
  69. data/spec/bard/cli/hurt_spec.rb +3 -9
  70. data/spec/bard/cli/install_spec.rb +5 -11
  71. data/spec/bard/cli/master_key_spec.rb +5 -19
  72. data/spec/bard/cli/open_spec.rb +14 -30
  73. data/spec/bard/cli/ping_spec.rb +8 -23
  74. data/spec/bard/cli/run_spec.rb +27 -21
  75. data/spec/bard/cli/setup_spec.rb +10 -27
  76. data/spec/bard/cli/ssh_spec.rb +10 -25
  77. data/spec/bard/cli/stage_spec.rb +28 -23
  78. data/spec/bard/cli/vim_spec.rb +3 -9
  79. data/spec/bard/command_spec.rb +1 -8
  80. data/spec/bard/config_spec.rb +78 -98
  81. data/spec/bard/copy_spec.rb +54 -18
  82. data/spec/bard/deploy_strategy/ssh_spec.rb +65 -7
  83. data/spec/bard/deploy_strategy_spec.rb +1 -1
  84. data/spec/bard/dynamic_dsl_spec.rb +18 -98
  85. data/spec/bard/git_spec.rb +9 -5
  86. data/spec/bard/github_spec.rb +2 -2
  87. data/spec/bard/ping_spec.rb +5 -5
  88. data/spec/bard/ssh_copy_spec.rb +44 -0
  89. data/spec/bard/ssh_server_spec.rb +8 -101
  90. data/spec/bard/target_spec.rb +66 -109
  91. data/spec/spec_helper.rb +6 -1
  92. metadata +79 -143
  93. data/README.rdoc +0 -15
  94. data/features/bard_check.feature +0 -94
  95. data/features/bard_deploy.feature +0 -18
  96. data/features/bard_pull.feature +0 -112
  97. data/features/bard_push.feature +0 -112
  98. data/features/podman_testcontainers.feature +0 -16
  99. data/features/step_definitions/check_steps.rb +0 -47
  100. data/features/step_definitions/git_steps.rb +0 -73
  101. data/features/step_definitions/global_steps.rb +0 -56
  102. data/features/step_definitions/podman_steps.rb +0 -23
  103. data/features/step_definitions/rails_steps.rb +0 -44
  104. data/features/step_definitions/submodule_steps.rb +0 -110
  105. data/features/support/grit_ext.rb +0 -13
  106. data/features/support/io.rb +0 -32
  107. data/features/support/podman.rb +0 -153
  108. data/lib/bard/ci/jenkins.rb +0 -105
  109. data/lib/bard/ci/retryable.rb +0 -27
  110. data/lib/bard/ci.rb +0 -50
  111. data/lib/bard/cli/ci.rb +0 -66
  112. data/lib/bard/cli/command.rb +0 -26
  113. data/lib/bard/cli/data.rb +0 -45
  114. data/lib/bard/cli/deploy.rb +0 -85
  115. data/lib/bard/cli/hurt.rb +0 -20
  116. data/lib/bard/cli/install.rb +0 -16
  117. data/lib/bard/cli/master_key.rb +0 -17
  118. data/lib/bard/cli/new.rb +0 -101
  119. data/lib/bard/cli/new_rails_template.rb +0 -197
  120. data/lib/bard/cli/open.rb +0 -22
  121. data/lib/bard/cli/ping.rb +0 -18
  122. data/lib/bard/cli/provision.rb +0 -34
  123. data/lib/bard/cli/run.rb +0 -24
  124. data/lib/bard/cli/setup.rb +0 -56
  125. data/lib/bard/cli/ssh.rb +0 -14
  126. data/lib/bard/cli/stage.rb +0 -27
  127. data/lib/bard/cli/vim.rb +0 -13
  128. data/lib/bard/default_config.rb +0 -35
  129. data/lib/bard/deploy_strategy/ssh.rb +0 -19
  130. data/lib/bard/github_pages.rb +0 -134
  131. data/lib/bard/provision/app.rb +0 -10
  132. data/lib/bard/provision/apt.rb +0 -16
  133. data/lib/bard/provision/authorizedkeys.rb +0 -25
  134. data/lib/bard/provision/data.rb +0 -27
  135. data/lib/bard/provision/deploy.rb +0 -10
  136. data/lib/bard/provision/http.rb +0 -16
  137. data/lib/bard/provision/logrotation.rb +0 -30
  138. data/lib/bard/provision/masterkey.rb +0 -18
  139. data/lib/bard/provision/mysql.rb +0 -22
  140. data/lib/bard/provision/passenger.rb +0 -37
  141. data/lib/bard/provision/repo.rb +0 -72
  142. data/lib/bard/provision/rvm.rb +0 -22
  143. data/lib/bard/provision/ssh.rb +0 -72
  144. data/lib/bard/provision/swapfile.rb +0 -21
  145. data/lib/bard/provision/user.rb +0 -42
  146. data/lib/bard/provision.rb +0 -16
  147. data/lib/bard/server.rb +0 -117
  148. data/spec/bard/cli/command_spec.rb +0 -50
  149. data/spec/bard/cli/new_spec.rb +0 -73
  150. data/spec/bard/cli/provision_spec.rb +0 -42
  151. data/spec/bard/github_pages_spec.rb +0 -143
  152. data/spec/bard/provision/app_spec.rb +0 -33
  153. data/spec/bard/provision/apt_spec.rb +0 -39
  154. data/spec/bard/provision/authorizedkeys_spec.rb +0 -40
  155. data/spec/bard/provision/data_spec.rb +0 -54
  156. data/spec/bard/provision/deploy_spec.rb +0 -33
  157. data/spec/bard/provision/http_spec.rb +0 -57
  158. data/spec/bard/provision/logrotation_spec.rb +0 -34
  159. data/spec/bard/provision/masterkey_spec.rb +0 -63
  160. data/spec/bard/provision/mysql_spec.rb +0 -55
  161. data/spec/bard/provision/passenger_spec.rb +0 -81
  162. data/spec/bard/provision/repo_spec.rb +0 -208
  163. data/spec/bard/provision/rvm_spec.rb +0 -49
  164. data/spec/bard/provision/ssh_spec.rb +0 -229
  165. data/spec/bard/provision/swapfile_spec.rb +0 -32
  166. data/spec/bard/provision/user_spec.rb +0 -103
  167. data/spec/bard/provision_spec.rb +0 -28
  168. data/spec/bard/server_spec.rb +0 -127
  169. /data/lib/bard/{ci → plugins/deploy/ci}/state.rb +0 -0
  170. /data/{install_files → lib/bard/plugins/install}/apt_dependencies.rb +0 -0
  171. /data/{install_files → lib/bard/plugins/install}/ci +0 -0
  172. /data/{install_files → lib/bard/plugins/install}/setup +0 -0
  173. /data/{install_files → lib/bard/plugins/install}/specified_bundler.rb +0 -0
  174. /data/{install_files → lib/bard/plugins/install}/specified_ruby.rb +0 -0
@@ -1,110 +0,0 @@
1
- Given /^a submodule$/ do
2
- Given 'on development_b, a commit with a new submodule'
3
- Given 'on development_b, I type "bard push"'
4
- Given 'I type "bard pull"'
5
- @submodule_url = File.read(".gitmodules").match(/url = (.*)$/)[1]
6
- @submodule_commit = type "git submodule status"
7
- end
8
-
9
- Given /^the submodule working directory is dirty$/ do
10
- Dir.chdir "submodule" do
11
- type "git checkout master"
12
- type "echo 'submodule_update' > submodule_update"
13
- end
14
- end
15
-
16
- Given /^a commit to the submodule$/ do
17
- Dir.chdir "submodule" do
18
- type "echo 'submodule_update' > submodule_update"
19
- type "git add ."
20
- type "git commit -am 'update in submodule'"
21
- end
22
- end
23
-
24
- Given /^a commit with a new submodule$/ do
25
- type "git submodule add #{ROOT}/tmp/submodule_a.git submodule"
26
- type "git submodule update --init"
27
- Dir.chdir "submodule" do
28
- type "git checkout master"
29
- end
30
- type "git add ."
31
- type "git commit -m 'added submodule'"
32
- end
33
-
34
- Given /^a commit with a submodule update$/ do
35
- type "git checkout integration"
36
- Dir.chdir "submodule" do
37
- type "git checkout master"
38
- type "echo 'submodule_update' > submodule_update"
39
- type "git add ."
40
- type "git commit -m 'update in submodule'"
41
- type "git push origin HEAD"
42
- end
43
- type "git add ."
44
- type "git commit -m 'updated submodule'"
45
- end
46
-
47
- Given /^a commit with a submodule url change$/ do
48
- gsub_file ".gitmodules", "submodule_a.git", "submodule_b.git"
49
- type "git add ."
50
- type "git commit -m 'updated submodule url'"
51
- end
52
-
53
- Given /^I a commit a with a submodule deletion$/ do
54
- type "rm .gitmodules"
55
- type "rm -rf --cached submodule"
56
- type "git add ."
57
- type "git commit -am'removed submodule'"
58
- end
59
-
60
- Then /^there should be one new submodule$/ do
61
- status = type "git submodule status"
62
- status.should match /.[a-z0-9]{40} submodule/
63
- end
64
-
65
- Then /^the submodule branch should match the submodule origin branch$/ do
66
- @submodule_url = File.read(".gitmodules").match(/url = (.*)$/)[1]
67
- @submodule_commit = type "git submodule status"
68
- @submodule_commit.should match %r( [a-z0-9]{40} submodule)
69
- Dir.chdir "submodule" do
70
- @submodule = Grit::Repo.new "."
71
- branch = @submodule.head.name rescue nil
72
- remote_branch = @submodule.remotes.find {|n| n.name == "origin/HEAD" }.commit.id[/\w+$/]
73
- branch.should_not be_nil
74
- remote_branch.should_not be_nil
75
- branch.should == remote_branch
76
- type("git rev-parse HEAD").should == type("git rev-parse origin/HEAD")
77
- type("git name-rev --name-only HEAD").should == type("git name-rev --name-only origin/HEAD")
78
- end
79
- end
80
-
81
- Then /^the submodule should be checked out$/ do
82
- @submodule_url = File.read(".gitmodules").match(/url = (.*)$/)[1]
83
- @submodule_commit = type "git submodule status"
84
- @submodule_commit.should match %r( [a-z0-9]{40} submodule)
85
- end
86
-
87
- Then /^the submodule should be updated$/ do
88
- @submodule_commit[/[a-z0-9]{40}/].should_not == type("git submodule status")[/[a-z0-9]{40}/]
89
- end
90
-
91
- Then /^the submodule url should be changed$/ do
92
- Dir.chdir "submodule" do
93
- remote = type "git remote show origin"
94
- remote.should_not match %r(Fetch URL: #{@submodule_url}$)
95
- remote.should_not match %r(Push URL: #{@submodule_url}$)
96
- end
97
- end
98
-
99
- Then /^the submodule should be deleted$/ do
100
- Then 'the directory should not be dirty'
101
- @submodule_commit = type "git submodule status"
102
- @submodule_commit.should_not match /.[a-z0-9]{40} submodule/
103
-
104
- end
105
-
106
- Then /^the submodule working directory should be clean$/ do
107
- Dir.chdir "submodule" do
108
- type("git status").should include "working directory clean"
109
- end
110
- end
@@ -1,13 +0,0 @@
1
- Grit::Repo.class_eval do
2
- def remote_branches(remote = "origin")
3
- branches = self.remotes
4
- branches.reject! { |r| r.name !~ %r(^#{remote}/) }
5
- branches.collect! { |r| r.name.split('/')[1] }
6
- branches.reject! { |b| b == "HEAD" }
7
- end
8
-
9
- def find_common_ancestor(head1, head2)
10
- `git merge-base #{head1} #{head2}`.chomp
11
- end
12
- end
13
-
@@ -1,32 +0,0 @@
1
- require "open3"
2
- def type(command)
3
- @stdout, @stderr, @status = Open3.capture3(@env, command)
4
- if ENV['DEBUG']
5
- puts '-' * 20
6
- puts "Executing command: #{command}"
7
- puts " Status: #{@status}"
8
- puts " Stdout:\n #{@stdout}"
9
- puts " Stderr:\n #{@stderr}"
10
- puts '-' * 20
11
- end
12
- @stdout || @stderr
13
- end
14
-
15
- def file_append(file_name, contents)
16
- File.open(file_name, 'ab') { |file| file.puts("\n#{contents}") }
17
- end
18
-
19
- def file_inject(file_name, sentinel, string, before_after=:after)
20
- gsub_file file_name, /(#{Regexp.escape(sentinel)})/mi do |match|
21
- if before_after == :after
22
- "#{match}\n#{string}"
23
- else
24
- "#{string}\n#{match}"
25
- end
26
- end
27
- end
28
-
29
- def gsub_file(file_name, regexp, *args, &block)
30
- content = File.read(file_name).gsub(regexp, *args, &block)
31
- File.open(file_name, 'wb') { |file| file.write(content) }
32
- end
@@ -1,153 +0,0 @@
1
- require "fileutils"
2
- require "open3"
3
- require "securerandom"
4
- require "shellwords"
5
- require "testcontainers"
6
-
7
- module PodmanWorld
8
- class << self
9
- attr_accessor :podman_available, :podman_image_built
10
- end
11
-
12
- class PrerequisiteError < StandardError; end
13
-
14
- def ensure_podman_available
15
- return if @podman_available || PodmanWorld.podman_available
16
-
17
- raise PrerequisiteError, "podman is not installed or not on PATH" unless system("command -v podman >/dev/null 2>&1")
18
-
19
- configure_podman_socket
20
- ensure_bard_test_image
21
- FileUtils.chmod(0o600, podman_ssh_key_path)
22
-
23
- PodmanWorld.podman_available = true
24
- @podman_available = true
25
- end
26
-
27
- def configure_podman_socket
28
- return if ENV["DOCKER_HOST"]
29
-
30
- podman_socket = "/run/user/#{Process.uid}/podman/podman.sock"
31
- unless File.exist?(podman_socket)
32
- system("systemctl --user start podman.socket 2>/dev/null || podman system service --time=0 unix://#{podman_socket} &")
33
- sleep 2
34
- end
35
-
36
- raise PrerequisiteError, "Podman socket not available at #{podman_socket}" unless File.exist?(podman_socket)
37
-
38
- ENV["DOCKER_HOST"] = "unix://#{podman_socket}"
39
- end
40
-
41
- def ensure_bard_test_image
42
- return if @podman_image_built || PodmanWorld.podman_image_built
43
-
44
- raise PrerequisiteError, "Unable to pull ubuntu:22.04 image" unless system("podman pull ubuntu:22.04 >/dev/null 2>&1")
45
-
46
- docker_dir = File.join(ROOT, "spec/acceptance/docker")
47
- dockerfile = File.join(docker_dir, "Dockerfile")
48
- unless system("podman build -t bard-test-server -f #{dockerfile} #{docker_dir} 2>&1")
49
- raise PrerequisiteError, "Failed to build bard test image"
50
- end
51
-
52
- PodmanWorld.podman_image_built = true
53
- @podman_image_built = true
54
- end
55
-
56
- def start_podman_container
57
- ensure_podman_available
58
-
59
- @podman_container = Testcontainers::DockerContainer
60
- .new("localhost/bard-test-server:latest")
61
- .with_exposed_port(22)
62
- .with_name("bard-test-#{SecureRandom.hex(4)}")
63
- .start
64
-
65
- @podman_ssh_port = @podman_container.mapped_port(22)
66
- wait_for_ssh
67
- run_ssh("mkdir -p testproject")
68
- write_bard_config
69
- end
70
-
71
- def wait_for_ssh
72
- 30.times do
73
- return if run_ssh("echo ready", quiet: true)
74
- sleep 0.5
75
- end
76
-
77
- raise PrerequisiteError, "SSH in podman container did not become ready"
78
- end
79
-
80
- def write_bard_config
81
- FileUtils.mkdir_p(File.join(ROOT, "tmp"))
82
- @bard_config_path = File.join(ROOT, "tmp", "test_bard_#{SecureRandom.hex(4)}.rb")
83
-
84
- File.write(@bard_config_path, <<~RUBY)
85
- server :production do
86
- ssh "deploy@localhost:#{@podman_ssh_port}"
87
- path "testproject"
88
- ssh_key "#{podman_ssh_key_path}"
89
- ping false
90
- end
91
- RUBY
92
- end
93
-
94
- def run_ssh(command, quiet: false)
95
- escaped = Shellwords.escape(command)
96
- ssh_command = [
97
- "ssh",
98
- "-o", "StrictHostKeyChecking=no",
99
- "-o", "ConnectTimeout=1",
100
- "-p", @podman_ssh_port.to_s,
101
- "-i", podman_ssh_key_path,
102
- "deploy@localhost",
103
- "--",
104
- "bash",
105
- "-lc",
106
- escaped
107
- ].join(" ")
108
-
109
- quiet ? system("#{ssh_command} >/dev/null 2>&1") : system(ssh_command)
110
- end
111
-
112
- def run_bard_against_container(command)
113
- Dir.chdir(File.join(ROOT, "tmp")) do
114
- FileUtils.cp(@bard_config_path, "bard.rb")
115
- @stdout, @status = Open3.capture2e(@env || {}, "bard run #{command}")
116
- @stderr = ""
117
- FileUtils.rm_f("bard.rb")
118
- end
119
- end
120
-
121
- def podman_ssh_key_path
122
- @podman_ssh_key_path ||= File.expand_path(File.join(ROOT, "spec/acceptance/docker/test_key"))
123
- end
124
-
125
- def stop_podman_container
126
- FileUtils.rm_f(@bard_config_path) if @bard_config_path
127
- return unless @podman_container
128
-
129
- @podman_container.stop
130
- @podman_container.remove
131
- rescue StandardError => e
132
- warn "Failed to cleanup podman container: #{e.message}"
133
- ensure
134
- @podman_container = nil
135
- @podman_ssh_port = nil
136
- end
137
- end
138
-
139
- World(PodmanWorld)
140
-
141
- Before("@podman") do
142
- @env ||= {}
143
-
144
- begin
145
- start_podman_container
146
- rescue PodmanWorld::PrerequisiteError => e
147
- pending(e.message)
148
- end
149
- end
150
-
151
- After("@podman") do
152
- stop_podman_container
153
- end
@@ -1,105 +0,0 @@
1
- require "json"
2
- require "bard/ci/runner"
3
-
4
- module Bard
5
- class CI
6
- class Jenkins < Runner
7
-
8
- def exists?
9
- `curl -s -I #{ci_host}/` =~ /\b200 OK\b/
10
- end
11
-
12
- def console
13
- raw = `curl -s #{ci_host}/lastBuild/console`
14
- raw[%r{<pre.*?>(.+)</pre>}m, 1]
15
- end
16
-
17
- attr_accessor :last_response
18
-
19
- protected
20
-
21
- def wait_until_started
22
- sleep(2) until started?
23
- end
24
-
25
- def start
26
- command = "curl -s -I -X POST -L '#{ci_host}/buildWithParameters?GIT_REF=#{sha}'"
27
- output = `#{command}`
28
- @queueId = output[%r{Location: .+/queue/item/(\d+)/}, 1].to_i
29
- end
30
-
31
- def building?
32
- retry_with_backoff do
33
- self.last_response = `curl -s #{ci_host}/#{job_id}/api/json?tree=building,result`
34
- raise "Blank response from CI" if last_response.blank?
35
- end
36
- last_response.include? '"building":true'
37
- end
38
-
39
- def success?
40
- last_response.include? '"result":"SUCCESS"'
41
- end
42
-
43
- def get_state_data
44
- {
45
- "project_name" => project_name,
46
- "branch" => branch,
47
- "queue_id" => @queueId,
48
- "job_id" => @job_id,
49
- "start_time" => @start_time,
50
- "last_time_elapsed" => @last_time_elapsed
51
- }
52
- end
53
-
54
- def restore_state(data)
55
- @queueId = data["queue_id"]
56
- @job_id = data["job_id"]
57
- @start_time = data["start_time"]
58
- @last_time_elapsed = data["last_time_elapsed"]
59
- end
60
-
61
- private
62
-
63
- def get_last_time_elapsed
64
- retry_with_backoff do
65
- response = `curl -s #{ci_host}/lastStableBuild/api/xml`
66
- raise "Blank response from CI" if response.blank?
67
- response
68
- end
69
- response.match(/<duration>(\d+)<\/duration>/)
70
- $1 ? $1.to_i / 1000 : nil
71
- rescue => e
72
- puts " Warning: Could not get last build duration: #{e.message}"
73
- nil
74
- end
75
-
76
- def auth
77
- "botandrose:11cc2ba6ef2e43fbfbedc1f466724f6290"
78
- end
79
-
80
- def ci_host
81
- "http://#{auth}@ci.botandrose.com/job/#{project_name}"
82
- end
83
-
84
- def started?
85
- retry_with_backoff do
86
- command = "curl -s -g '#{ci_host}/api/json?depth=1&tree=builds[queueId,number]'"
87
- output = `#{command}`
88
- raise "Blank response from CI" if output.blank?
89
- JSON.parse(output)["builds"][0]["queueId"] == @queueId
90
- end
91
- end
92
-
93
- def job_id
94
- @job_id ||= begin
95
- retry_with_backoff do
96
- output = `curl -s -g '#{ci_host}/api/json?depth=1&tree=builds[queueId,number]'`
97
- raise "Blank response from CI" if output.blank?
98
- output[/"number":(\d+),"queueId":#{@queueId}\b/, 1].to_i
99
- end
100
- end
101
- end
102
- end
103
- end
104
- end
105
-
@@ -1,27 +0,0 @@
1
- module Bard
2
- class CI
3
- module Retryable
4
- MAX_RETRIES = 5
5
- INITIAL_DELAY = 1
6
-
7
- def retry_with_backoff(max_retries: MAX_RETRIES)
8
- retries = 0
9
- delay = INITIAL_DELAY
10
-
11
- begin
12
- yield
13
- rescue => e
14
- if retries < max_retries
15
- retries += 1
16
- puts " Network error (attempt #{retries}/#{max_retries}): #{e.message}. Retrying in #{delay}s..."
17
- sleep(delay)
18
- delay *= 2
19
- retry
20
- else
21
- raise "Network error after #{max_retries} attempts: #{e.message}"
22
- end
23
- end
24
- end
25
- end
26
- end
27
- end
data/lib/bard/ci.rb DELETED
@@ -1,50 +0,0 @@
1
- require "forwardable"
2
-
3
- module Bard
4
- class CI
5
- def initialize project_name, branch, local: false
6
- @project_name = project_name
7
- @branch = branch
8
- @local = !!local
9
- end
10
-
11
- extend Forwardable
12
- delegate [:run, :resume, :exists?, :console, :status] => :runner
13
-
14
- private
15
-
16
- def local?
17
- @local
18
- end
19
-
20
- def github_actions?
21
- File.exist?(".github/workflows/ci.yml")
22
- end
23
-
24
- def jenkins?
25
- !local? && !github_actions?
26
- end
27
-
28
- def runner
29
- @runner ||= choose_runner_class.new(@project_name, @branch, sha)
30
- end
31
-
32
- def sha
33
- @sha ||= `git rev-parse #{@branch}`.chomp
34
- end
35
-
36
- def choose_runner_class
37
- if local?
38
- require_relative "./ci/local"
39
- Local
40
- elsif github_actions?
41
- require_relative "./ci/github_actions"
42
- GithubActions
43
- elsif jenkins?
44
- require_relative "./ci/jenkins"
45
- Jenkins
46
- end
47
- end
48
- end
49
- end
50
-
data/lib/bard/cli/ci.rb DELETED
@@ -1,66 +0,0 @@
1
- require "bard/ci"
2
- require "bard/git"
3
-
4
- module Bard::CLI::CI
5
- def self.included mod
6
- mod.class_eval do
7
-
8
- option :"local-ci", type: :boolean
9
- option :status, type: :boolean
10
- option :resume, type: :boolean
11
- desc "ci [branch=HEAD]", "runs ci against BRANCH"
12
- def ci branch=Bard::Git.current_branch
13
- ci = Bard::CI.new(project_name, branch, local: options["local-ci"])
14
- if ci.exists?
15
- return puts ci.status if options["status"]
16
-
17
- if options["resume"]
18
- puts "Continuous integration: resuming build..."
19
- success = ci.resume do |elapsed_time, last_time|
20
- if last_time
21
- percentage = (elapsed_time.to_f / last_time.to_f * 100).to_i
22
- output = " Estimated completion: #{percentage}%"
23
- else
24
- output = " No estimated completion time. Elapsed time: #{elapsed_time} sec"
25
- end
26
- print "\x08" * output.length
27
- print output
28
- $stdout.flush
29
- end
30
- else
31
- puts "Continuous integration: starting build on #{branch}..."
32
-
33
- success = ci.run do |elapsed_time, last_time|
34
- if last_time
35
- percentage = (elapsed_time.to_f / last_time.to_f * 100).to_i
36
- output = " Estimated completion: #{percentage}%"
37
- else
38
- output = " No estimated completion time. Elapsed time: #{elapsed_time} sec"
39
- end
40
- print "\x08" * output.length
41
- print output
42
- $stdout.flush
43
- end
44
- end
45
-
46
- if success
47
- puts
48
- puts "Continuous integration: success!"
49
- else
50
- puts
51
- puts ci.console
52
- puts red("Automated tests failed!")
53
- exit 1
54
- end
55
-
56
- else
57
- puts red("No CI found for #{project_name}!")
58
- puts "Re-run with --skip-ci to bypass CI, if you absolutely must, and know what you're doing."
59
- exit 1
60
- end
61
- end
62
-
63
- end
64
- end
65
- end
66
-
@@ -1,26 +0,0 @@
1
- require "delegate"
2
-
3
- class Bard::CLI::Command < SimpleDelegator
4
- def self.desc command, description
5
- @command = command
6
- @method = command.split(" ").first.to_sym
7
- @description = description
8
- end
9
-
10
- def self.option *args, **kwargs
11
- @option_args = args
12
- @option_kwargs = kwargs
13
- end
14
-
15
- def self.setup cli
16
- cli.desc @command, @description
17
- cli.option *@option_args, **@option_kwargs if @option_args || @option_kwargs
18
- # put in local variables so next block can capture it
19
- command_class = self
20
- method = @method
21
- cli.define_method method do |*args|
22
- command = command_class.new(self)
23
- command.send method, *args
24
- end
25
- end
26
- end
data/lib/bard/cli/data.rb DELETED
@@ -1,45 +0,0 @@
1
- require "bard/command"
2
-
3
- module Bard::CLI::Data
4
- def self.included mod
5
- mod.class_eval do
6
-
7
- desc "data --from=production --to=local", "copy database and assets from from to to"
8
- option :from, default: "production"
9
- option :to, default: "local"
10
- def data
11
- from = config[options[:from]]
12
- to = config[options[:to]]
13
-
14
- if to.key == :production
15
- url = to.ping.first
16
- puts yellow "WARNING: You are about to push data to production, overwriting everything that is there!"
17
- answer = ask("If you really want to do this, please type in the full HTTPS url of the production server:")
18
- if answer != url
19
- puts red("!!! ") + "Failed! We expected #{url}. Is this really where you want to overwrite all the data?"
20
- exit 1
21
- end
22
- end
23
-
24
- puts "Dumping #{from.key} database to file..."
25
- from.run! "bin/rake db:dump"
26
-
27
- puts "Transfering file from #{from.key} to #{to.key}..."
28
- from.copy_file "db/data.sql.gz", to: to, verbose: true
29
-
30
- puts "Loading file into #{to.key} database..."
31
- to.run! "bin/rake db:load"
32
-
33
- config.data.each do |path|
34
- puts "Synchronizing files in #{path}..."
35
- from.copy_dir path, to: to, verbose: true
36
- end
37
- rescue Bard::Command::Error => e
38
- puts red("!!! ") + "Running command failed: #{yellow(e.message)}"
39
- exit 1
40
- end
41
-
42
- end
43
- end
44
- end
45
-