bard 1.9.6 → 2.0.0.beta

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 (106) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +0 -5
  3. data/MIGRATION_GUIDE.md +9 -24
  4. data/README.md +6 -14
  5. data/README.rdoc +15 -0
  6. data/Rakefile +1 -3
  7. data/features/bard_check.feature +94 -0
  8. data/features/bard_deploy.feature +18 -0
  9. data/features/bard_pull.feature +112 -0
  10. data/features/bard_push.feature +112 -0
  11. data/features/podman_testcontainers.feature +16 -0
  12. data/features/step_definitions/check_steps.rb +47 -0
  13. data/features/step_definitions/git_steps.rb +73 -0
  14. data/features/step_definitions/global_steps.rb +56 -0
  15. data/features/step_definitions/podman_steps.rb +23 -0
  16. data/features/step_definitions/rails_steps.rb +44 -0
  17. data/features/step_definitions/submodule_steps.rb +110 -0
  18. data/features/support/env.rb +39 -14
  19. data/features/support/grit_ext.rb +13 -0
  20. data/features/support/io.rb +32 -0
  21. data/features/support/podman.rb +153 -0
  22. data/lib/bard/ci/github_actions.rb +2 -1
  23. data/lib/bard/ci/jenkins.rb +11 -82
  24. data/lib/bard/ci/local.rb +6 -6
  25. data/lib/bard/ci/runner.rb +1 -35
  26. data/lib/bard/ci.rb +23 -11
  27. data/lib/bard/cli/ci.rb +38 -45
  28. data/lib/bard/cli/deploy.rb +9 -40
  29. data/lib/bard/cli/hurt.rb +15 -10
  30. data/lib/bard/cli/install.rb +12 -7
  31. data/lib/bard/cli/open.rb +16 -12
  32. data/lib/bard/cli/ping.rb +14 -8
  33. data/lib/bard/cli/provision.rb +1 -1
  34. data/lib/bard/cli/run.rb +3 -5
  35. data/lib/bard/cli/stage.rb +1 -9
  36. data/lib/bard/cli/vim.rb +10 -5
  37. data/lib/bard/cli.rb +11 -13
  38. data/lib/bard/command.rb +13 -33
  39. data/lib/bard/config.rb +14 -10
  40. data/lib/bard/copy.rb +33 -12
  41. data/lib/bard/deploy_strategy/github_pages.rb +2 -10
  42. data/lib/bard/deploy_strategy.rb +0 -3
  43. data/lib/bard/github.rb +4 -2
  44. data/lib/bard/provision/apt.rb +1 -1
  45. data/lib/bard/provision/logrotation.rb +1 -1
  46. data/lib/bard/provision/mysql.rb +2 -2
  47. data/lib/bard/provision/passenger.rb +2 -2
  48. data/lib/bard/provision/repo.rb +2 -2
  49. data/lib/bard/provision/ssh.rb +2 -9
  50. data/lib/bard/provision/swapfile.rb +1 -3
  51. data/lib/bard/server.rb +3 -46
  52. data/lib/bard/ssh_server.rb +2 -9
  53. data/lib/bard/target.rb +16 -67
  54. data/lib/bard/version.rb +1 -1
  55. data/spec/acceptance/docker/Dockerfile +1 -2
  56. data/spec/bard/ci/github_actions_spec.rb +13 -116
  57. data/spec/bard/cli/ci_spec.rb +8 -34
  58. data/spec/bard/cli/deploy_spec.rb +8 -46
  59. data/spec/bard/cli/hurt_spec.rb +2 -2
  60. data/spec/bard/cli/install_spec.rb +4 -4
  61. data/spec/bard/cli/open_spec.rb +8 -10
  62. data/spec/bard/cli/ping_spec.rb +5 -5
  63. data/spec/bard/cli/run_spec.rb +1 -20
  64. data/spec/bard/cli/stage_spec.rb +0 -20
  65. data/spec/bard/cli/vim_spec.rb +5 -5
  66. data/spec/bard/command_spec.rb +1 -3
  67. data/spec/bard/config_spec.rb +0 -28
  68. data/spec/bard/copy_spec.rb +3 -3
  69. data/spec/bard/github_spec.rb +1 -1
  70. data/spec/bard/provision/apt_spec.rb +1 -1
  71. data/spec/bard/provision/logrotation_spec.rb +1 -1
  72. data/spec/bard/provision/passenger_spec.rb +1 -1
  73. data/spec/bard/provision/repo_spec.rb +1 -1
  74. data/spec/bard/provision/ssh_spec.rb +4 -17
  75. data/spec/bard/provision/swapfile_spec.rb +1 -2
  76. data/spec/bard/ssh_server_spec.rb +8 -12
  77. data/spec/bard/target_spec.rb +5 -9
  78. data/spec/spec_helper.rb +1 -6
  79. metadata +31 -41
  80. data/CLAUDE.md +0 -76
  81. data/PLUGINS.md +0 -114
  82. data/cucumber.yml +0 -1
  83. data/features/ci.feature +0 -62
  84. data/features/data.feature +0 -12
  85. data/features/deploy.feature +0 -13
  86. data/features/deploy_git_workflow.feature +0 -88
  87. data/features/run.feature +0 -13
  88. data/features/step_definitions/bard_steps.rb +0 -135
  89. data/features/support/bard-coverage +0 -16
  90. data/features/support/test_server.rb +0 -216
  91. data/lib/bard/deprecation.rb +0 -19
  92. data/lib/bard/plugin.rb +0 -100
  93. data/lib/bard/plugins/backup.rb +0 -19
  94. data/lib/bard/plugins/github_pages.rb +0 -34
  95. data/lib/bard/plugins/hurt.rb +0 -5
  96. data/lib/bard/plugins/install.rb +0 -5
  97. data/lib/bard/plugins/jenkins.rb +0 -6
  98. data/lib/bard/plugins/new.rb +0 -5
  99. data/lib/bard/plugins/ping.rb +0 -6
  100. data/lib/bard/plugins/provision.rb +0 -5
  101. data/lib/bard/plugins/vim.rb +0 -5
  102. data/lib/bard/secrets.rb +0 -10
  103. data/spec/bard/ci/jenkins_spec.rb +0 -139
  104. data/spec/bard/ci/runner_spec.rb +0 -61
  105. data/spec/bard/deprecation_spec.rb +0 -281
  106. data/spec/bard/plugin_spec.rb +0 -79
