bard 1.7.2 → 1.7.4

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: 674739cf14af03776e5f892cb47463decdc1a3a6a493ee4e62dc3528a4fe2c36
4
- data.tar.gz: 5685ced74cc962121ad634d9290aeba0663373c9db052ca3480d37f29ec8ec1b
3
+ metadata.gz: d1e8b7a7dc083d479b5322405ddbfc58ecab56967433c5f2a543c7637a86ce28
4
+ data.tar.gz: 3c09e62579e85fa615e2005ed6f5cc2d0068576eba46255791523a211b7c0387
5
5
  SHA512:
6
- metadata.gz: 734fe59b6c52c355168637b37685bda6a54dc05faf66db0d7758bcb00305a00069056915f433fc5359b36236896a300ac5162632872cc00e71c5bc7d730239de
7
- data.tar.gz: 924afe38e6cdf1dfa179917856d172c35c1d083571d1dfbfdce53d3c45e8b15950c94c3d6131c123d529005a80ba518ae7ffb420f9eb16fa700999a7cff8e432
6
+ metadata.gz: 2ab06301b535cfe5eb37bb3da892fc6e8a66cb46c6c346bfb0d8279bfd37bba9c495f3c9fc9ffda8663881b7fe333186820cc4c57d36f086c524191dc953da14
7
+ data.tar.gz: 0f55ad3b8d94900354005227d73b86ba3ab25847eb7d88242f89e28fafbd281256755a802a7ca10092bd1571040335a3a4d8de845406af94b4d5a10edd1e9e16
@@ -22,34 +22,55 @@ module Bard
22
22
  private
23
23
 
24
24
  def build_site
25
- system "rm -rf #{@build_dir.sub(@sha, "*")}"
26
- run! <<~SH
27
- set -e
28
- RAILS_ENV=production bundle exec rails s -p 3000 -d --pid tmp/pids/server.pid
29
- OUTPUT=$(bundle exec rake assets:clean assets:precompile 2>&1) || echo "$OUTPUT"
30
-
31
- # Create the output directory and enter it
32
- BUILD=#{@build_dir}
33
- rm -rf $BUILD
34
- mkdir -p $BUILD
35
- cp -R public/assets $BUILD/
36
- cd $BUILD
37
-
38
- # wait until server responds
39
- echo waiting...
40
- curl -s --retry 5 --retry-delay 2 http://localhost:3000 >/dev/null 2>&1
41
-
42
- echo copying...
43
- # Mirror the site to the build folder, ignoring links with query params
44
- wget -nv -r -l inf --no-remove-listing -FEnH --reject-regex "(\\.*)\\?(.*)" http://localhost:3000/ 2>&1
45
-
46
- echo #{@domain} > CNAME
47
- SH
48
- ensure # cleanup
49
- run! <<~SH
50
- cat tmp/pids/server.pid | xargs -I {} kill {}
51
- rm -rf public/assets
52
- SH
25
+ with_locked_port do |port|
26
+ system "rm -rf #{@build_dir.sub(@sha, "*")}"
27
+ run! <<~SH
28
+ set -e
29
+ RAILS_ENV=production bundle exec rails s -p #{port} -d --pid tmp/pids/server.pid
30
+ OUTPUT=$(bundle exec rake assets:clean assets:precompile 2>&1) || echo "$OUTPUT"
31
+
32
+ # Create the output directory and enter it
33
+ BUILD=#{@build_dir}
34
+ rm -rf $BUILD
35
+ mkdir -p $BUILD
36
+ cp -R public/assets $BUILD/
37
+ cd $BUILD
38
+
39
+ # wait until server responds
40
+ echo waiting...
41
+ curl -s --retry 5 --retry-delay 2 http://localhost:#{port} >/dev/null 2>&1
42
+
43
+ echo copying...
44
+ # Mirror the site to the build folder, ignoring links with query params
45
+ wget -nv -r -l inf --no-remove-listing -FEnH --reject-regex "(\\.*)\\?(.*)" http://localhost:#{port}/ 2>&1
46
+
47
+ echo #{@domain} > CNAME
48
+ SH
49
+ ensure # cleanup
50
+ run! <<~SH
51
+ cat tmp/pids/server.pid | xargs -I {} kill {}
52
+ rm -rf public/assets
53
+ SH
54
+ end
55
+ end
56
+
57
+ def with_locked_port
58
+ (3000..3020).each do |port|
59
+ lock_file = "/tmp/bard_github_pages_#{port}.lock"
60
+ file = File.open(lock_file, File::RDWR | File::CREAT, 0644)
61
+ if file.flock(File::LOCK_EX | File::LOCK_NB)
62
+ begin
63
+ yield port
64
+ return
65
+ ensure
66
+ file.flock(File::LOCK_UN)
67
+ file.close
68
+ end
69
+ else
70
+ file.close
71
+ end
72
+ end
73
+ raise "Could not find an available port for GitHub Pages deployment (checked 3000-3020)."
53
74
  end
