bard 1.8.0 → 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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +0 -5
  3. data/MIGRATION_GUIDE.md +9 -24
  4. data/Rakefile +1 -3
  5. data/features/bard_check.feature +94 -0
  6. data/features/bard_deploy.feature +18 -0
  7. data/features/bard_pull.feature +112 -0
  8. data/features/bard_push.feature +112 -0
  9. data/features/podman_testcontainers.feature +16 -0
  10. data/features/step_definitions/check_steps.rb +47 -0
  11. data/features/step_definitions/git_steps.rb +73 -0
  12. data/features/step_definitions/global_steps.rb +56 -0
  13. data/features/step_definitions/podman_steps.rb +23 -0
  14. data/features/step_definitions/rails_steps.rb +44 -0
  15. data/features/step_definitions/submodule_steps.rb +110 -0
  16. data/features/support/env.rb +39 -5
  17. data/features/support/grit_ext.rb +13 -0
  18. data/features/support/io.rb +32 -0
  19. data/features/support/podman.rb +153 -0
  20. data/lib/bard/command.rb +10 -29
  21. data/lib/bard/config.rb +0 -2
  22. data/lib/bard/copy.rb +33 -12
  23. data/lib/bard/server.rb +1 -43
  24. data/lib/bard/ssh_server.rb +1 -1
  25. data/lib/bard/target.rb +15 -65
  26. data/lib/bard/version.rb +1 -1
  27. data/spec/acceptance/docker/Dockerfile +1 -2
  28. data/spec/bard/command_spec.rb +1 -1
  29. data/spec/bard/copy_spec.rb +3 -3
  30. data/spec/bard/ssh_server_spec.rb +3 -7
  31. data/spec/bard/target_spec.rb +5 -9
  32. metadata +30 -16
  33. data/cucumber.yml +0 -1
  34. data/features/data.feature +0 -12
  35. data/features/deploy.feature +0 -13
  36. data/features/run.feature +0 -13
  37. data/features/step_definitions/bard_steps.rb +0 -39
  38. data/features/support/test_server.rb +0 -215
  39. data/lib/bard/deprecation.rb +0 -19
  40. data/spec/bard/deprecation_spec.rb +0 -281
@@ -0,0 +1,44 @@
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
@@ -0,0 +1,110 @@
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 +1,47 @@
1
1
  $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib')
2
- require 'rspec/expectations'
3
- require 'fileutils'
2
+ require 'ruby-debug'
3
+ require 'grit'
4
+ require 'spec/expectations'
5
+ gem 'sqlite3-ruby'
4
6
 
5
- ENV["PATH"] = "#{File.dirname(File.expand_path(__FILE__))}/../../bin:#{ENV['PATH']}"
7
+ ENV["PATH"] += ":#{File.dirname(File.expand_path(__FILE__))}/../../bin"
6
8
  ENV["GIT_DIR"] = nil
7
9
  ENV["GIT_WORK_TREE"] = nil
8
10
  ENV["GIT_INDEX_FILE"] = nil
9
11
 
10
12
  ROOT = File.expand_path(File.dirname(__FILE__) + '/../..')
11
13
 
12
- # Ensure tmp directory exists
13
- FileUtils.mkdir_p(File.join(ROOT, "tmp"))
14
+ # setup fixtures
15
+ if File.exist?("/dev/shm")
16
+ FileUtils.rm_rf "tmp"
17
+ tmp_dir = "/dev/shm/bard_testing_tmp"
18
+ FileUtils.rm_rf tmp_dir
19
+ FileUtils.mkdir tmp_dir
20
+ `ln -s #{tmp_dir} tmp`
21
+ else
22
+ FileUtils.rm_rf "tmp"
23
+ FileUtils.mkdir "tmp"
24
+ end
25
+
26
+ Dir.chdir 'tmp' do
27
+ `git clone --mirror --recursive #{ROOT}/fixtures/repo origin.git`
28
+
29
+ `git clone --bare --recursive origin.git submodule_a.git`
30
+ `git clone --bare --recursive origin.git submodule_b.git`
31
+ %w(development_a development_b staging production).each do |env|
32
+ `git clone --recursive origin.git #{env}`
33
+ Dir.chdir env do
34
+ FileUtils.cp "config/database.sample.yml", "config/database.yml"
35
+ `grb track master`
36
+ `git checkout master`
37
+ unless env == "production"
38
+ `grb track integration`
39
+ `git checkout integration`
40
+ end
41
+ end
42
+ end
43
+ FileUtils.mkdir "fixtures"
44
+ Dir.foreach "." do |file|
45
+ FileUtils.mv(file, "fixtures/") unless %w(fixtures . ..).include? file
46
+ end
47
+ end
@@ -0,0 +1,13 @@
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
+
@@ -0,0 +1,32 @@
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
@@ -0,0 +1,153 @@
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
data/lib/bard/command.rb CHANGED
@@ -64,43 +64,24 @@ module Bard
64
64
  # Support both new Target (with server attribute) and old Server architecture
