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.
Files changed (157) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +2 -2
  3. data/CLAUDE.md +1 -1
  4. data/PLUGINS.md +31 -46
  5. data/bard.gemspec +2 -1
  6. data/features/ci.feature +1 -0
  7. data/features/data.feature +1 -0
  8. data/features/deploy.feature +1 -0
  9. data/features/deploy_git_workflow.feature +1 -0
  10. data/features/run.feature +1 -0
  11. data/features/step_definitions/bard_steps.rb +1 -0
  12. data/features/support/test_server.rb +3 -3
  13. data/lib/bard/cli.rb +9 -28
  14. data/lib/bard/command.rb +9 -88
  15. data/lib/bard/config.rb +38 -177
  16. data/lib/bard/copy.rb +28 -82
  17. data/lib/bard/plugins/data.rb +56 -0
  18. data/lib/bard/{ci → plugins/deploy/ci}/github_actions.rb +2 -2
  19. data/lib/bard/{ci → plugins/deploy/ci}/jenkins.rb +1 -1
  20. data/lib/bard/{ci → plugins/deploy/ci}/local.rb +1 -1
  21. data/lib/bard/{ci → plugins/deploy/ci}/runner.rb +3 -3
  22. data/lib/bard/{ci.rb → plugins/deploy/ci.rb} +1 -1
  23. data/lib/bard/plugins/deploy/ssh_strategy.rb +27 -0
  24. data/lib/bard/{deploy_strategy.rb → plugins/deploy/strategy.rb} +1 -4
  25. data/lib/bard/plugins/deploy.rb +240 -0
  26. data/lib/bard/{git.rb → plugins/git.rb} +6 -3
  27. data/lib/bard/{github.rb → plugins/github.rb} +2 -2
  28. data/lib/bard/{deploy_strategy/github_pages.rb → plugins/github_pages/strategy.rb} +3 -4
  29. data/lib/bard/plugins/github_pages.rb +11 -15
  30. data/lib/bard/plugins/hurt.rb +12 -4
  31. data/{install_files → lib/bard/plugins/install}/.github/dependabot.yml +2 -1
  32. data/{install_files → lib/bard/plugins/install}/.github/workflows/cache-ci.yml +1 -1
  33. data/{install_files → lib/bard/plugins/install}/.github/workflows/ci.yml +2 -2
  34. data/lib/bard/plugins/install.rb +7 -3
  35. data/lib/bard/plugins/open.rb +20 -0
  36. data/lib/bard/{ping.rb → plugins/ping/check.rb} +4 -4
  37. data/lib/bard/plugins/ping/target_methods.rb +23 -0
  38. data/lib/bard/plugins/ping.rb +8 -4
  39. data/lib/bard/plugins/run.rb +19 -0
  40. data/lib/bard/plugins/setup.rb +54 -0
  41. data/lib/bard/plugins/ssh/connection.rb +75 -0
  42. data/lib/bard/plugins/ssh/copy.rb +95 -0
  43. data/lib/bard/{ssh_server.rb → plugins/ssh/server.rb} +10 -42
  44. data/lib/bard/plugins/ssh/target_methods.rb +20 -0
  45. data/lib/bard/plugins/ssh.rb +10 -0
  46. data/lib/bard/plugins/url/target_methods.rb +23 -0
  47. data/lib/bard/plugins/url.rb +1 -0
  48. data/lib/bard/plugins/vim.rb +5 -4
  49. data/lib/bard/retryable.rb +25 -0
  50. data/lib/bard/target.rb +21 -230
  51. data/lib/bard/version.rb +1 -1
  52. data/lib/bard.rb +1 -3
  53. data/spec/acceptance/docker/Dockerfile +1 -1
  54. data/spec/bard/capability_spec.rb +8 -50
  55. data/spec/bard/ci/github_actions_spec.rb +1 -1
  56. data/spec/bard/ci/jenkins_spec.rb +1 -1
  57. data/spec/bard/ci/runner_spec.rb +3 -3
  58. data/spec/bard/ci_spec.rb +1 -1
  59. data/spec/bard/cli/ci_spec.rb +4 -23
  60. data/spec/bard/cli/data_spec.rb +7 -26
  61. data/spec/bard/cli/deploy_spec.rb +43 -40
  62. data/spec/bard/cli/hurt_spec.rb +2 -8
  63. data/spec/bard/cli/install_spec.rb +4 -10
  64. data/spec/bard/cli/master_key_spec.rb +5 -19
  65. data/spec/bard/cli/open_spec.rb +17 -35
  66. data/spec/bard/cli/ping_spec.rb +10 -25
  67. data/spec/bard/cli/run_spec.rb +10 -23
  68. data/spec/bard/cli/setup_spec.rb +10 -27
  69. data/spec/bard/cli/ssh_spec.rb +10 -25
  70. data/spec/bard/cli/stage_spec.rb +17 -32
  71. data/spec/bard/cli/vim_spec.rb +5 -11
  72. data/spec/bard/command_spec.rb +1 -10
  73. data/spec/bard/config_spec.rb +68 -116
  74. data/spec/bard/copy_spec.rb +54 -18
  75. data/spec/bard/deploy_strategy/ssh_spec.rb +65 -7
  76. data/spec/bard/deploy_strategy_spec.rb +1 -1
  77. data/spec/bard/dynamic_dsl_spec.rb +18 -98
  78. data/spec/bard/git_spec.rb +9 -5
  79. data/spec/bard/github_spec.rb +1 -1
  80. data/spec/bard/ping_spec.rb +5 -5
  81. data/spec/bard/ssh_copy_spec.rb +44 -0
  82. data/spec/bard/ssh_server_spec.rb +1 -98
  83. data/spec/bard/target_spec.rb +61 -108
  84. metadata +50 -124
  85. data/lib/bard/ci/retryable.rb +0 -27
  86. data/lib/bard/cli/ci.rb +0 -73
  87. data/lib/bard/cli/command.rb +0 -26
  88. data/lib/bard/cli/data.rb +0 -45
  89. data/lib/bard/cli/deploy.rb +0 -116
  90. data/lib/bard/cli/hurt.rb +0 -15
  91. data/lib/bard/cli/install.rb +0 -11
  92. data/lib/bard/cli/master_key.rb +0 -17
  93. data/lib/bard/cli/new.rb +0 -101
  94. data/lib/bard/cli/new_rails_template.rb +0 -197
  95. data/lib/bard/cli/open.rb +0 -18
  96. data/lib/bard/cli/ping.rb +0 -12
  97. data/lib/bard/cli/provision.rb +0 -34
  98. data/lib/bard/cli/run.rb +0 -26
  99. data/lib/bard/cli/setup.rb +0 -56
  100. data/lib/bard/cli/ssh.rb +0 -14
  101. data/lib/bard/cli/stage.rb +0 -35
  102. data/lib/bard/cli/vim.rb +0 -8
  103. data/lib/bard/default_config.rb +0 -35
  104. data/lib/bard/deploy_strategy/ssh.rb +0 -19
  105. data/lib/bard/deprecation.rb +0 -19
  106. data/lib/bard/github_pages.rb +0 -134
  107. data/lib/bard/plugin.rb +0 -100
  108. data/lib/bard/plugins/backup.rb +0 -19
  109. data/lib/bard/plugins/jenkins.rb +0 -6
  110. data/lib/bard/plugins/new.rb +0 -5
  111. data/lib/bard/plugins/provision.rb +0 -5
  112. data/lib/bard/provision/app.rb +0 -10
  113. data/lib/bard/provision/apt.rb +0 -16
  114. data/lib/bard/provision/authorizedkeys.rb +0 -25
  115. data/lib/bard/provision/data.rb +0 -27
  116. data/lib/bard/provision/deploy.rb +0 -10
  117. data/lib/bard/provision/http.rb +0 -16
  118. data/lib/bard/provision/logrotation.rb +0 -30
  119. data/lib/bard/provision/masterkey.rb +0 -18
  120. data/lib/bard/provision/mysql.rb +0 -22
  121. data/lib/bard/provision/passenger.rb +0 -37
  122. data/lib/bard/provision/repo.rb +0 -72
  123. data/lib/bard/provision/rvm.rb +0 -22
  124. data/lib/bard/provision/ssh.rb +0 -79
  125. data/lib/bard/provision/swapfile.rb +0 -23
  126. data/lib/bard/provision/user.rb +0 -42
  127. data/lib/bard/provision.rb +0 -16
  128. data/lib/bard/server.rb +0 -160
  129. data/spec/bard/cli/command_spec.rb +0 -50
  130. data/spec/bard/cli/new_spec.rb +0 -73
  131. data/spec/bard/cli/provision_spec.rb +0 -42
  132. data/spec/bard/deprecation_spec.rb +0 -281
  133. data/spec/bard/github_pages_spec.rb +0 -143
  134. data/spec/bard/plugin_spec.rb +0 -79
  135. data/spec/bard/provision/app_spec.rb +0 -33
  136. data/spec/bard/provision/apt_spec.rb +0 -39
  137. data/spec/bard/provision/authorizedkeys_spec.rb +0 -40
  138. data/spec/bard/provision/data_spec.rb +0 -54
  139. data/spec/bard/provision/deploy_spec.rb +0 -33
  140. data/spec/bard/provision/http_spec.rb +0 -57
  141. data/spec/bard/provision/logrotation_spec.rb +0 -34
  142. data/spec/bard/provision/masterkey_spec.rb +0 -63
  143. data/spec/bard/provision/mysql_spec.rb +0 -55
  144. data/spec/bard/provision/passenger_spec.rb +0 -81
  145. data/spec/bard/provision/repo_spec.rb +0 -208
  146. data/spec/bard/provision/rvm_spec.rb +0 -49
  147. data/spec/bard/provision/ssh_spec.rb +0 -242
  148. data/spec/bard/provision/swapfile_spec.rb +0 -33
  149. data/spec/bard/provision/user_spec.rb +0 -103
  150. data/spec/bard/provision_spec.rb +0 -28
  151. data/spec/bard/server_spec.rb +0 -127
  152. /data/lib/bard/{ci → plugins/deploy/ci}/state.rb +0 -0
  153. /data/{install_files → lib/bard/plugins/install}/apt_dependencies.rb +0 -0
  154. /data/{install_files → lib/bard/plugins/install}/ci +0 -0
  155. /data/{install_files → lib/bard/plugins/install}/setup +0 -0
  156. /data/{install_files → lib/bard/plugins/install}/specified_bundler.rb +0 -0
  157. /data/{install_files → lib/bard/plugins/install}/specified_ruby.rb +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e4a1a61baf060f0f93948d1fe64ae0439e40510fc68315375c758c9533f59ac6