54
75
 
55
76
  def create_tree_from_build
data/lib/bard/ping.rb CHANGED
@@ -8,16 +8,26 @@ module Bard
8
8
  end
9
9
 
10
10
  def call
11
- server.ping.reject do |url|
12
- response = get_response_with_redirect(url) rescue nil
13
- response.is_a?(Net::HTTPSuccess)
14
- end
11
+ server.ping.reject { |url| reachable?(url) }
15
12
  end
16
13
 
17
14
  private
18
15
 
16
+ def reachable?(url)
17
+ attempts = 0
18
+ begin
19
+ attempts += 1
20
+ response = get_response_with_redirect(url)
21
+ response.is_a?(Net::HTTPSuccess)
22
+ rescue StandardError
23
+ retry if attempts < 2
24
+ false
25
+ end
26
+ end
27
+
19
28
  def get_response_with_redirect uri_str, limit=5
20
- response = Net::HTTP.get_response(URI(uri_str))
29
+ uri = URI(uri_str)
30
+ response = http_get(uri)
21
31
 
22
32
  case response
23
33
  when Net::HTTPRedirection
@@ -25,11 +35,32 @@ module Bard
25
35
  puts "too many HTTP redirects"
26
36
  response
27
37
  else
28
- get_response_with_redirect(response["location"], limit - 1)
38
+ location = response["location"]
39
+ return response unless location
40
+
41
+ next_uri = begin
42
+ uri + location
43
+ rescue URI::InvalidURIError
44
+ URI(location)
45
+ end
46
+
47
+ get_response_with_redirect(next_uri, limit - 1)
29
48
  end
30
49
  else
31
50
  response
32
51
  end
33
52
  end
53
+
54
+ def http_get(uri)
55
+ Net::HTTP.start(
56
+ uri.host,
57
+ uri.port,
58
+ use_ssl: uri.scheme == "https",
59
+ open_timeout: 5,
60
+ read_timeout: 5,
61
+ ) do |http|
62
+ http.get(uri.request_uri)
63
+ end
64
+ end
34
65
  end
35
66
  end
data/lib/bard/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module Bard
2
- VERSION = "1.7.2"
2
+ VERSION = "1.7.4"
3
3
  end
4
4
 
@@ -38,6 +38,69 @@ describe Bard::GithubPages do
38
38
  end
39
39
  end
40
40
 
41
+ describe "#build_site" do
42
+ it "uses the locked port" do
43
+ github_pages.instance_variable_set(:@sha, "abc123")
44
+ github_pages.instance_variable_set(:@build_dir, "tmp/github-build-abc123")
45
+ github_pages.instance_variable_set(:@domain, "example.com")
46
+
47
+ allow(github_pages).to receive(:with_locked_port).and_yield(3005)
48
+
49
+ expect(github_pages).to receive(:run!).with(satisfy { |cmd|
50
+ cmd.include?("rails s -p 3005") && cmd.include?("http://localhost:3005")
51
+ }).ordered
52
+
53
+ expect(github_pages).to receive(:run!).with(include("kill")).ordered
54
+
55
+ github_pages.send(:build_site)
56
+ end
57
+ end
58
+
59
+ describe "#with_locked_port" do
60
+ let(:file_mock) { double("file", close: true) }
61
+
62
+ before do
63
+ allow(File).to receive(:open).and_return(file_mock)
64
+ end
65
+
66
+ it "yields the first available port" do
67
+ allow(file_mock).to receive(:flock).and_return(true)
68
+
69
+ expect(File).to receive(:open).with("/tmp/bard_github_pages_3000.lock", anything, anything)
70
+
71
+ yielded_port = nil
72
+ github_pages.send(:with_locked_port) { |p| yielded_port = p }
73
+ expect(yielded_port).to eq(3000)
74
+ end
75
+
76
+ it "retries if the first port is locked" do
77
+ # 1. Try port 3000
78
+ expect(File).to receive(:open).with("/tmp/bard_github_pages_3000.lock", anything, anything).ordered
79
+ expect(file_mock).to receive(:flock).with(File::LOCK_EX | File::LOCK_NB).and_return(false).ordered
80
+ expect(file_mock).to receive(:close).ordered
81
+
82
+ # 2. Try port 3001
83
+ expect(File).to receive(:open).with("/tmp/bard_github_pages_3001.lock", anything, anything).ordered
84
+ expect(file_mock).to receive(:flock).with(File::LOCK_EX | File::LOCK_NB).and_return(true).ordered
85
+
86
+ # 3. Cleanup after yielding
87
+ expect(file_mock).to receive(:flock).with(File::LOCK_UN).ordered
88
+ expect(file_mock).to receive(:close).ordered
89
+
90
+ yielded_port = nil
91
+ github_pages.send(:with_locked_port) { |p| yielded_port = p }
92
+ expect(yielded_port).to eq(3001)
93
+ end
94
+
95
+ it "raises an error if no ports are available" do
96
+ allow(file_mock).to receive(:flock).and_return(false)
97
+
98
+ expect {
99
+ github_pages.send(:with_locked_port) {}
100
+ }.to raise_error(/Could not find an available port/)
101
+ end
102
+ end
103
+
41
104
  describe "#get_parent_commit" do