data/lib/bard/config.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  require "bard/server"
2
2
  require "bard/target"
3
- require "bard/deprecation"
4
3
 
5
4
  module Bard
6
5
  class Config
@@ -44,21 +43,14 @@ module Bard
44
43
 
45
44
  # Old v1.x API - creates Server instances
46
45
  def server(key, &block)
47
- Deprecation.warn "`server` is deprecated; use `target` instead (will be removed in v2.0)"
48
46
  key = key.to_sym
49
47
  @servers[key] = Server.define(project_name, key, &block)
50
48
  end
51
49
 
52
- def remove_target(key)
53
- @servers.delete(key.to_sym)
54
- end
55
-
56
50
  # New v2.0 API - creates Target instances
57
51
  def target(key, &block)
58
52
  key = key.to_sym
59
- unless @servers[key].is_a?(Target)
60
- @servers[key] = Target.new(key, self)
61
- end
53
+ @servers[key] ||= Target.new(key, self)
62
54
  @servers[key].instance_eval(&block) if block
63
55
  @servers[key]
64
56
  end
@@ -137,7 +129,19 @@ module Bard
137
129
  return nil if @ci_system == false
138
130
 
139
131
  require "bard/ci"
140
- CI.new(project_name, branch, runner_name: @ci_system)
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)
144
+ end
141
145
  end
142
146
 
143
147
  private
data/lib/bard/copy.rb CHANGED
@@ -29,16 +29,10 @@ module Bard
29
29
 
30
30
  ssh_key = ssh_server.ssh_key ? "-i #{ssh_server.ssh_key}" : ""
31
31
 
