bard 1.5.5 → 1.7.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e299224241e3766a31c3dfe6a95b6d4ac74d50a189b4fecd8cd79a2f1856c8ab
4
- data.tar.gz: 0b87cc9f75177aa95105924a65af0a5e67a9067b177a8824cfd3206bc3d17fe1
3
+ metadata.gz: a11cf0d4ee1ff435809dbe0e48beb1dcff72f2a4191fa39c40ef8932bbf64915
4
+ data.tar.gz: 4861d2109f0cb0d438dd281425865ac8c0496019c8529560464fab55c05b5da7
5
5
  SHA512:
6
- metadata.gz: 866ce92cbfcc95a4cd2813da2f5cf1db8f40631270ae3160ad8981ae5d71e79deb8cf20f6b41125853b9f0ba05cadd3b878d074532d1ab5cdba5bee3e7ad9921
7
- data.tar.gz: f33f6555089298fd42b361ed3d5f7c681d6df9ed0265b4027a83a4168d680ee35cb877002e8fb8d58c4e8c9e6e8e05b89d389c0e85e9de75e6861af0f70fdf39
6
+ metadata.gz: 53d527f268f68f7b5a61cae324e583f83c26246bd69f4717a37e10c2e1ae44ca53d7d3b194a60454a82c1471ba8588f1182177dd7994b41c193739889a0d5a36
7
+ data.tar.gz: 669b2ae00f5c9983fed2747e1426f48710c5da142b3d43cda7bb0c7c3c160dc9ee52385f606ea65dbf087803733461bf4ce71d15b3bb8ed5373737555dd441a8
data/Gemfile CHANGED
@@ -6,4 +6,4 @@ gemspec
6
6
  group :test do
7
7
  gem "simplecov", require: false
8
8
  gem "webmock", require: false
9
- end
9
+ end
data/bard.gemspec CHANGED
@@ -26,5 +26,6 @@ Gem::Specification.new do |spec|
26
26
  spec.add_development_dependency "rake"
27
27
  spec.add_development_dependency "rspec"
28
28
  spec.add_development_dependency "debug"
29
+ spec.add_development_dependency "cucumber"
29
30
  spec.add_development_dependency "testcontainers"
30
31
  end
@@ -0,0 +1,16 @@
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"
@@ -0,0 +1,23 @@
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
@@ -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/config.rb CHANGED
@@ -2,6 +2,12 @@ require "bard/server"
2
2
 
3
3
  module Bard
4
4
  class Config
5
+ def self.current(working_directory: Dir.getwd)
6
+ project_name = File.basename(working_directory)
7
+ path = File.join(working_directory, "bard.rb")
8
+ new(project_name, path: path)
9
+ end
10
+
5
11
  def initialize project_name, path: nil, source: nil
6
12
  @project_name = project_name