4
- data.tar.gz: ca0e627b6c823a8922d1dbd7822633bb324d46ec994d53f20a339db074e51db0
3
+ metadata.gz: 24a9ee78c591a6446ac1bb749225497c75470e12e4fe822d96e975870ac88152
4
+ data.tar.gz: 2b8ae0acbf1237b3312957128130f399a516a3a19d52d45f63c7b0677952e4f2
5
5
  SHA512:
6
- metadata.gz: 476b6b23bdd7e5314ca7a389e42fd878a01568408ae3de1c5f6f91ed2086f59c5545100a0ccaa371b083d63e6800bea32b0d23ea3dcd85c0d2ace6ced944e28b
7
- data.tar.gz: 0fb206ae49740b070cea34d19b150da314cc4a8c41284886d45a261f934dcccf7b6e3dd7a507f1b69f23b429adb8b03992e1b8d746e52533e3a60aa19fdfdd0a
6
+ metadata.gz: 55a1a4f6fcf3bf12201105974a619be5178c3c9c6367d8dccf2eef1e7e504880a869a6291dfb5024930e025f63d8cf2f22c8925cf5fc24f9e83d2aa85ed047ed
7
+ data.tar.gz: 5da5d707ceed5ca663e4a68cd4a2ba2afbb232052e4a0542a4e41a1badc0161ba4eda410195b717d21f8fdaea299af771afa6a19310d93b66915996e8579b5fa
@@ -5,7 +5,7 @@ jobs:
5
5
  strategy:
6
6
  fail-fast: false
7
7
  matrix:
8
- ruby: [ "3.2", "3.3", "3.4" ]
8
+ ruby: [ "3.3", "3.4", "4.0" ]
9
9
 
10
10
  runs-on: ubuntu-latest
11
11
  steps:
@@ -36,7 +36,7 @@ jobs:
36
36
 
37
37
  - name: Build test container image
38
38
  run: |
39
- sudo podman pull ubuntu:22.04
39
+ sudo podman pull ubuntu:24.04
40
40
  sudo podman build -t bard-test-server -f spec/acceptance/docker/Dockerfile spec/acceptance/docker
41
41
 
42
42
  - name: Run tests
data/CLAUDE.md CHANGED
@@ -44,7 +44,7 @@ bundle exec bin/bard
44
44
  - **`Bard::CLI`** (`lib/bard/cli.rb`) - Thor-based command dispatcher. Commands are modules in `lib/bard/cli/` included into CLI.
45
45
  - **`Bard::Config`** (`lib/bard/config.rb`) - DSL parser for `bard.rb` files. Manages targets and settings.
46
46
  - **`Bard::Target`** (`lib/bard/target.rb`) - Deployment destination with capabilities (ssh, ping, data). Supports dynamic strategy DSL via `method_missing`.