32
- ssh_opts = "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR"
33
-
34
- # scp uses -P for port (uppercase, unlike ssh's -p)
35
- port = ssh_server.port
36
- port_opt = port && port.to_s != "22" ? "-P #{port}" : ""
37
-
38
- from_and_to = [path, target_or_server.scp_uri(path).to_s]
32
+ from_and_to = [path, target_or_server.scp_uri(path)]
39
33
  from_and_to.reverse! if direction == :from
40
34
 
41
- command = ["scp", ssh_opts, gateway, ssh_key, port_opt, *from_and_to].reject(&:empty?).join(" ")
35
+ command = ["scp", gateway, ssh_key, *from_and_to].join(" ")
42
36
  Bard::Command.run! command, verbose: verbose
43
37
  end
44
38
 
@@ -65,7 +59,18 @@ module Bard
65
59
  # Support both new Target (with server attribute) and old Server
66
60
  ssh_server = target_or_server.respond_to?(:server) ? target_or_server.server : target_or_server
67
61
 
68
- ssh_uri = ssh_server.ssh_uri
62
+ # Get ssh_uri - it might be a URI object (old Server), string (new SSHServer), or mock
63
+ ssh_uri_value = ssh_server.respond_to?(:ssh_uri) ? ssh_server.ssh_uri : nil
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
73
+ end
69
74
 
70
75
  gateway = ssh_server.gateway ? "-oProxyCommand=\"ssh #{ssh_server.gateway} -W %h:%p\"" : ""
71
76
 
@@ -86,13 +91,29 @@ module Bard
86
91
 
87
92
  raise NotImplementedError if from_server.gateway || to_server.gateway || from_server.ssh_key || to_server.ssh_key
88
93
 
89
- from_uri = from_server.ssh_uri
90
- to_uri = to_server.ssh_uri
94
+ # Get ssh_uri - it might be a URI object (old Server), string (new SSHServer), or mock
95
+ from_uri_value = from_server.respond_to?(:ssh_uri) ? from_server.ssh_uri : nil
96
+ if from_uri_value.respond_to?(:port)
97
+ from_uri = from_uri_value
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
91
112
 
92
113
  from_str = "-p#{from_uri.port || 22} #{from_uri.user}@#{from_uri.host}"
93
114
  to_str = to.rsync_uri(path).sub(%r(/[^/]+$), '/')
94
115
 