65
65
  ssh_server = on.respond_to?(:server) ? on.server : on
66
66
 
67
- # Get options from Target first (for deprecated separate method calls), fall back to SSHServer
68
- env_value = on.respond_to?(:env) ? on.env : nil
69
- env_value ||= ssh_server.env if ssh_server.respond_to?(:env)
70
-
71
- ssh_key = on.respond_to?(:ssh_key) ? on.ssh_key : nil
72
- ssh_key ||= ssh_server.ssh_key if ssh_server.respond_to?(:ssh_key)
73
-
74
- gateway = on.respond_to?(:gateway) ? on.gateway : nil
75
- gateway ||= ssh_server.gateway if ssh_server.respond_to?(:gateway)
76
-
77
67
  cmd = command
78
- cmd = "#{env_value} #{command}" if env_value
79
-
68
+ if ssh_server.env
69
+ cmd = "#{ssh_server.env} #{command}"
70
+ end
80
71
  unless home
81
72
  path = on.respond_to?(:path) ? on.path : ssh_server.path
82
73
  cmd = "cd #{path} && #{cmd}" if path
83
74
  end
84
75
 
85
- ssh_opts = ["-tt", "-o StrictHostKeyChecking=no", "-o UserKnownHostsFile=/dev/null", "-o LogLevel=ERROR"]
86
- ssh_opts << "-i #{ssh_key}" if ssh_key
76
+ ssh_key = ssh_server.ssh_key ? "-i #{ssh_server.ssh_key} " : ""
77
+ ssh_uri = ssh_server.respond_to?(:ssh_uri) ? ssh_server.ssh_uri : ssh_server.ssh_uri(:ssh)
87
78
 
88
- # Handle new SSHServer vs old Server architecture
89
- if ssh_server.respond_to?(:host)
90
- # New SSHServer - has separate host/port/user
91
- ssh_opts << "-p #{ssh_server.port}" if ssh_server.port && ssh_server.port != "22"
92
- ssh_opts << "-o ProxyJump=#{gateway}" if gateway
93
- ssh_target = "#{ssh_server.user}@#{ssh_server.host}"
94
- else
95
- # Old Server - uses URI-based ssh_uri
96
- ssh_target = ssh_server.ssh_uri
97
- if gateway
98
- gateway_uri = ssh_server.ssh_uri(:gateway)
99
- ssh_opts << "-o ProxyJump=#{gateway_uri}"
100
- end
101
- end
79
+ cmd = "ssh -tt #{ssh_key} #{ssh_uri} '#{cmd}'"
102
80
 
103
- cmd = "ssh #{ssh_opts.join(' ')} #{ssh_target} '#{cmd}'"
81
+ if ssh_server.gateway
82
+ gateway_uri = ssh_server.respond_to?(:ssh_uri) ? ssh_server.gateway : ssh_server.ssh_uri(:gateway)
83
+ cmd = "ssh -tt #{gateway_uri} \"#{cmd}\""
84
+ end
104
85
 
105
86
  cmd += " 2>&1" if quiet
106
87
  cmd
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,7 +43,6 @@ 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
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
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"
@@ -23,7 +23,7 @@ module Bard
23
23
  end
24
24
 
25
25
  def ssh_uri
26
- URI("ssh://#{user}@#{host}:#{port}")
26
+ "#{user}@#{host}:#{port}"
27
27
  end
28
28
 
29
29
  def hostname