47
- - **`Bard::Server`** (`lib/bard/server.rb`) - Legacy v1.x server representation (deprecated, use Target).
47
+ - **`Bard::SSHServer`** (`lib/bard/ssh_server.rb`) - SSH connection details (user, host, port, gateway, ssh_key, env).
48
48
  - **`Bard::DeployStrategy`** (`lib/bard/deploy_strategy.rb`) - Base class for deployment strategies. Subclasses auto-register via Ruby's `inherited` hook.
49
49
 
50
50
  ### Capability System
data/PLUGINS.md CHANGED
@@ -4,18 +4,37 @@ Bard uses a plugin system to extend functionality. Plugins can add CLI commands,
4
4
 
5
5
  ## Plugin Structure
6
6
 
7
- Plugins live in `lib/bard/plugins/` and register themselves:
7
+ Plugins live in `lib/bard/plugins/` and are auto-loaded. Command classes auto-register when they subclass `Bard::Plugin::Command`:
8
8
 
9
9
  ```ruby
10
10
  # lib/bard/plugins/my_plugin.rb
11
11
  require "bard/plugin"
12
12
 
13
- Bard::Plugin.register :my_plugin do
14
- # Add CLI commands (class must implement .setup)
15
- cli "Bard::CLI::MyPlugin", require: "bard/cli/my_plugin"
13
+ class Bard::CLI::MyPlugin < Bard::Plugin::Command
14
+ desc "mycommand", "Description of my command"
15
+ option :verbose, type: :boolean
16
+ def mycommand
17
+ puts "Hello from my plugin!"
18
+ puts config[:production].url
19
+ end
20
+ end
21
+ ```
22
+
23
+ That's it — no registration block needed. The `Command` base class provides:
24
+ - Automatic registration via Ruby's `inherited` hook
25
+ - `desc` and `option` class methods for Thor integration
26
+ - Delegation to the CLI instance (access to `config`, `project_name`, `run!`, `options`, etc.)
27
+
28
+ ## Extending Target and Config
16
29
 
17
- # Add methods to Target
18
- target_method :my_feature do |url = nil|
30
+ To add methods to `Bard::Target` or `Bard::Config`, reopen the class directly:
31
+
32
+ ```ruby
33
+ # lib/bard/plugins/my_plugin.rb
34
+ require "bard/target"
35
+
36
+ class Bard::Target
37
+ def my_feature(url = nil)
19
38
  if url.nil?
20
39
  @my_feature_url
21
40
  else
@@ -23,9 +42,12 @@ Bard::Plugin.register :my_plugin do
23
42
  enable_capability(:my_feature)
24
43
  end
25
44
  end
45
+ end
26
46
 
27
- # Add methods to Config
28
- config_method :my_global_setting do |value = nil|
47
+ require "bard/config"
48
+
49
+ class Bard::Config
50
+ def my_global_setting(value = nil)
29
51
  if value.nil?
30
52
  @my_global_setting
31
53
  else
@@ -35,43 +57,6 @@ Bard::Plugin.register :my_plugin do
35
57
  end
36
58
  ```
37
59
 
38
- ## CLI Commands
39
-
40
- CLI commands inherit from `Bard::CLI::Command` and implement a `setup` class method:
41
-
42
- ```ruby
43
- # lib/bard/cli/my_plugin.rb
44
- require "bard/cli/command"
45
-
46
- class Bard::CLI::MyPlugin < Bard::CLI::Command
47
- desc "mycommand", "Description of my command"
48
- def mycommand
49
- puts "Hello from my plugin!"
50
- end
51
- end
52
- ```
53
-
54
- The `Command` base class provides:
55
- - Automatic `setup` that registers the command with the CLI
56
- - Delegation to the CLI instance (access to `config`, `project_name`, etc.)
57
- - `desc` and `option` class methods for Thor integration
58
-
59
- For Thor subcommand groups (nested under `bard mygroup`):
60
-
61
- ```ruby
62
- # lib/bard/cli/my_subcommand.rb
63
- class Bard::CLI::MySubcommand < Thor
64
- def self.setup(cli)
65
- cli.register(self, "mygroup", "mygroup COMMAND", "My subcommand group")
66
- end
67
-
68
- desc "action", "Do something"
69
- def action
70
- puts "Hello from subcommand!"
71
- end
72
- end
73
- ```
74
-
75
60
  ## Adding Strategies