95
- command = %(ssh -A #{from_str} 'rsync -e \"ssh -A -p#{to_uri.port || 22} -o StrictHostKeyChecking=no -o LogLevel=ERROR\" --delete --info=progress2 -az #{from.path}/#{path} #{to_str}')
116
+ command = %(ssh -A #{from_str} 'rsync -e \"ssh -A -p#{to_uri.port || 22} -o StrictHostKeyChecking=no\" --delete --info=progress2 -az #{from.path}/#{path} #{to_str}')
96
117
  Bard::Command.run! command, verbose: verbose
97
118
  end
98
119
  end
@@ -8,7 +8,7 @@ module Bard
8
8
  class GithubPages < DeployStrategy
9
9
  def initialize(target, url = nil, **options)
10
10
  super(target)
11
- @url = url || target.github_pages
11
+ @url = url
12
12
  @options = options
13
13
 
14
14
  # Auto-configure ping URL if provided
@@ -65,15 +65,7 @@ module Bard
65
65
  ensure
66
66
  # cleanup
67
67
  run! <<~SH
68
- PID=$(cat tmp/pids/server.pid 2>/dev/null)
69
- if [ -n "$PID" ]; then
70
- kill $PID 2>/dev/null
71
- for i in 1 2 3 4 5; do
72
- kill -0 $PID 2>/dev/null || break
73
- sleep 1
74
- done
75
- kill -9 $PID 2>/dev/null || true
76
- fi
68
+ cat tmp/pids/server.pid | xargs -I {} kill {}
77
69
  rm -rf public/assets
78
70
  SH
79
71
  end
@@ -58,6 +58,3 @@ module Bard
58
58
  end
59
59
  end
60
60
  end
61
-
62
- # Auto-load all strategy files
63
- Dir[File.join(__dir__, "deploy_strategy", "*.rb")].each { |f| require f }
data/lib/bard/github.rb CHANGED
@@ -3,7 +3,6 @@ require "json"
3
3
  require "base64"
4
4
  require "rbnacl"
5
5
  require "bard/ci/retryable"
6
- require "bard/secrets"
7
6
 
8
7
  module Bard
9
8
  class Github < Struct.new(:project_name)
@@ -108,7 +107,10 @@ module Bard
108
107
  private
109
108
 
110
109
  def api_key
111
- @api_key ||= Bard::Secrets.fetch("github-apikey")
110
+ @api_key ||= begin
111
+ raw = `git ls-remote -t git@github.com:botandrosedesign/secrets`
112
+ raw[/github-apikey\|(.+)$/, 1]
113
+ end
112
114
  end
113
115
 
114
116
  def request path, &block
@@ -7,7 +7,7 @@ class Bard::Provision::Apt < Bard::Provision
7
7
  %(echo "\\$nrconf{restart} = \\"a\\";" | sudo tee /etc/needrestart/conf.d/90-autorestart.conf),
8
8
  "sudo apt-get update -y",
9
9
  "sudo apt-get upgrade -y",
10
- "sudo apt-get install -y curl build-essential",
10
+ "sudo apt-get install -y curl",
11
11
  ].join("; "), home: true
12
12
 
13
13
  puts " ✓"
@@ -5,7 +5,7 @@ class Bard::Provision::LogRotation < Bard::Provision
5
5
  print "Log Rotation:"
6
6
 
7
7
  provision_server.run! <<~SH, quiet: true
8
- file=/etc/logrotate.d/#{config.project_name}
8
+ file=/etc/logrotate.d/#{server.project_name}
9
9
  if [ ! -f $file ]; then
10
10
  sudo tee $file > /dev/null <<EOF
11
11
  $(pwd)/log/*.log {
@@ -7,8 +7,8 @@ class Bard::Provision::MySQL < Bard::Provision
7
7
  print " Installing,"
8
8
  provision_server.run! [
9
9
  "sudo apt-get install -y mysql-server",
10
- %{sudo mysql -uroot -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '' PASSWORD EXPIRE NEVER; FLUSH PRIVILEGES;"},
11
- %{mysql -uroot -e "UPDATE mysql.user SET password_lifetime = NULL WHERE user = 'root' AND host = 'localhost';"},
10
+ %(sudo mysql -uroot -e "ALTER USER \\"'\\"root\\"'\\"@\\"'\\"localhost\\"'\\" IDENTIFIED WITH mysql_native_password BY \\"'\\"\\"'\\", \\"'\\"root\\"'\\"@\\"'\\"localhost\\"'\\" PASSWORD EXPIRE NEVER; FLUSH PRIVILEGES;"),
11
+ %(mysql -uroot -e "UPDATE mysql.user SET password_lifetime = NULL WHERE user = \\"'\\"root\\"'\\" AND host = \\"'\\"localhost\\"'\\";"),
12
12
  ].join("; "), home: true
13
13
  end
14
14
 
@@ -9,7 +9,7 @@ class Bard::Provision::Passenger < Bard::Provision
9
9
  %(grep -qxF "RAILS_ENV=production" /etc/environment || echo "RAILS_ENV=production" | sudo tee -a /etc/environment),
10
10
  %(grep -qxF "EDITOR=vim" /etc/environment || echo "EDITOR=vim" | sudo tee -a /etc/environment),
11
11
  "sudo apt-get install -y vim dirmngr gnupg apt-transport-https ca-certificates",
12
- "curl https://oss-binaries.phusionpassenger.com/auto-software-signing-gpg-key-2025.txt | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/phusion.gpg >/dev/null",
12
+ "curl https://oss-binaries.phusionpassenger.com/auto-software-signing-gpg-key.txt | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/phusion.gpg >/dev/null",
13
13
  %(echo "deb https://oss-binaries.phusionpassenger.com/apt/passenger jammy main" | sudo tee /etc/apt/sources.list.d/passenger.list),
14
14
  "sudo apt-get update -y",
15
15
  "sudo apt-get install -y nginx libnginx-mod-http-passenger",
@@ -30,7 +30,7 @@ class Bard::Provision::Passenger < Bard::Provision
30
30
  end
31
31
 
32
32
  def app_configured?
33
- provision_server.run "[ -f /etc/nginx/sites-enabled/#{config.project_name} ]", quiet: true
33
+ provision_server.run "[ -f /etc/nginx/sites-enabled/#{server.project_name} ]", quiet: true
34
34
  end
35
35
  end
36
36
 
@@ -16,7 +16,7 @@ class Bard::Provision::Repo < Bard::Provision
16
16
  print " Add public key to GitHub repo deploy keys,"
17
17
  title = "#{server.ssh_uri.user}@#{server.ssh_uri.host}"
18
18
  key = provision_server.run "cat ~/.ssh/id_rsa.pub", home: true
19
- Bard::Github.new(config.project_name).add_deploy_key title:, key:
19
+ Bard::Github.new(server.project_name).add_deploy_key title:, key:
20
20
  end
21
21
  print " Cloning repo,"
22
22
  provision_server.run! "git clone git@github.com:botandrosedesign/#{project_name}", home: true
@@ -50,7 +50,7 @@ class Bard::Provision::Repo < Bard::Provision
50
50
  end
51
51
 
52
52
  def project_name
53
- config.project_name
53
+ server.project_name
54
54
  end
55
55
 
56
56
  def on_latest_master?
@@ -19,14 +19,7 @@ class Bard::Provision::SSH < Bard::Provision
19
19
  add_ssh_known_host!(provision_server.ssh_uri)
20
20
  end
21
21
  print " Reconfiguring port to #{target_port},"
22
- provision_server.run! %(echo "Port #{target_port}" | sudo tee /etc/ssh/sshd_config.d/port_#{target_port}.conf && sudo service ssh restart), home: true
23
- 5.times do
24
- sleep 1
25
- break if ssh_available?(provision_server.ssh_uri, port: target_port)
26
- end
27
- if !ssh_available?(provision_server.ssh_uri, port: target_port)
28
- raise "reconfigured SSH to port #{target_port} but it's not responding — check firewall and sshd_config Include directive"
29
- end
22
+ provision_server.run! %(echo "Port #{target_port}" | sudo tee /etc/ssh/sshd_config.d/port_#{target_port}.conf; sudo service ssh restart), home: true
30
23
  end
31
24
 
32
25
  if !ssh_known_host?(provision_server.ssh_uri)
@@ -72,7 +65,7 @@ class Bard::Provision::SSH < Bard::Provision
72
65
 
73
66
  def disable_password_auth!
74
67
  provision_server.run!(
75
- %q{echo "PasswordAuthentication no" | sudo tee /etc/ssh/sshd_config.d/disable_password_auth.conf && sudo service ssh restart},
68
+ %q{echo "PasswordAuthentication no" | sudo tee /etc/ssh/sshd_config.d/disable_password_auth.conf; sudo service ssh restart},
76
69
  home: true
77
70
  )
78
71
  end
@@ -4,7 +4,7 @@ class Bard::Provision::Swapfile < Bard::Provision
4
4
  def call
5
5
  print "Swapfile:"
6
6
 
7
- provision_server.run! <<~SH, home: true
7
+ provision_server.run! <<~SH
8
8
  if [ ! -f /swapfile ]; then
9
9
  sudo fallocate -l $(grep MemTotal /proc/meminfo | awk '{print $2}')K /swapfile
10
10
  fi
@@ -14,8 +14,6 @@ class Bard::Provision::Swapfile < Bard::Provision
14
14
  grep -q '/swapfile none swap sw 0 0' /etc/fstab || echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
15
15
  SH
16
16
 
17
- provision_server.run! "sudo swapon --show | grep -q /swapfile", home: true
18
-
19
17
  puts " ✓"
20
18
  end
21
19
  end
data/lib/bard/server.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  require "uri"
2
2
  require "bard/command"
3
3
  require "bard/copy"
4
- require "bard/deprecation"
5
4
 
6
5
  module Bard
7
6
  class Server < Struct.new(:project_name, :key, :ssh, :path, :ping, :gateway, :ssh_key, :env, :github_pages)
@@ -25,24 +24,7 @@ module Bard
25
24
  end
26
25
  end
27
26
 
28
- def self.setting_with_deprecation *fields, message:
29
- fields.each do |field|
30
- define_method field do |*args|
31
- if args.length == 1
32
- Deprecation.warn message
33
- send :"#{field}=", args.first
34
- elsif args.length == 0
35
- super()
36
- else
37
- raise ArgumentError
38
- end
39
- end
40
- end
41
- end
42
-
43
- setting :ssh, :ping, :github_pages
44
- setting_with_deprecation :gateway, :ssh_key, :env,
45
- message: "Separate SSH options are deprecated; pass as keyword arguments to `ssh` instead, e.g., `ssh \"user@host\", path: \"/app\"` (will be removed in v2.0)"
27
+ setting :ssh, :path, :ping, :gateway, :ssh_key, :env, :github_pages
46
28
 
47
29
  def ping(*args)
48
30
  if args.length == 0
@@ -68,7 +50,6 @@ module Bard
68
50
 
69
51
  def path(*args)
70
52
  if args.length == 1
71
- Deprecation.warn "Separate SSH options are deprecated; pass as keyword arguments to `ssh` instead, e.g., `ssh \"user@host\", path: \"/app\"` (will be removed in v2.0)"
72
53
  self.path = args.first
73
54
  elsif args.length == 0
74
55
  super() || project_name
@@ -77,34 +58,11 @@ module Bard
77
58
  end
78
59
  end
79
60
 
80
- def strategy(name)
81
- Deprecation.warn "`strategy` is deprecated; use the strategy method directly, e.g., `jets \"url\"` instead of `strategy :jets` (will be removed in v2.0)"
82
- @strategy = name
83
- end
84
-
85
- def option(key, value)
86
- Deprecation.warn "`option` is deprecated; pass options as keyword arguments to the strategy method, e.g., `jets \"url\", run_tests: true` (will be removed in v2.0)"
87
- @options ||= {}
88
- @options[key] = value
89
- end
90
-
91
- def strategy_name
92
- @strategy
93
- end
94
-
95
- def strategy_options
96
- @options || {}
97
- end
98
-
99
61
  def ssh_uri which=:ssh
100
62
  value = send(which)
101
63
  URI("ssh://#{value}")
102
64
  end
103
65
 
104
- def port
105
- ssh_uri.port || 22
106
- end
107
-
108
66
  def scp_uri file_path=nil
109
67
  ssh_uri.dup.tap do |uri|
110
68
  uri.scheme = "scp"
@@ -135,9 +93,8 @@ module Bard
135
93
  key
136
94
  end
137
95
 
138
- def run! command, home: false, verbose: false, quiet: false, capture: false
139
- result = Bard::Command.run!(command, on: self, home:, verbose:, quiet:)
140
- result if capture
96
+ def run! command, home: false, verbose: false, quiet: false
97
+ Bard::Command.run! command, on: self, home:, verbose:, quiet:
141
98
  end
142
99
 
143
100
  def run command, home: false, verbose: false, quiet: false
@@ -1,5 +1,4 @@
1
1
  require "uri"
2
- require "shellwords"
3
2
  require "bard/command"
4
3
 
5
4
  module Bard
@@ -24,19 +23,13 @@ module Bard
24
23
  end
25
24
 
26
25
  def ssh_uri
27
- URI("ssh://#{user}@#{host}:#{port}")
26
+ "#{user}@#{host}:#{port}"
28
27
  end
29
28
 
30
29
  def hostname
31
30
  host
32
31
  end
33
32
 
34
- def to_s
35
- str = "#{user}@#{host}"
36
- str += ":#{port}" if port && port != "22"
37
- str
38
- end
39
-
40
33
  def connection_string
41
34
  "#{user}@#{host}"
42
35
  end
@@ -100,7 +93,7 @@ module Bard
100
93
  remote_cmd += "cd #{path} && " if path
101
94
  remote_cmd += command
102
95
 
103
- cmd += " #{Shellwords.shellescape(remote_cmd)}"
96
+ cmd += " '#{remote_cmd}'"
104
97
  cmd
105
98
  end
106
99
  end
data/lib/bard/target.rb CHANGED
@@ -2,12 +2,11 @@ require "uri"
2
2
  require "bard/command"
3
3
  require "bard/copy"
4
4
  require "bard/deploy_strategy"
5
- require "bard/deprecation"
6
5
 
7
6
  module Bard
8
7
  class Target
9
- attr_reader :key, :config
10
- attr_accessor :server
8
+ attr_reader :key, :config, :path
9
+ attr_accessor :server, :gateway, :ssh_key, :env
11
10
 
12
11
  def initialize(key, config)
13
12
  @key = key
@@ -68,7 +67,7 @@ module Bard
68
67
 
69
68
  # Auto-configure ping from hostname
70
69
  hostname = @server.hostname
71
- ping("https://#{hostname}") if hostname
70
+ ping(hostname) if hostname
72
71
  end
73
72
  end
74
73
 
@@ -79,46 +78,17 @@ module Bard
79
78
  # Path configuration
80
79
  def path(new_path = nil)
81
80
  if new_path
82
- Deprecation.warn "Separate `path` call is deprecated; pass as keyword argument to `ssh` instead, e.g., `ssh \"user@host\", path: \"#{new_path}\"` (will be removed in v2.0)"
83
81
  @path = new_path
84
82
  else
85
83
  @path || config.project_name
86
84
  end
87
85
  end
88
86
 
89
- # Deprecated separate setter methods - use ssh(..., option: value) instead
90
- def gateway(value = nil)
91
- if value
92
- Deprecation.warn "Separate `gateway` call is deprecated; pass as keyword argument to `ssh` instead, e.g., `ssh \"user@host\", gateway: \"#{value}\"` (will be removed in v2.0)"
93
- @gateway = value
94
- else
95
- @gateway
96
- end
97
- end
98
-
99
- def ssh_key(value = nil)
100
- if value
101
- Deprecation.warn "Separate `ssh_key` call is deprecated; pass as keyword argument to `ssh` instead, e.g., `ssh \"user@host\", ssh_key: \"#{value}\"` (will be removed in v2.0)"
102
- @ssh_key = value
103
- else
104
- @ssh_key
105
- end
106
- end
107
-
108
- def env(value = nil)
109
- if value
110
- Deprecation.warn "Separate `env` call is deprecated; pass as keyword argument to `ssh` instead, e.g., `ssh \"user@host\", env: \"#{value}\"` (will be removed in v2.0)"
111
- @env = value
112
- else
113
- @env
114
- end
115
- end
116
-
117
87
  # Ping configuration
118
88
  def ping(*urls)
119
89
  if urls.empty?
120
- # Getter - normalize URLs like Server does
121
- @ping_urls.map { |url| normalize_ping(url) }
90
+ # Getter
91
+ @ping_urls
122
92
  elsif urls.first == false
123
93
  # Disable ping
124
94
  @ping_urls = []
@@ -164,18 +134,6 @@ module Bard
164
134
  end
165
135
  end
166
136
 
167
- # Deprecated strategy configuration methods
168
- def strategy(name)
169
- Deprecation.warn "`strategy` is deprecated; use the strategy method directly, e.g., `#{name} \"url\"` instead of `strategy :#{name}` (will be removed in v2.0)"
170
- @deploy_strategy = name
171
- end
172
-
173
- def option(key, value)
174
- Deprecation.warn "`option` is deprecated; pass options as keyword arguments to the strategy method, e.g., `jets \"url\", #{key}: #{value.inspect}` (will be removed in v2.0)"
175
- @strategy_options_hash[@deploy_strategy] ||= {}
176
- @strategy_options_hash[@deploy_strategy][key] = value
177
- end
178
-
179
137
  def strategy_options(strategy_name)
180
138
  @strategy_options_hash[strategy_name] || {}
181
139
  end
@@ -217,44 +175,44 @@ module Bard
217
175
  end
218
176
 
219
177
  # Remote command execution
220
- def run!(command, home: false, verbose: false, quiet: false, capture: false)
178
+ def run!(command, home: false, verbose: false, quiet: false)
221
179
  require_capability!(:ssh)
222
- result = Command.run!(command, on: self, home: home, verbose: verbose, quiet: quiet)
223
- result if capture
180
+ Command.run!(command, on: server, home: home, verbose: verbose, quiet: quiet)
224
181
  end
225
182
 
226
183
  def run(command, home: false, verbose: false, quiet: false)
227
184
  require_capability!(:ssh)
228
- Command.run(command, on: self, home: home, verbose: verbose, quiet: quiet)
185
+ Command.run(command, on: server, home: home, verbose: verbose, quiet: quiet)
229
186
  end
230
187
 
231
188
  def exec!(command, home: false)
232
189
  require_capability!(:ssh)
233
- Command.exec!(command, on: self, home: home)
190
+ Command.exec!(command, on: server, home: home)
234
191
  end
235
192
 
236
193
  # File transfer
237
194
  def copy_file(path, to:, verbose: false)
238
195
  require_capability!(:ssh)
239
- to.require_capability!(:ssh) if to.respond_to?(:require_capability!)
196
+ to.require_capability!(:ssh)
240
197
  Copy.file(path, from: self, to: to, verbose: verbose)
241
198
  end
242
199
 
243
200
  def copy_dir(path, to:, verbose: false)
244
201
  require_capability!(:ssh)
245
- to.require_capability!(:ssh) if to.respond_to?(:require_capability!)
202
+ to.require_capability!(:ssh)
246
203
  Copy.dir(path, from: self, to: to, verbose: verbose)
247
204
  end
248
205
 
249
206
  # URI methods for compatibility
250
207
  def scp_uri(file_path = nil)
251
- full_path = "/#{path}"
252
- full_path += "/#{file_path}" if file_path
253
- URI::Generic.build(scheme: "scp", userinfo: server.user, host: server.host, port: server.port.to_i, path: full_path)
208
+ uri = URI("scp://#{ssh_uri}")
209
+ uri.path = "/#{path}"
210
+ uri.path += "/#{file_path}" if file_path
211
+ uri
254
212
  end
255
213
 
256
214
  def rsync_uri(file_path = nil)
257
- uri = ssh_uri
215
+ uri = URI("ssh://#{ssh_uri}")
258
216
  str = "#{uri.user}@#{uri.host}"
259
217
  str += ":#{path}"
260
218
  str += "/#{file_path}" if file_path
@@ -277,14 +235,5 @@ module Bard
277
235
  end
278
236
  end
279
237
  end
280
-
281
- private
282
-
283
- def normalize_ping(value)
284
- return nil if value == false || value.nil?
285
- normalized = value.to_s
286
- normalized = "https://#{normalized}" unless normalized.start_with?("http")
287
- normalized
288
- end
289
238
  end
290
239
  end
data/lib/bard/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module Bard
2
- VERSION = "1.9.6"
2
+ VERSION = "2.0.0.beta"
3
3
  end
4
4
 
@@ -3,11 +3,10 @@ FROM ubuntu:22.04
3
3
  # Prevent interactive prompts
4
4
  ENV DEBIAN_FRONTEND=noninteractive
5
5
 
6
- # Install SSH server, git, and basic tools
6
+ # Install SSH server and basic tools
7
7
  RUN apt-get update && \
8
8
  apt-get install -y \
9
9
  openssh-server \
10
- git \
11
10
  sudo \
12
11
  && rm -rf /var/lib/apt/lists/*
13
12