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.
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} +13 -6
  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,112 +0,0 @@
1
- Feature: bard push
2
- Background:
3
- Given a shared rails project
4
-
5
- Scenario: Uploading local changes onto the remote integration branch
6
- Given a commit
7
- When I type "bard push"
8
- And on staging, I type "bard stage"
9
- Then the "integration" branch should match the "staging:integration" branch
10
-
11
- Scenario: Uploading local changes onto a remote topic branch
12
- Given a commit on the "topic" branch
13
- And I am on the "topic" branch
14
- When I type "bard push"
15
- Then the "topic" branch should match the "origin/topic" branch
16
-
17
- Scenario: Pushing a change that includes a migration
18
- Given on staging, a staging database
19
- And a commit with a new migration
20
- When I type "bard push"
21
- And on staging, I type "bard stage"
22
- Then on staging, the staging database should include that migration
23
-
24
- Scenario: Pushing a change that includes a gem dependency change
25
- Given the test gem is not installed
26
- And a commit that adds the test gem as a dependency
27
- When I type "bard push"
28
- And on staging, I type "bard stage"
29
- Then on staging, the test gem should be installed
30
-
31
- Scenario: Pushing a change should advance the staging HEAD and restart the staging rails server
32
- Given a commit
33
- When I type "bard push"
34
- And on staging, I type "bard stage"
35
- And the "integration" branch should match the "staging:integration" branch
36
- Then on staging, passenger should have been restarted
37
-
38
- Scenario: Pushing a change that includes a submodule addition
39
- Given a commit with a new submodule
40
- When I type "bard push"
41
- And on staging, I type "bard stage"
42
- Then on staging, there should be one new submodule
43
- And the submodule branch should match the submodule origin branch
44
- And on staging, the submodule working directory should be clean
45
-
46
- Scenario: Pushing a change that includes a submodule update
47
- Given a submodule
48
- And a commit with a submodule update
49
- When I type "bard push"
50
- And on staging, I type "bard stage"
51
- Then the submodule branch should match the submodule origin branch
52
- Then on staging, the submodule working directory should be clean
53
-
54
- Scenario: Pushing a change that includes a submodule url change
55
- Given a submodule
56
- And a commit with a submodule url change
57
- When I type "bard push"
58
- And on staging, I type "bard stage"
59
- Then on staging, the submodule url should be changed
60
- And the submodule branch should match the submodule origin branch
61
- Then on staging, the submodule working directory should be clean
62
-
63
- # TODO
64
- #Scenario: Pushing a change that includes a submodule deletion
65
- # Given a submodule
66
- # Given I have committed a set of changes that includes a submodule deletion
67
- # When I type "bard push"
68
- # And on staging, I type "bard stage"
69
- # Then the remote submodule should be deleted
70
-
71
- Scenario: Trying to bard push when not in the project root
72
- Given I am in a subdirectory
73
- When I type "bard push"
74
- Then I should see the fatal error "root directory"
75
-
76
- Scenario: Trying to bard push when not on the integration branch
77
- Given a commit on the "master" branch
78
- And I am on the "master" branch
79
- When I type "bard push"
80
- Then I should see the fatal error "on the master branch"
81
- And the "master" branch should not match the "origin/master" branch
82
-
83
- Scenario: Trying to bard push with a dirty working directory
84
- Given a commit
85
- And a dirty working directory
86
- When I type "bard push"
87
- Then I should see the fatal error "You have uncommitted changes!"
88
- And the "integration" branch should not match the "origin/integration" branch
89
-
90
- Scenario: Trying to bard push with a non-fast-foward changeset
91
- Given a commit
92
- And on development_b, a commit
93
- And on development_b, I type "bard push"
94
- When I type "bard push"
95
- Then I should see the fatal error "Someone has pushed some changes"
96
- And the "integration" branch should not match the "origin/integration" branch
97
-
98
- Scenario: Trying to bard push with an uncommitted change to a submodule
99
- Given a submodule
100
- And a commit
101
- And the submodule working directory is dirty
102
- When I type "bard push"
103
- Then I should see the fatal error "Micah"
104
- And the "integration" branch should not match the "origin/integration" branch
105
-
106
- Scenario: Trying to bard push with a committed but unpushed change to a submodule
107
- Given a submodule
108
- And a commit to the submodule
109
- And a commit
110
- When I type "bard push"
111
- Then I should see the fatal error "Micah"
112
- And the "integration" branch should not match the "origin/integration" branch
@@ -1,16 +0,0 @@
1
- @podman
2
- Feature: bard run against a podman TestContainers host
3
- Background:
4
- Given a podman testcontainer is ready for bard
5
-
6
- Scenario: Running ls via bard run
7
- Given a remote file "test-file.txt" exists in the test container
8
- When I run bard "ls" against the test container
9
- Then the bard command should succeed
10
- And the bard output should include "test-file.txt"
11
-
12
- Scenario: Running commands in isolated containers
13
- Given a remote file "another-file.txt" containing "content" exists in the test container
14
- When I run bard "cat another-file.txt" against the test container
15
- Then the bard command should succeed
16
- And the bard output should include "content"
@@ -1,47 +0,0 @@
1
- Then /^I should see the current version of bard$/ do
2
- version = File.read("#{ROOT}/VERSION").chomp
3
- @stdout.should =~ /bard\s+\(#{Regexp.escape(version)}\)/
4
- end
5
-
6
- Then /^I should see the current version of git$/ do
7
- version = `git --version`[/[0-9]+\.[0-9]+\.[0-9]+/]
8
- @stdout.should =~ /git\s+\(#{Regexp.escape(version)}/
9
- end
10
-
11
- Then /^I should see the current version of rubygems$/ do
12
- version = `gem --version`.chomp
13
- @stdout.should =~ /rubygems\s+\(#{Regexp.escape(version)}\)/
14
- end
15
-
16
- Then /^I should see the current version of ruby$/ do
17
- version = `ruby --version`[/[0-9]+\.[0-9]+\.[0-9]+/]
18
- @stdout.should =~ /ruby\s+\(#{Regexp.escape(version)}\)/
19
- end
20
-
21
- Given /^"([^\"]*)" is missing$/ do |file|
22
- type "rm #{file}"
23
- end
24
-
25
- Given /^the database is missing$/ do
26
- File.open "config/database.yml", "w" do |f|
27
- f.puts <<-DB
28
- development:
29
- adapter: mysql
30
- username: root
31
- password:
32
- database: bad_bad_bad
33
- socket: /var/run/mysqld/mysqld.sock
34
- DB
35
- end
36
- end
37
-
38
- Given /^the submodule is missing$/ do
39
- type "rm -rf submodule"
40
- type "mkdir submodule"
41
- end
42
-
43
- Given /^the submodule has a detached head$/ do
44
- Dir.chdir "submodule" do
45
- type "git checkout `git rev-parse HEAD`"
46
- end
47
- end
@@ -1,73 +0,0 @@
1
- Given /^I am on the "([^\"]+)" branch$/ do |branch|
2
- if `git branch` =~ / #{branch}$/
3
- type "git checkout #{branch}"
4
- else
5
- type "git checkout -b #{branch}"
6
- end
7
- end
8
-
9
- Given /^there is no integration branch$/ do
10
- type "git checkout master"
11
- type "git branch -d integration"
12
- end
13
-
14
- Given /^the integration branch isnt tracking origin\/integration$/ do
15
- type "git checkout master"
16
- type "git branch -d integration"
17
- type "git checkout -b integration"
18
- end
19
-
20
- Given /^a dirty working directory$/ do
21
- File.open("dirty_file", "w") { |f| f.puts "dirty dirty" }
22
- end
23
-
24
- Given /^a commit$/ do
25
- text = (rand * 100000000).round
26
- type "echo '#{text}' > foobar_#{text}_file"
27
- type "git add ."
28
- type "git commit -am'test commit'"
29
- end
30
-
31
- Given /^a commit on the "([^\"]+)" branch$/ do |branch|
32
- Given %(I am on the "#{branch}" branch)
33
- text = (rand * 100000000).round
34
- type "echo '#{text}' > #{branch}_#{text}_file"
35
- type "git add ."
36
- type "git commit -am 'testing #{branch} change'"
37
- type "git checkout integration"
38
- end
39
-
40
- Then /^the directory should not be dirty$/ do
41
- type("git status").should include "working directory clean"
42
- end
43
-
44
- Then /^I should be on the "([^\"]*)" branch$/ do |branch|
45
- @repo.head.name.should == branch
46
- end
47
-
48
- Then /^there should not be a "([^\"]*)" branch$/ do |branch_name|
49
- @repo.branches.any? { |branch| branch.name == branch_name }
50
- end
51
-
52
- Then /^the "([^\"]*)" branch (should|should not) match the "([^\"]*)" branch$/ do |local_branch, which, remote_branch|
53
- type "git fetch origin"
54
- local_env, local_branch = local_branch.split(':') if local_branch.include? ':'
55
- local_env ||= "development_a"
56
- remote_env, remote_branch = remote_branch.split(':') if remote_branch.include? ':'
57
- remote_env ||= "development_a"
58
- local_sha = @repos[local_env].commits(local_branch).first.id
59
- remote_sha = @repos[remote_env].commits(remote_branch).first.id
60
- which = which.gsub(/ /, '_').to_sym
61
- local_sha.send(which) == remote_sha
62
- end
63
-
64
- Then /^the "([^\"]*)" branch should be a fast\-forward from the "([^\"]*)" branch$/ do |local_branch, remote_branch|
65
- local_env, local_branch = local_branch.split(':') if local_branch.include? ':'
66
- local_env ||= "development_a"
67
- remote_env, remote_branch = remote_branch.split(':') if remote_branch.include? ':'
68
- remote_env ||= "development_a"
69
- local_sha = @repos[local_env].commits(local_branch).first.id
70
- remote_sha = @repos[remote_env].commits(remote_branch).first.id
71
- common_ancestor = @repos[local_env].find_common_ancestor local_sha, remote_sha
72
- common_ancestor.should == remote_sha
73
- end
@@ -1,56 +0,0 @@
1
- Given /^a shared rails project$/ do
2
- # TEARDOWN
3
- Dir.foreach "#{ROOT}/tmp" do |file|
4
- FileUtils.rm_rf("#{ROOT}/tmp/#{file}") unless %w(fixtures . ..).include? file
5
- end
6
-
7
- # SETUP
8
- Dir.chdir ROOT
9
- `cp -r tmp/fixtures/* tmp/`
10
-
11
- Dir.chdir 'tmp'
12
- @repos = {}
13
- %w(development_a development_b staging production).each do |env|
14
- @repos[env] = Grit::Repo.new env
15
- end
16
- Dir.chdir 'development_a'
17
- @repo = @repos['development_a']
18
- @env = { 'RAILS_ENV' => 'development', 'TESTING' => true }
19
- end
20
-
21
- Given /^I am in a subdirectory$/ do
22
- FileUtils.mkdir "test_subdirectory"
23
- Dir.chdir "test_subdirectory"
24
- end
25
-
26
- When /^I type "([^\"]*)"$/ do |command|
27
- type command.sub /\b(bard)\b/, "#{ROOT}/bin/bard"
28
- end
29
-
30
- When /^on (\w+), (.*$)/ do |env, step|
31
- old_env = @env['RAILS_ENV']
32
- @env['RAILS_ENV'] = env if %w(staging production).include? env
33
- Dir.chdir "#{ROOT}/tmp/#{env}" do
34
- old_repo = @repo
35
- @repo = @repos[env]
36
- When step
37
- @repo = old_repo
38
- end
39
- @env['RAILS_ENV'] = old_env
40
- end
41
-
42
- Then /^I should see the fatal error "([^\"]*)"$/ do |error_message|
43
- @stderr.should include(error_message)
44
- end
45
-
46
- Then /^I should see the warning "([^\"]*)"$/ do |warning_message|
47
- @stderr.should include(warning_message)
48
- end
49
-
50
- Then /^I should see "([^\"]*)"$/ do |message|
51
- @stdout.should include(message)
52
- end
53
-
54
- Then /^debug$/ do
55
- debugger
56
- end
@@ -1,23 +0,0 @@
1
- Given /^a podman testcontainer is ready for bard$/ do
2
- raise "Podman testcontainer failed to start" unless @podman_container && @podman_ssh_port
3
- end
4
-
5
- Given /^a remote file "([^\"]+)" exists in the test container$/ do |filename|
6
- run_ssh("touch testproject/#{filename}").should be_true
7
- end
8
-
9
- Given /^a remote file "([^\"]+)" containing "([^\"]+)" exists in the test container$/ do |filename, content|
10
- run_ssh("echo #{Shellwords.escape(content)} > testproject/#{filename}").should be_true
11
- end
12
-
13
- When /^I run bard "([^\"]+)" against the test container$/ do |command|
14
- run_bard_against_container(command)
15
- end
16
-
17
- Then /^the bard command should succeed$/ do
18
- @status.success?.should be_true
19
- end
20
-
21
- Then /^the bard output should include "([^\"]+)"$/ do |expected|
22
- @stdout.should include(expected)
23
- end
@@ -1,44 +0,0 @@
1
- Given /^a commit with a new migration$/ do
2
- type "script/generate migration test_migration"
3
- type "git add ."
4
- type "git commit -am'added test migration.'"
5
- end
6
-
7
- Given /^a (\w+) database$/ do |env|
8
- type "rake db:create RAILS_ENV=#{env} && rake db:migrate RAILS_ENV=#{env}"
9
- end
10
-
11
- Then /^the (\w+) database should include that migration$/ do |env|
12
- db_version = type("rake db:version RAILS_ENV=#{env}")[/[0-9]{14}/]
13
- migration_version = type("ls db/migrate/*_test_migration.rb")[/[0-9]{14}/]
14
- db_version.should == migration_version
15
- end
16
-
17
- Given /^the test gem is not installed$/ do
18
- type "gem uninstall rake-dotnet -v=0.0.1 -x"
19
- end
20
-
21
- Given /^a commit that adds the test gem as a dependency$/ do
22
- file_inject "config/environment.rb", "
23
- Rails::Initializer.run do |config|", <<-RUBY
24
- config.gem "rake-dotnet", :version => "0.0.1"
25
- RUBY
26
- type "git add ."
27
- type "git commit -am'added test gem dependency.'"
28
- end
29
-
30
- Then /^the test gem should be installed$/ do
31
- type("gem list rake-dotnet").should include "rake-dotnet (0.0.1)"
32
- end
33
-
34
- Then /^passenger should have been restarted$/ do
35
- File.exist?("tmp/restart.txt").should be_true
36
- end
37
-
38
- Given /^the "([^\"]+)" file includes "([^\"]+)"$/ do |file, contents|
39
- file_append file, contents
40
- end
41
-
42
- Given /^the "([^\"]+)" file does not include "([^\"]+)"$/ do |file, contents|
43
- gsub_file file, contents, ""
44
- end
@@ -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