76
61
 
77
62
  Plugins can add deployment strategies or CI runners:
@@ -111,4 +96,4 @@ Strategies auto-register via Ruby's `inherited` hook. The class name determines
111
96
 
112
97
  ## Plugin Loading
113
98
 
114
- Plugins are loaded automatically from `lib/bard/plugins/`. The load order is alphabetical by filename. For CI runners, the last registered runner becomes the default.
99
+ Plugins are loaded automatically from `lib/bard/plugins/`. The load order is alphabetical by filename. External plugins are loaded from the project's `lib/bard/plugins/` directory.
data/bard.gemspec CHANGED
@@ -15,11 +15,11 @@ Gem::Specification.new do |spec|
15
15
  spec.files = `git ls-files -z`.split("\x0")
16
16
  spec.executables = ["bard"]
17
17
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.required_ruby_version = ">= 3.3"
18
19
  spec.require_paths = ["lib"]
19
20
 
20
21
  spec.add_dependency "thor", ">= 0.19.0"
21
22
  spec.add_dependency "rvm"
22
- spec.add_dependency "term-ansicolor", ">= 1.0.3"
23
23
  spec.add_dependency "rbnacl"
24
24
  spec.add_dependency "base64"
25
25
 
@@ -28,4 +28,5 @@ Gem::Specification.new do |spec|
28
28
  spec.add_development_dependency "debug"
29
29
  spec.add_development_dependency "cucumber"
30
30
  spec.add_development_dependency "testcontainers"
31
+ spec.add_development_dependency "ostruct"
31
32
  end
data/features/ci.feature CHANGED
@@ -1,3 +1,4 @@
1
+ @server
1
2
  Feature: bard ci
2
3
  Run continuous integration tests.
3
4
 
@@ -1,3 +1,4 @@
1
+ @server
1
2
  Feature: bard data
2
3
  Copy database from a remote server to local.
3
4
 
@@ -1,3 +1,4 @@
1
+ @server
1
2
  Feature: bard deploy
2
3
  Deploy code changes to a remote server.
3
4
 
@@ -1,3 +1,4 @@
1
+ @server
1
2
  Feature: bard deploy git workflow
2
3
  Git workflow behaviors during deploy.
3
4
 
data/features/run.feature CHANGED
@@ -1,3 +1,4 @@
1
+ @server
1
2
  Feature: bard run
2
3
  Execute commands on a remote server.
3
4
 
@@ -133,3 +133,4 @@ end
133
133
  Then /^the output should not contain "([^"]+)"$/ do |unexpected|
134
134
  expect(@stdout).not_to include(unexpected)
135
135
  end
136
+
@@ -53,7 +53,7 @@ module TestServerWorld
53
53
  return
54
54
  end
55
55
 
56
- system("podman pull ubuntu:22.04 >/dev/null 2>&1")
56
+ system("podman pull ubuntu:24.04 >/dev/null 2>&1")
57
57
 
58
58
  docker_dir = File.join(ROOT, "spec/acceptance/docker")
59
59
  unless system("podman build -t bard-test-server -f #{docker_dir}/Dockerfile #{docker_dir} 2>&1")
@@ -207,10 +207,10 @@ end
207
207
 
208
208
  World(TestServerWorld)
209
209
 
210
- Before do
210
+ Before("@server") do
211
211
  start_test_server
212
212
  end
213
213
 
214
- After do
214
+ After("@server") do
215
215
  stop_test_server
216
216
  end
data/lib/bard/cli.rb CHANGED
@@ -4,36 +4,11 @@ require "thor"
4
4
  require "bard/version"
5
5
  require "bard/config"
6
6
  require "bard/command"
7
- require "bard/plugin"
8
- require "term/ansicolor"
9
7
 
10
8
  module Bard
11
9
  class CLI < Thor
12
- include Term::ANSIColor
13
-
14
10
  class_option :verbose, type: :boolean, aliases: :v
15
11
 