7
13
  @servers = {
@@ -65,28 +71,27 @@ module Bard
65
71
  end
66
72
  end
67
73
 
68
- def backup *args
69
- if args.length == 1
70
- @backup = args.first
71
- elsif args.length == 0
72
- return @backup if defined?(@backup)
73
- @backup = true
74
+ def backup(value = nil, &block)
75
+ if block_given?
76
+ @backup = BackupConfig.new(&block)
77
+ elsif value == false
78
+ @backup = BackupConfig.new { disabled }
79
+ elsif value.nil? # Getter
80
+ @backup ||= BackupConfig.new { bard }
74
81
  else
75
- raise ArgumentError
82
+ raise ArgumentError, "backup accepts false or a block"
76
83
  end
77
84
  end
78
85
 
79
86
  # short-hand for michael
80
87
 
81
- def github_pages url=nil
88
+ def github_pages url
82
89
  urls = []
83
- if url.present?
84
- uri = url.start_with?("http") ? URI.parse(url) : URI.parse("https://#{url}")
85
- hostname = uri.hostname.sub(/^www\./, '')
86
- urls = [hostname]
87
- if hostname.count(".") < 2
88
- urls << "www.#{hostname}"
89
- end
90
+ uri = url.start_with?("http") ? URI.parse(url) : URI.parse("https://#{url}")
91
+ hostname = uri.hostname.sub(/^www\./, '')
92
+ urls = [hostname]
93
+ if hostname.count(".") < 2
94
+ urls << "www.#{hostname}"
90
95
  end
91
96
 
92
97
  server :production do
@@ -98,4 +103,45 @@ module Bard
98
103
  backup false
99
104
  end
100
105
  end
106
+
107
+ class BackupConfig
108
+ attr_reader :destinations
109
+
110
+ def initialize(&block)
111
+ @destinations = []
112
+ instance_eval(&block) if block_given?
113
+ end
114
+
115
+ def bard
116
+ @bard = true
117
+ end
118
+
119
+ def bard?
120
+ !!@bard
121
+ end
122
+
123
+ def disabled
124
+ @disabled = true
125
+ end
126
+
127
+ def disabled?
128
+ !!@disabled
129
+ end
130
+
131
+ def enabled?
132
+ !disabled?
133
+ end
134
+
135
+ def s3(name, **kwargs)
136
+ @destinations << {
137
+ name: name,
138
+ type: :s3,
139
+ **kwargs,
140
+ }
141
+ end
142
+
143
+ def self_managed?
144
+ @destinations.any?
145
+ end
146
+ end
101
147
  end
data/lib/bard/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module Bard
2
- VERSION = "1.5.5"
2
+ VERSION = "1.7.0"
3
3
  end
4
4
 
@@ -29,8 +29,11 @@ describe Bard::Config do
29
29
  end
30
30
 
31
31
  describe "#backup" do
32
- it "returns true" do
33
- expect(subject.backup).to eq true
32
+ it "returns a BackupConfig with bard enabled by default" do
33
+ backup = subject.backup
34
+ expect(backup).to be_a(Bard::BackupConfig)
35
+ expect(backup.bard?).to eq true
36
+ expect(backup.destinations).to be_empty
34
37
  end
35
38
  end
36
39
  end
@@ -75,7 +78,91 @@ describe Bard::Config do
75
78
 
76
79
  describe "#backup" do
77
80
  it "returns the backup setting" do
78
- expect(subject.backup).to eq false
81
+ expect(subject.backup).to be_disabled
82
+ end
83
+ end
84
+ end
85
+
86
+ context "with new backup block syntax" do
87
+ describe "#backup with bard directive" do
88
+ subject { described_class.new("test", source: "backup { bard }") }
89
+
90
+ it "returns a BackupConfig with bard enabled" do
91
+ backup = subject.backup
92
+ expect(backup).to be_a(Bard::BackupConfig)
93
+ expect(backup.bard?).to eq true
94
+ expect(backup.destinations).to be_empty
95
+ expect(backup.self_managed?).to eq false
96
+ end
97
+ end
98
+
99
+ describe "#backup with s3 directive" do
100
+ subject { described_class.new("test", source: "backup { s3 :primary, path: 'bucket/path' }") }
101
+
102
+ it "returns a BackupConfig with s3 destination" do
103
+ backup = subject.backup
104
+ expect(backup).to be_a(Bard::BackupConfig)
105
+ expect(backup.bard?).to eq false
106
+ expect(backup.destinations.length).to eq 1
107
+ expect(backup.self_managed?).to eq true
108
+
109
+ dest = backup.destinations.first
110
+ expect(dest[:name]).to eq :primary
111
+ expect(dest[:type]).to eq :s3
112
+ expect(dest[:path]).to eq 'bucket/path'
113
+ end
114
+ end
115
+
116
+ describe "#backup with both bard and s3 directives" do
117
+ subject { described_class.new("test", source: "backup { bard; s3 :custom, path: 'bucket/path' }") }
118
+
119
+ it "returns a BackupConfig with bard and s3 destination" do
120
+ backup = subject.backup
121
+ expect(backup).to be_a(Bard::BackupConfig)
122
+ expect(backup.bard?).to eq true
123
+ expect(backup.destinations.length).to eq 1
124
+ expect(backup.self_managed?).to eq true
125
+ end
126
+ end
127
+
128
+ describe "#backup with multiple s3 destinations" do
129
+ subject do
130
+ described_class.new("test", source: <<~SOURCE)
131
+ backup do
132
+ s3 :primary, path: 'bucket1/path'
133
+ s3 :secondary, path: 'bucket2/path'
134
+ end
135
+ SOURCE
136
+ end
137
+
138
+ it "returns a BackupConfig with multiple destinations" do
139
+ backup = subject.backup
140
+ expect(backup).to be_a(Bard::BackupConfig)
141
+ expect(backup.bard?).to eq false
142
+ expect(backup.destinations.length).to eq 2
143
+ expect(backup.self_managed?).to eq true
144
+
145
+ expect(backup.destinations[0][:name]).to eq :primary
146
+ expect(backup.destinations[1][:name]).to eq :secondary
147
+ end
148
+ end
149
+ end
150
+
151
+ context "with github_pages directive" do
152
+ subject { described_class.new("test", source: "github_pages 'example.com'") }
153
+
154
+ describe "#backup" do
155
+ it "sets backup to false" do
156
+ expect(subject.backup).to be_disabled
157
+ end
158
+ end
159
+
160
+ describe "#server" do
161
+ it "creates a production server with github_pages enabled" do
162
+ production = subject[:production]
163
+ expect(production).not_to be_nil
164
+ expect(production.github_pages).to eq true
165
+ expect(production.ssh).to eq false
79
166
  end
80
167
  end
81
168
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bard
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.5
4
+ version: 1.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Micah Geisel
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-11-30 00:00:00.000000000 Z
10
+ date: 2025-12-11 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: thor
@@ -121,6 +121,20 @@ dependencies:
121
121
  - - ">="
122
122
  - !ruby/object:Gem::Version
123
123
  version: '0'
124
+ - !ruby/object:Gem::Dependency
125
+ name: cucumber
126
+ requirement: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ type: :development
132
+ prerelease: false
133
+ version_requirements: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
124
138
  - !ruby/object:Gem::Dependency
125
139
  name: testcontainers
126
140
  requirement: !ruby/object:Gem::Requirement
@@ -157,14 +171,17 @@ files:
157
171
  - features/bard_deploy.feature
158
172
  - features/bard_pull.feature
159
173
  - features/bard_push.feature
174
+ - features/podman_testcontainers.feature
160
175
  - features/step_definitions/check_steps.rb
161
176
  - features/step_definitions/git_steps.rb
162
177
  - features/step_definitions/global_steps.rb
178
+ - features/step_definitions/podman_steps.rb
163
179
  - features/step_definitions/rails_steps.rb
164
180
  - features/step_definitions/submodule_steps.rb
165
181
  - features/support/env.rb
166
182
  - features/support/grit_ext.rb
167
183
  - features/support/io.rb
184
+ - features/support/podman.rb
168
185
  - install_files/.github/dependabot.yml
169
186
  - install_files/.github/workflows/cache-ci.yml
170
187
  - install_files/.github/workflows/ci.yml
@@ -228,7 +245,6 @@ files:
228
245
  - spec/acceptance/docker/Dockerfile
229
246
  - spec/acceptance/docker/test_key
230
247
  - spec/acceptance/docker/test_key.pub
231
- - spec/acceptance/podman_testcontainers_spec.rb
232
248
  - spec/bard/ci/github_actions_spec.rb
233
249
  - spec/bard/ci_spec.rb
234
250
  - spec/bard/cli/ci_spec.rb
@@ -301,19 +317,21 @@ test_files:
301
317
  - features/bard_deploy.feature
302
318
  - features/bard_pull.feature
303
319
  - features/bard_push.feature
320
+ - features/podman_testcontainers.feature
304
321
  - features/step_definitions/check_steps.rb
305
322
  - features/step_definitions/git_steps.rb
306
323
  - features/step_definitions/global_steps.rb
324
+ - features/step_definitions/podman_steps.rb
307
325
  - features/step_definitions/rails_steps.rb
308
326
  - features/step_definitions/submodule_steps.rb
309
327
  - features/support/env.rb
310
328
  - features/support/grit_ext.rb
311
329
  - features/support/io.rb
330
+ - features/support/podman.rb
312
331
  - spec/acceptance/.gitignore
313
332
  - spec/acceptance/docker/Dockerfile
314
333
  - spec/acceptance/docker/test_key
315
334
  - spec/acceptance/docker/test_key.pub
316
- - spec/acceptance/podman_testcontainers_spec.rb
317
335
  - spec/bard/ci/github_actions_spec.rb
318
336
  - spec/bard/ci_spec.rb
319
337
  - spec/bard/cli/ci_spec.rb
@@ -1,140 +0,0 @@
1
- # Acceptance test for Bard using Podman + TestContainers
2
- #
3
- # This test validates end-to-end functionality of `bard run ls` by:
4
- # 1. Starting an SSH server container using TestContainers
5
- # 2. Configuring Bard to connect to it
6
- # 3. Running bard commands against the container
7
- # 4. Automatically cleaning up when done
8
- #
9
- # Prerequisites:
10
- # - gem install testcontainers
11
- # - podman installed
12
- # - podman socket running (systemctl --user start podman.socket)
13
- # - Set DOCKER_HOST to podman socket
14
-
15
- require 'spec_helper'
16
- require 'testcontainers'
17
- require 'open3'
18
-
19
- RSpec.describe "Bard acceptance test with Podman + TestContainers", type: :acceptance do
20
- # Disable WebMock for acceptance tests - we need real HTTP connections to Podman
21
- before(:all) do
22
- WebMock.allow_net_connect!
23
- end
24
-
25
- after(:all) do
26
- WebMock.disable_net_connect!
27
- end
28
- # Configure TestContainers to use Podman
29
- before(:all) do
30
- # Set up podman socket for TestContainers
31
- # Use existing DOCKER_HOST if set (e.g., in CI), otherwise use default location
32
- unless ENV['DOCKER_HOST']
33
- podman_socket = "/run/user/#{Process.uid}/podman/podman.sock"
34
-
35
- # Start podman socket if not running
36
- unless File.exist?(podman_socket)
37
- system("systemctl --user start podman.socket 2>/dev/null || podman system service --time=0 unix://#{podman_socket} &")
38
- sleep 2
39
- end
40
-
41
- # Configure TestContainers to use podman
42
- ENV['DOCKER_HOST'] = "unix://#{podman_socket}"
43
- end
44
-
45
- # Ensure SSH key has correct permissions
46
- system("chmod 600 spec/acceptance/docker/test_key")
47
-
48
- # Check if we can pull images
49
- unless system("podman pull ubuntu:22.04 >/dev/null 2>&1")
50
- skip "Cannot pull images in this environment. Run in a network-enabled environment."
51
- end
52
-
53
- # Build the test image
54
- unless system("podman build -t bard-test-server -f spec/acceptance/docker/Dockerfile spec/acceptance/docker 2>&1")
55
- skip "Failed to build test image"
56
- end
57
- end
58
-
59
- # TestContainers will automatically manage this container
60
- let(:container) do
61
- Testcontainers::DockerContainer.new("localhost/bard-test-server:latest")
62
- .with_exposed_port(22)
63
- .with_name("bard-test-#{SecureRandom.hex(4)}")
64
- .start
65
- end
66
-
67
- let(:ssh_port) { container.mapped_port(22) }
68
-
69
- before(:each) do
70
- # Ensure container is started
71
- container
72
-
73
- # Wait for SSH to be ready
74
- 30.times do
75
- break if system("ssh -o StrictHostKeyChecking=no -o ConnectTimeout=1 -p #{ssh_port} deploy@localhost -i spec/acceptance/docker/test_key 'echo ready' 2>/dev/null")
76
- sleep 0.5
77
- end
78
-
79
- # Create test project directory
80
- system("ssh -o StrictHostKeyChecking=no -p #{ssh_port} deploy@localhost -i spec/acceptance/docker/test_key 'mkdir -p testproject'")
81
-
82
- # Create bard config for this container
83
- @bard_config_path = "tmp/test_bard_#{SecureRandom.hex(4)}.rb"
84
- FileUtils.mkdir_p("tmp")
85
- ssh_key_path = File.expand_path("spec/acceptance/docker/test_key")
86
- File.write(@bard_config_path, <<~RUBY)
87
- server :production do
88
- ssh "deploy@localhost:#{ssh_port}"
89
- path "testproject"
90
- ssh_key "#{ssh_key_path}"
91
- ping false
92
- end
93
- RUBY
94
- end
95
-
96
- after(:each) do
97
- # Clean up bard config
98
- FileUtils.rm_f(@bard_config_path) if @bard_config_path
99
-
100
- # TestContainers will automatically stop and remove the container
101
- container.stop if container
102
- container.remove if container
103
- end
104
-
105
- it "runs ls command via bard run" do
106
- # Create a test file in the container
107
- result = system("ssh -o StrictHostKeyChecking=no -p #{ssh_port} deploy@localhost -i spec/acceptance/docker/test_key 'touch testproject/test-file.txt'")
108
- expect(result).to be true
109
-
110
- # Run bard command
111
- Dir.chdir("tmp") do
112
- # Copy config to bard.rb
113
- FileUtils.cp("../#{@bard_config_path}", "bard.rb")
114
-
115
- output, status = Open3.capture2e("bard run ls")
116
-
117
- # Clean up
118
- FileUtils.rm_f("bard.rb")
119
-
120
- # Verify the command succeeded
121
- expect(status).to be_success, "bard run failed with output: #{output}"
122
- expect(output).to include("test-file.txt")
123
- end
124
- end
125
-
126
- it "runs multiple commands in isolated containers" do
127
- # Each test gets its own container automatically!
128
- result = system("ssh -o StrictHostKeyChecking=no -p #{ssh_port} deploy@localhost -i spec/acceptance/docker/test_key 'echo content > testproject/another-file.txt'")
129
- expect(result).to be true
130
-
131
- Dir.chdir("tmp") do
132
- FileUtils.cp("../#{@bard_config_path}", "bard.rb")
133
- output, status = Open3.capture2e("bard run 'cat another-file.txt'")
134
- FileUtils.rm_f("bard.rb")
135
-
136
- expect(status).to be_success, "bard run failed with output: #{output}"
137
- expect(output).to include("content")
138
- end
139
- end
140
- end