fbe 0.29.1 → 0.31.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: f08a090d74c79938d45d53b09d1cdb6fafada89c54b5c33b2b4154a1174ac38c
4
- data.tar.gz: d927725171355f731620718cc2d7125e1f8e961af53e01c4d36e03c288fcd640
3
+ metadata.gz: 2621af2f5ed4213f7137482238a6c1fc2af0f534a88b196e277cdf17bb9d27de
4
+ data.tar.gz: 65bfa710cd5680c579207ea1ce650c25ed743b39b835ce1500cd1b62d7d57fd4
5
5
  SHA512:
6
- metadata.gz: 32999571b725d7b68de93231ea2bdb1731ddc2a8d2c84bbf84d248fd25fae5223a3fb3b6cb85c73364815548019055420344d2bc3bba4ef14e57450f131026c5
7
- data.tar.gz: 6e26bb3ba8c15c24912b46c3a7841ebd59ab17a9ee3f530e7d19968efdbc8da9d20acaefa9fdece7c61999240118972277588b9ceda5e5a033cb4ef9be94166f
6
+ metadata.gz: a0d04610b4d6bd1bf32587b73baade530e6b22aead4fffe69f0bd6067f254d4f0899701ed84bf8a38f2fc9798ab39708d71a05d0a9681d001da5d12e8c4d47e5
7
+ data.tar.gz: dc62b030009940330564967622a9e0c16f16bce715d0fcd9e00cb54a7e617a24bfd68e70409b98fcc7cfbcc70c1ff356286b4047530ebcf808fe2f20c68b1f55
@@ -16,6 +16,6 @@ jobs:
16
16
  runs-on: ubuntu-24.04
17
17
  steps:
18
18
  - uses: actions/checkout@v5
19
- - uses: crate-ci/typos@v1.35.6
19
+ - uses: crate-ci/typos@v1.35.7
20
20
  with:
21
21
  config: .github/.typos.toml
data/Gemfile.lock CHANGED
@@ -63,7 +63,7 @@ GEM
63
63
  bigdecimal (3.2.2)
64
64
  builder (3.3.0)
65
65
  concurrent-ruby (1.3.5)
66
- connection_pool (2.5.3)
66
+ connection_pool (2.5.4)
67
67
  crack (1.0.0)
68
68
  bigdecimal
69
69
  rexml
@@ -76,7 +76,7 @@ GEM
76
76
  ellipsized (0.3.0)
77
77
  ethon (0.15.0)
78
78
  ffi (>= 1.15.0)
79
- factbase (0.15.6)
79
+ factbase (0.15.7)
80
80
  backtrace (~> 0.4)
81
81
  decoor (~> 0.1)
82
82
  ellipsized (~> 0.3)
@@ -121,7 +121,7 @@ GEM
121
121
  iri (0.11.2)
122
122
  joined (0.4.0)
123
123
  json (2.13.2)
124
- judges (0.53.3)
124
+ judges (0.53.6)
125
125
  backtrace (~> 0.4)
126
126
  baza.rb (~> 0.5)
127
127
  concurrent-ruby (~> 1.2)
@@ -203,7 +203,7 @@ GEM
203
203
  rubocop-ast (1.46.0)
204
204
  parser (>= 3.3.7.2)
205
205
  prism (~> 1.4)
206
- rubocop-minitest (0.38.1)
206
+ rubocop-minitest (0.38.2)
207
207
  lint_roller (~> 1.1)
208
208
  rubocop (>= 1.75.0, < 2.0)
209
209
  rubocop-ast (>= 1.38.0, < 2.0)
data/fbe.gemspec CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |s|
19
19
  s.authors = ['Yegor Bugayenko']
20
20
  s.email = 'yegor256@gmail.com'
21
21
  s.homepage = 'https://github.com/zerocracy/fbe'
22
- s.files = `git ls-files`.split($RS)
22
+ s.files = `git ls-files | grep -v -E '^(test/|renovate|coverage)'`.split($RS)
23
23
  s.rdoc_options = ['--charset=UTF-8']
24
24
  s.extra_rdoc_files = ['README.md', 'LICENSE.txt']
25
25
  s.add_dependency 'backtrace', '~>0.4'
data/lib/fbe/conclude.rb CHANGED
@@ -17,13 +17,16 @@ require_relative 'octo'
17
17
  # @param [Judges::Options] options The options coming from the +judges+ tool