16
- {
17
- data: "Data",
18
- stage: "Stage",
19
- deploy: "Deploy",
20
- ci: "CI",
21
- master_key: "MasterKey",
22
- setup: "Setup",
23
- run: "Run",
24
- ssh: "SSH",
25
- }.each do |command, klass|
26
- require "bard/cli/#{command}"
27
- include const_get(klass)
28
- end
29
-
30
- Plugin.load_all!
31
- Plugin.all.each { |plugin| plugin.apply_to_cli(self) }
32
-
33
- # Load core CI runners AFTER plugins so GithubActions is the default (last registered wins)
34
- require "bard/ci/local"
35
- require "bard/ci/github_actions"
36
-
37
12
  map "--version" => :version
38
13
  desc "version", "Display version"
39
14
  def version
@@ -43,6 +18,10 @@ module Bard
43
18
  def self.exit_on_failure? = true
44
19
 
45
20
  no_commands do
21
+ def red(text) = "\e[31m#{text}\e[0m"
22
+ def yellow(text) = "\e[33m#{text}\e[0m"
23
+ def green(text) = "\e[32m#{text}\e[0m"
24
+
46
25
  def run!(...)
47
26
  Bard::Command.run!(...)
48
27
  rescue Bard::Command::Error => e
@@ -51,13 +30,15 @@ module Bard
51
30
  end
52
31
 
53
32
  def config
54
- @config ||= Bard::Config.new(project_name, path: "bard.rb")
33
+ @config ||= Bard::Config.current
55
34
  end
56
35
 
57
36
  def project_name
58
- @project_name ||= File.expand_path(".").split("/").last
37
+ config.project_name
59
38
  end
60
39
  end
40
+
41
+ # load plugins from bard and other gems
42
+ Gem.find_files("bard/plugins/*.rb").sort.each { |path| require path }
61
43
  end
62
44
  end
63
-
data/lib/bard/command.rb CHANGED
@@ -1,43 +1,20 @@
1
1
  require "open3"
2
- require "shellwords"
3
2
 
4
3
  module Bard
5
- class Command < Struct.new(:command, :on, :home)
4
+ module Command
6
5
  class Error < RuntimeError; end
7
6
 
8
- def self.run! command, on: :local, home: false, verbose: false, quiet: false
9
- new(command, on, home).run! verbose:, quiet:
10
- end
11
-
12
- def self.run command, on: :local, home: false, verbose: false, quiet: false
13
- new(command, on, home).run verbose:, quiet:
14
- end
15
-
16
- def self.exec! command, on: :local, home: false
17
- new(command, on, home).exec!
18
- end
19
-
20
- def run! verbose: false, quiet: false
21
- result = run(verbose:, quiet:)
22
- raise Error.new(full_command) unless result
7
+ def self.run!(command, verbose: false, quiet: false)
8
+ result = run(command, verbose:, quiet:)
9
+ raise Error.new(command) unless result
23
10
  result
24
11
  end
25
12
 
26
- def run verbose: false, quiet: false
27
- # no-op if server doesn't really exist
28
- if on.to_sym != :local
29
- # Check for new Target architecture
30
- if on.respond_to?(:server) && on.server.nil?
31
- return true
32
- # Check for old Server architecture
33
- elsif on.respond_to?(:ssh) && on.ssh == false
34
- return true
35
- end
36
- end
13
+ def self.run(command, verbose: false, quiet: false)
37
14
  if verbose
38
- system full_command(quiet: quiet)
15
+ system command
39
16
  else
40
- stdout, stderr, status = Open3.capture3(full_command)
17
+ stdout, stderr, status = Open3.capture3(command)
41
18
  failed = status.to_i.nonzero?
42
19
  if failed && !quiet
43
20
  $stdout.puts stdout
@@ -47,64 +24,8 @@ module Bard
47
24
  end
48
25
  end
49
26
 