42
105
  it "returns the sha of the gh-pages branch" do
43
106
  github_pages.instance_variable_set(:@branch, "gh-pages")
@@ -77,4 +140,4 @@ describe Bard::GithubPages do
77
140
  end
78
141
  end
79
142
  end
80
- end
143
+ end
@@ -3,29 +3,58 @@ require "bard/ping"
3
3
 
4
4
  describe Bard::Ping do
5
5
  let(:server) { double("server", ping: ["http://example.com"]) }
6
+ let(:ping) { described_class.new(server) }
7
+
8
+ def success_response
9
+ Net::HTTPSuccess.new(1.0, "200", "OK")
10
+ end
11
+
12
+ def not_found_response
13
+ Net::HTTPNotFound.new(1.0, "404", "Not Found")
14
+ end
6
15
 
7
16
  context "when the server is reachable" do
8
- it "should return an empty array" do
9
- allow(Net::HTTP).to receive(:get_response).and_return(Net::HTTPSuccess.new(1.0, "200", "OK"))
10
- expect(Bard::Ping.call(server)).to be_empty
17
+ it "returns an empty array" do
18
+ allow(ping).to receive(:http_get).and_return(success_response)
19
+ expect(ping.call).to be_empty
11
20
  end
12
21
  end
13
22
 
14
23
  context "when the server is not reachable" do
15
- it "should return the url" do
16
- allow(Net::HTTP).to receive(:get_response).and_return(Net::HTTPNotFound.new(1.0, "404", "Not Found"))
17
- expect(Bard::Ping.call(server)).to eq(["http://example.com"])
24
+ it "returns the url" do
25
+ allow(ping).to receive(:http_get).and_return(not_found_response)
26
+ expect(ping.call).to eq(["http://example.com"])
18
27
  end
19
28
  end
20
29
 
21
30
  context "when there is a redirect" do
22
- it "should follow the redirect and return an empty array" do
31
+ it "follows the redirect and returns an empty array" do
23
32
  redirect_response = Net::HTTPRedirection.new(1.0, "301", "Moved Permanently")
24
- redirect_response["location"] = "http://example.com/new"
25
- success_response = Net::HTTPSuccess.new(1.0, "200", "OK")
26
- allow(Net::HTTP).to receive(:get_response).with(URI("http://example.com")).and_return(redirect_response)
27
- allow(Net::HTTP).to receive(:get_response).with(URI("http://example.com/new")).and_return(success_response)
28
- expect(Bard::Ping.call(server)).to be_empty
33
+ redirect_response["location"] = "/new"
34
+ allow(ping).to receive(:http_get).with(URI("http://example.com")).and_return(redirect_response)
35
+ allow(ping).to receive(:http_get).with(URI("http://example.com/new")).and_return(success_response)
36
+ expect(ping.call).to be_empty
37
+ end
38
+ end
39
+
40
+ context "when a transient error occurs" do
41
+ it "retries once before marking down" do
42
+ calls = 0
43
+ allow(ping).to receive(:http_get) do
44
+ calls += 1
45
+ raise Errno::ECONNRESET if calls == 1
46
+
47
+ success_response
48
+ end
49
+
50
+ expect(ping.call).to be_empty
51
+ end
52
+ end
53
+
54
+ context "when errors persist across retries" do
55
+ it "returns the url" do
56
+ allow(ping).to receive(:http_get).and_raise(Errno::ECONNREFUSED)
57
+ expect(ping.call).to eq(["http://example.com"])
29
58
  end
30
59
  end
31
60
  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.7.2
4
+ version: 1.7.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Micah Geisel
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-12-11 00:00:00.000000000 Z
10
+ date: 2025-12-16 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: thor