18
18
  # @param [Loog] loog The logging facility
19
19
  # @yield [Factbase::Fact] The fact
20
- def Fbe.conclude(fb: Fbe.fb, judge: $judge, loog: $loog, options: $options, global: $global, time: Time, &)
20
+ def Fbe.conclude(
21
+ fb: Fbe.fb, judge: $judge, loog: $loog, options: $options, global: $global,
22
+ start: $start, time: Time, &
23
+ )
21
24
  raise 'The fb is nil' if fb.nil?
22
25
  raise 'The $judge is not set' if judge.nil?
23
26
  raise 'The $global is not set' if global.nil?
24
27
  raise 'The $options is not set' if options.nil?
25
28
  raise 'The $loog is not set' if loog.nil?
26
- c = Fbe::Conclude.new(fb:, judge:, loog:, options:, global:, time:)
29
+ c = Fbe::Conclude.new(fb:, judge:, loog:, options:, global:, start:, time:)
27
30
  c.instance_eval(&)
28
31
  end
29
32
 
@@ -59,28 +62,38 @@ class Fbe::Conclude
59
62
  # @param [Judges::Options] options The options coming from the +judges+ tool
60
63
  # @param [Loog] loog The logging facility
61
64
  # @param [Time] time The time
62
- def initialize(fb:, judge:, global:, options:, loog:, time: Time)
65
+ def initialize(fb:, judge:, global:, options:, loog:, start:, time: Time)
63
66
  @fb = fb
64
67
  @judge = judge
65
68
  @loog = loog
66
69
  @options = options
67
70
  @global = global
71
+ @start = start
68
72
  @query = nil
69
73
  @follows = []
70
- @quota_aware = false
74
+ @lifetime_aware = true
75
+ @quota_aware = true
71
76
  @timeout = 60
72
77
  @time = time
73
78
  end
74
79
 
75
- # Make this block aware of GitHub API quota.
80
+ # Make this block not aware of GitHub API quota.
76
81
  #
77
- # When the quota is reached, the loop will gracefully stop to avoid
78
- # hitting GitHub API rate limits. This helps prevent interruptions
79
- # in long-running operations.
82
+ # When the quota is reached, the loop will NOT gracefully stop to avoid
83
+ # hitting GitHub API rate limits.
80
84
  #
81
85
  # @return [nil] Nothing is returned
82
- def quota_aware
83
- @quota_aware = true
86
+ def quota_unaware
87
+ @quota_aware = false
88
+ end
89
+
90
+ # Make this block NOT aware of lifetime limitations.
91
+ #
92
+ # When the lifetime is over, the loop will NOT gracefully stop.
93
+ #
94
+ # @return [nil] Nothing is returned
95
+ def lifetime_aware
96
+ @lifetime_aware = false
84
97
  end
85
98
 
86
99
  # Make sure this block runs for less than allowed amount of seconds.
@@ -189,6 +202,10 @@ class Fbe::Conclude
189
202
  @loog.debug('We ran out of GitHub quota, must stop here')
190
203
  break
191
204
  end
205
+ if @lifetime_aware && @options.lifetime && Time.now - @start > @options.lifetime - 10
206
+ @loog.debug('We ran out of lifetime, must stop here')
207
+ break
208
+ end
192
209
  now = @time.now
193
210
  if now > start + @timeout
194
211
  @loog.debug("We've spent more than #{start.ago}, must stop here")
@@ -49,12 +49,17 @@ end
49
49
  # @param [Judges::Options] options Options containing 'repositories' field with masks
50
50
  # @param [Hash] global Global cache for storing API responses
51
51
  # @param [Loog] loog Logger for debug output
52
+ # @param [Time] start When did we start the entire pipeline
52
53
  # @param [quota_aware] Boolean Should we stop if quota is off?
54
+ # @param [lifetime_aware] Boolean Should we stop if lifetime is over?
53
55
  # @return [Array<String>] Shuffled list of repository full names (e.g., 'org/repo')
54
56
  # @raise [RuntimeError] If no repositories match the provided masks
55
57
  # @note Exclusion patterns must start with '-' (e.g., '-org/pattern*')
56
58
  # @note Results are shuffled to distribute load when processing