50
- def exec!
51
- exec full_command
52
- end
53
-
54
- private
55
-
56
- def full_command quiet: false
57
- if on.to_sym == :local
58
- command
59
- else
60
- remote_command quiet: false
61
- end
62
- end
63
-
64
- def remote_command quiet: false
65
- # Support both new Target (with server attribute) and old Server architecture
66
- ssh_server = on.respond_to?(:server) ? on.server : on
67
-
68
- # Get options from Target first (for deprecated separate method calls), fall back to SSHServer
69
- env_value = on.respond_to?(:env) ? on.env : nil
70
- env_value ||= ssh_server.env if ssh_server.respond_to?(:env)
71
-
72
- ssh_key = on.respond_to?(:ssh_key) ? on.ssh_key : nil
73
- ssh_key ||= ssh_server.ssh_key if ssh_server.respond_to?(:ssh_key)
74
-
75
- gateway = on.respond_to?(:gateway) ? on.gateway : nil
76
- gateway ||= ssh_server.gateway if ssh_server.respond_to?(:gateway)
77
-
78
- cmd = command
79
- cmd = "#{env_value} #{command}" if env_value
80
-
81
- unless home
82
- path = on.respond_to?(:path) ? on.path : ssh_server.path
83
- cmd = "cd #{path} && #{cmd}" if path
84
- end
85
-
86
- ssh_opts = ["-tt", "-o StrictHostKeyChecking=no", "-o UserKnownHostsFile=/dev/null", "-o LogLevel=ERROR"]
87
- ssh_opts << "-i #{ssh_key}" if ssh_key
88
-
89
- # Handle new SSHServer vs old Server architecture
90
- if ssh_server.respond_to?(:host)
91
- # New SSHServer - has separate host/port/user
92
- ssh_opts << "-p #{ssh_server.port}" if ssh_server.port && ssh_server.port != "22"
93
- ssh_opts << "-o ProxyJump=#{gateway}" if gateway
94
- ssh_target = "#{ssh_server.user}@#{ssh_server.host}"
95
- else
96
- # Old Server - uses URI-based ssh_uri
97
- ssh_target = ssh_server.ssh_uri
98
- if gateway
99
- gateway_uri = ssh_server.ssh_uri(:gateway)
100
- ssh_opts << "-o ProxyJump=#{gateway_uri}"
101
- end
102
- end
103
-
104
- cmd = "ssh #{ssh_opts.join(' ')} #{ssh_target} #{Shellwords.shellescape(cmd)}"
105
-
106
- cmd += " 2>&1" if quiet
107
- cmd
27
+ def self.exec!(command)
28
+ Kernel.exec command
108
29
  end
109
30
  end
110
31
  end
data/lib/bard/config.rb CHANGED
@@ -1,218 +1,79 @@
1
- require "bard/server"
2
1
  require "bard/target"
3
- require "bard/deprecation"
2
+ require "bard/plugins/ssh/target_methods"
3
+ require "bard/plugins/ping/target_methods"
4
4
 
5
5
  module Bard
6
6
  class Config
7
- def self.current(working_directory: Dir.getwd)
8
- project_name = File.basename(working_directory)
9
- path = File.join(working_directory, "bard.rb")
10
- new(project_name, path: path)
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)
11
19
  end
12
20
 
13
21
  attr_reader :project_name, :targets
14
22
 
15
23
  def initialize(project_name = nil, path: nil, source: nil)
16
- # Support both positional and keyword argument for project_name
17
24
  @project_name = project_name
18
- @servers = {} # Unified hash for both Server and Target instances
19
- @data_paths = []
20
- @backup = nil
21
- @ci_system = nil
22
-
23
- # Load default configuration (creates Server instances for backward compat)
25
+ @targets = {}
24
26
  load_defaults if project_name
25
27
 
26
- # Load user configuration
27
28
  if path && File.exist?(path)
28
29
  source = File.read(path)
29
30
  end
30
31
  if source
31
- instance_eval(source)
32
+ instance_eval(source, path)
32
33
  end
33
34
  end
34
35
 
35
- # Backward compatible accessor
36
- def servers
37
- @servers
38
- end
39
-
40
- # New v2.0 accessor (same as servers)
41
- def targets
42
- @servers
43
- end
44
-
45
- # Old v1.x API - creates Server instances
46
- def server(key, &block)
47
- Deprecation.warn "`server` is deprecated; use `target` instead (will be removed in v2.0)"
48
- key = key.to_sym
49
- @servers[key] = Server.define(project_name, key, &block)
50
- end
51
-
52
36
  def remove_target(key)
53
- @servers.delete(key.to_sym)
37
+ @targets.delete(key.to_sym)
54
38
  end
55
39
 
56
- # New v2.0 API - creates Target instances
57
40
  def target(key, &block)
58
41
  key = key.to_sym
59
- unless @servers[key].is_a?(Target)
60
- @servers[key] = Target.new(key, self)
42
+ unless @targets[key].is_a?(Target)
43
+ @targets[key] = Target.new(key, self)
61
44
  end
62
- @servers[key].instance_eval(&block) if block
63
- @servers[key]
45
+ @targets[key].instance_eval(&block) if block
46
+ @targets[key]
64
47
  end
65
48
 
66
- # Get a server/target by key
67
49
  def [](key)
68
- key = key.to_sym
69
- # Fallback to staging if production not defined
70
- if @servers[key].nil? && key == :production
71
- key = :staging
72
- end
73
- @servers[key]
50
+ @targets[key.to_sym]
74
51
  end
75
52
 
76
- # Data paths configuration
77
- def data(*paths)
78
- if paths.empty?
79
- @data_paths
80
- else
81
- @data_paths = paths
82
- end
83
- end
84
-
85
- def data_paths
86
- @data_paths
87
- end
88
-
89
- def backup(value = nil, &block)
90
- if block_given?
91
- @backup = BackupConfig.new(&block)
92
- elsif value == false
93
- @backup = BackupConfig.new { disabled }
94
- elsif value.nil? # Getter
95
- @backup ||= BackupConfig.new { bard }
96
- else
97
- raise ArgumentError, "backup accepts false or a block"
98
- end
99
- end
53
+ private
100
54
 
101
- def backup_enabled?
102
- backup == true
103
- end
55
+ def load_defaults
56
+ target :local
104
57
 
105
- def github_pages url
106
- urls = []
107
- uri = url.start_with?("http") ? URI.parse(url) : URI.parse("https://#{url}")
108
- hostname = uri.hostname.sub(/^www\./, '')
109
- urls = [hostname]
110
- if hostname.count(".") < 2
111
- urls << "www.#{hostname}"
58
+ target :gubs do
59
+ ssh "botandrose@cloud.hackett.world:22022",
60
+ path: "Sites/#{config.project_name}"
61
+ url false
112
62
  end
113
63
 
114
- target :production do
115
- github_pages url
116
- ssh false
117
- ping(*urls) if urls.any?
64
+ target :ci do
65
+ ssh "jenkins@staging.botandrose.com:22022",
66
+ path: "jobs/#{config.project_name}/workspace"
67
+ url false
118
68
  end
119
69
 
120
- backup false
121
- end
122
-
123
- # CI configuration
124
- def ci(system = nil)
125
- if system.nil?
126
- @ci_system
127
- else
128
- @ci_system = system
70
+ staging_defaults = proc do
71
+ ssh "www@staging.botandrose.com:22022"
72
+ url "#{config.project_name}.botandrose.com"
129
73
  end
130
- end
131
-
132
- def ci_system
133
- @ci_system
134
- end
135
-
136
- def ci_instance(branch)
137
- return nil if @ci_system == false
138
-
139
- require "bard/ci"
140
- CI.new(project_name, branch, runner_name: @ci_system)
141
- end
142
-
143
- private
144
-
145
- # Load default server configurations (v1.x compatible)
146
- def load_defaults
147
- @servers[:local] = Server.new(
148
- project_name,
149
- :local,
150
- false,
151
- "./",
152
- ["#{project_name}.local"],
153
- )
154
- @servers[:gubs] = Server.new(
155
- project_name,
156
- :gubs,
157
- "botandrose@cloud.hackett.world:22022",
158
- "Sites/#{project_name}",
159
- false,
160
- )
161
- @servers[:ci] = Server.new(
162
- project_name,
163
- :ci,
164
- "jenkins@staging.botandrose.com:22022",
165
- "jobs/#{project_name}/workspace",
166
- false,
167
- )
168
- @servers[:staging] = Server.new(
169
- project_name,
170
- :staging,
171
- "www@staging.botandrose.com:22022",
172
- project_name,
173
- ["#{project_name}.botandrose.com"],
174
- )
175
- end
176
- end
177
-
178
- class BackupConfig
179
- attr_reader :destinations
180
-
181
- def initialize(&block)
182
- @destinations = []
183
- instance_eval(&block) if block_given?
184
- end
185
-
186
- def bard
187
- @bard = true
188
- end
189
-
190
- def bard?
191
- !!@bard
192
- end
193
-
194
- def disabled
195
- @disabled = true
196
- end
197
-
198
- def disabled?
199
- !!@disabled
200
- end
201
-
202
- def enabled?
203
- !disabled?
204
- end
205
-
206
- def s3(name, **kwargs)
207
- @destinations << {
208
- name: name,
209
- type: :s3,
210
- **kwargs,
211
- }
212
- end
213
74
 
214
- def self_managed?
215
- @destinations.any?
75
+ target :staging, &staging_defaults
76
+ target :production, &staging_defaults
216
77
  end
217
78
  end
218
79
  end