57
- def Fbe.unmask_repos(options: $options, global: $global, loog: $loog, quota_aware: true)
59
+ def Fbe.unmask_repos(
60
+ options: $options, global: $global, loog: $loog, start: $start,
61
+ quota_aware: true, lifetime_aware: true
62
+ )
58
63
  raise 'Repositories mask is not specified' unless options.repositories
59
64
  raise 'Repositories mask is empty' if options.repositories.empty?
60
65
  repos = []
@@ -82,7 +87,11 @@ def Fbe.unmask_repos(options: $options, global: $global, loog: $loog, quota_awar
82
87
  return repos unless block_given?
83
88
  repos.each do |repo|
84
89
  if quota_aware && octo.off_quota?
85
- $loog.info("No GitHub quota left, it is time to stop at #{repo}")
90
+ loog.info("No GitHub quota left, it is time to stop at #{repo}")
91
+ break
92
+ end
93
+ if lifetime_aware && options.lifetime && Time.now - start > options.lifetime - 10
94
+ loog.info("No time left, it is time to stop at #{repo}")
86
95
  break
87
96
  end
88
97
  yield repo
data/lib/fbe.rb CHANGED
@@ -10,5 +10,5 @@
10
10
  # License:: MIT
11
11
  module Fbe
12
12
  # Current version of the gem (changed by +.rultor.yml+ on every release)
13
- VERSION = '0.29.1' unless const_defined?(:VERSION)
13
+ VERSION = '0.31.0' unless const_defined?(:VERSION)
14
14
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fbe
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.29.1
4
+ version: 0.31.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko
@@ -391,37 +391,7 @@ files:
391
391
  - lib/fbe/tombstone.rb
392
392
  - lib/fbe/unmask_repos.rb
393
393
  - lib/fbe/who.rb
394
- - renovate.json
395
394
  - rules/basic.fe
396
- - test/fbe/middleware/test_formatter.rb
397
- - test/fbe/middleware/test_rate_limit.rb
398
- - test/fbe/middleware/test_sqlite_store.rb
399
- - test/fbe/middleware/test_trace.rb
400
- - test/fbe/test_award.rb
401
- - test/fbe/test_bylaws.rb
402
- - test/fbe/test_conclude.rb
403
- - test/fbe/test_copy.rb
404
- - test/fbe/test_delete.rb
405
- - test/fbe/test_delete_one.rb
406
- - test/fbe/test_enter.rb
407
- - test/fbe/test_fb.rb
408
- - test/fbe/test_github_graph.rb
409
- - test/fbe/test_if_absent.rb
410
- - test/fbe/test_issue.rb
411
- - test/fbe/test_iterate.rb
412
- - test/fbe/test_just_one.rb
413
- - test/fbe/test_kill_if.rb
414
- - test/fbe/test_octo.rb
415
- - test/fbe/test_overwrite.rb
416
- - test/fbe/test_pmp.rb
417
- - test/fbe/test_regularly.rb
418
- - test/fbe/test_repeatedly.rb
419
- - test/fbe/test_sec.rb
420
- - test/fbe/test_tombstone.rb
421
- - test/fbe/test_unmask_repos.rb
422
- - test/fbe/test_who.rb
423
- - test/test__helper.rb
424
- - test/test_fbe.rb
425
395
  homepage: https://github.com/zerocracy/fbe
426
396
  licenses:
427
397
  - MIT
data/renovate.json DELETED
@@ -1,6 +0,0 @@
1
- {
2
- "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3
- "extends": [
4
- "config:base"
5
- ]
6
- }
@@ -1,104 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Zerocracy
4
- # SPDX-License-Identifier: MIT
5
-
6
- require 'faraday'
7
- require 'loog'
8
- require 'securerandom'
9
- require_relative '../../../lib/fbe'
10
- require_relative '../../../lib/fbe/middleware'
11
- require_relative '../../../lib/fbe/middleware/formatter'
12
- require_relative '../../test__helper'
13
-
14
- # Test.
15
- # Author:: Yegor Bugayenko (yegor256@gmail.com)
16
- # Copyright:: Copyright (c) 2024-2025 Zerocracy
17
- # License:: MIT
18
- class LoggingFormatterTest < Fbe::Test
19
- def test_success_response
20
- log_it(status: 200) do |loog|
21
- assert_empty(loog.to_s)
22
- end
23
- end
24
-
25
- def test_forward_response
26
- log_it(status: 303) do |loog|
27
- assert_empty(loog.to_s)
28
- end
29
- end
30
-
31
- def test_error_response
32
- log_it(status: 401) do |loog|
33
- str = loog.to_s
34
- refute_empty(str)
35
- [
36
- %r{http://example.com},
37
- /Authorization:/,
38
- %r{HTTP/1.1 401},
39
- /x-github-api-version-selected: "2022-11-28"/,
40
- /hello, world!/,
41
- /request body/
42
- ].each { |ptn| assert_match(ptn, str) }
43
- end
44
- end
45
-
46
- def test_limit_response
47
- log_it(status: 403) do |loog|
48
- str = loog.to_s
49
- refute_empty(str)
50
- end
51
- end
52
-
53
- def test_truncate_body_for_error_text_response
54
- body = SecureRandom.alphanumeric(120)
55
- log_it(
56
- status: 502,
57
- response_body: body,
58
- response_headers: {
59
- 'content-type' => 'text/html; charset=utf-8',
60
- 'x-github-api-version-selected' => '2022-11-28'
61
- }
62
- ) do |loog|
63
- str = loog.to_s
64
- refute_empty(str)
65
- [
66
- %r{http://example.com},
67
- /Authorization:/,
68
- /some request body/,
69
- %r{HTTP/1.1 502},
70
- /x-github-api-version-selected: "2022-11-28"/,
71
- %r{content-type: "text/html; charset=utf-8"},
72
- "#{body.slice(0, 97)}..."
73
- ].each { |ptn| assert_match(ptn, str) }
74
- end
75
- end
76
-
77
- private
78
-
79
- def log_it(
80
- status:,
81
- method: :get,
82
- response_body: '{"message": "hello, world!"}',
83
- response_headers: { 'content-type' => 'application/json', 'x-github-api-version-selected' => '2022-11-28' }
84
- )
85
- loog = Loog::Buffer.new
86
- formatter = Fbe::Middleware::Formatter.new(logger: loog, options: {})
87
- formatter.request(
88
- Faraday::Env.from(
89
- {
90
- method:,
91
- request_body: 'some request body',
92
- url: URI('http://example.com'),
93
- request_headers: {
94
- 'Authorization' => 'Bearer github_pat_11AAsecret'
95
- }
96
- }
97
- )
98
- )
99
- formatter.response(
100
- Faraday::Env.from({ status:, response_body:, response_headers: })
101
- )
102
- yield loog
103
- end
104
- end
@@ -1,152 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # SPDX-FileCopyrightText: Copyright (c) 2024-2025 Zerocracy
4
- # SPDX-License-Identifier: MIT
5
-
6
- require 'faraday'
7
- require 'webmock'
8
- require_relative '../../../lib/fbe'
9
- require_relative '../../../lib/fbe/middleware'
10
- require_relative '../../../lib/fbe/middleware/rate_limit'
11
- require_relative '../../test__helper'
12
-
13
- # Test.
14
- # Author:: Yegor Bugayenko (yegor256@gmail.com)
15
- # Copyright:: Copyright (c) 2024-2025 Zerocracy
16
- # License:: MIT
17
- class RateLimitTest < Fbe::Test
18
- def test_caches_rate_limit_response_on_first_call
19
- rate_limit_response = {
20
- 'rate' => {
21
- 'limit' => 5000,
22
- 'remaining' => 4999,
23
- 'reset' => 1_672_531_200
24
- }
25
- }
26
- stub_request(:get, 'https://api.github.com/rate_limit')
27
- .to_return(status: 200, body: rate_limit_response.to_json, headers: { 'Content-Type' => 'application/json' })
28
- conn = create_connection
29
- response = conn.get('/rate_limit')
30
- assert_equal 200, response.status
31
- assert_equal 4999, response.body['rate']['remaining']
32
- end
33
-
34
- def test_returns_cached_response_on_subsequent_calls
35
- rate_limit_response = {
36
- 'rate' => {
37
- 'limit' => 5000,
38
- 'remaining' => 4999,
39
- 'reset' => 1_672_531_200
40
- }
41
- }
42
- stub_request(:get, 'https://api.github.com/rate_limit')
43
- .to_return(status: 200, body: rate_limit_response.to_json, headers: { 'Content-Type' => 'application/json' })
44
- .times(1)
45
- conn = create_connection
46
- conn.get('/rate_limit')
47
- response = conn.get('/rate_limit')
48
- assert_equal 200, response.status
49
- assert_equal 4999, response.body['rate']['remaining']
50
- assert_requested :get, 'https://api.github.com/rate_limit', times: 1
51
- end
52
-
53
- def test_decrements_remaining_count_for_non_rate_limit_requests
54
- rate_limit_response = {
55
- 'rate' => {
56
- 'limit' => 5000,
57
- 'remaining' => 4999,
58
- 'reset' => 1_672_531_200
59
- }
60
- }
61
- stub_request(:get, 'https://api.github.com/rate_limit')
62
- .to_return(status: 200, body: rate_limit_response.to_json, headers: { 'Content-Type' => 'application/json',
63
- 'X-RateLimit-Remaining' => '4999' })
64
- stub_request(:get, 'https://api.github.com/user')
65
- .to_return(status: 200, body: '{"login": "test"}', headers: { 'Content-Type' => 'application/json' })
66
- conn = create_connection
67
- conn.get('/rate_limit')
68
- conn.get('/user')
69
- response = conn.get('/rate_limit')
70
- assert_equal 4998, response.body['rate']['remaining']
71
- assert_equal '4998', response.headers['x-ratelimit-remaining']
72
- end
73
-
74
- def test_refreshes_cache_after_hundred_requests
75
- rate_limit_response = {
76
- 'rate' => {
77
- 'limit' => 5000,
78
- 'remaining' => 4999,
79
- 'reset' => 1_672_531_200
80
- }
81
- }
82
- refreshed_response = {
83
- 'rate' => {
84
- 'limit' => 5000,
85
- 'remaining' => 4950,
86
- 'reset' => 1_672_531_200
87
- }
88
- }
89
- stub_request(:get, 'https://api.github.com/rate_limit')
90
- .to_return(status: 200, body: rate_limit_response.to_json, headers: { 'Content-Type' => 'application/json' })
91
- .then
92
- .to_return(status: 200, body: refreshed_response.to_json, headers: { 'Content-Type' => 'application/json' })
93
- stub_request(:get, 'https://api.github.com/user')
94
- .to_return(status: 200, body: '{"login": "test"}', headers: { 'Content-Type' => 'application/json' })
95
- .times(100)
96
- conn = create_connection
97
- conn.get('/rate_limit')
98
- 100.times { conn.get('/user') }
99
- response = conn.get('/rate_limit')
100
- assert_equal 4950, response.body['rate']['remaining']
101
- assert_requested :get, 'https://api.github.com/rate_limit', times: 2
102
- end
103
-
104
- def test_handles_response_without_rate_data
105
- stub_request(:get, 'https://api.github.com/rate_limit')
106
- .to_return(status: 200, body: '{}', headers: { 'Content-Type' => 'application/json' })
107
- conn = create_connection
108
- response = conn.get('/rate_limit')
109
- assert_equal 200, response.status
110
- assert_empty(response.body)
111
- end
112
-
113
- def test_ignores_non_hash_response_body
114
- stub_request(:get, 'https://api.github.com/rate_limit')
115
- .to_return(status: 200, body: 'invalid json', headers: { 'Content-Type' => 'text/plain' })
116
- conn = create_connection
117
- response = conn.get('/rate_limit')
118
- assert_equal 200, response.status
119
- assert_equal 'invalid json', response.body
120
- end
121
-
122
- def test_handles_zero_remaining_count
123
- rate_limit_response = {
124
- 'rate' => {
125
- 'limit' => 5000,
126
- 'remaining' => 1,
127
- 'reset' => 1_672_531_200
128
- }
129
- }
130
- stub_request(:get, 'https://api.github.com/rate_limit')
131
- .to_return(status: 200, body: rate_limit_response.to_json, headers: { 'Content-Type' => 'application/json' })
132
- stub_request(:get, 'https://api.github.com/user')
133
- .to_return(status: 200, body: '{"login": "test"}', headers: { 'Content-Type' => 'application/json' })
134
- .times(2)
135
- conn = create_connection
136
- conn.get('/rate_limit')
137
- conn.get('/user')
138
- conn.get('/user')
139
- response = conn.get('/rate_limit')
140
- assert_equal 0, response.body['rate']['remaining']
141
- end
142
-
143
- private
144
-
145
- def create_connection
146
- Faraday.new(url: 'https://api.github.com') do |f|
147
- f.use Fbe::Middleware::RateLimit
148
- f.response :json
149
- f.adapter :net_http
150
- end
151
- end
152
- end