sdk-reforge 1.9.0 → 1.9.1

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: 1c692e3a481a6340630232b56282b7e2dfa1e55102a03e910bb2522cc49a7660
4
- data.tar.gz: e4485f45667650616b375e9b1d4aa76b03f3377744fb223d2807e4a30e99a7f4
3
+ metadata.gz: 9c9d01ab174a9a1de737bd5791203b24fe2b4e4edc029497233e704a3475f17f
4
+ data.tar.gz: 21a6556498ae0131164663d297890669b100ae3dccdaf6575a7e17b996ef988b
5
5
  SHA512:
6
- metadata.gz: a378c5553acaf78760bdad194c3843f2d565a3cb8e66e4db3f462b5555748394674df9fa5a599eea144d533f6069dd3bc7a052bce3e604ede4193703e79fa719
7
- data.tar.gz: 39b53e20a22e1c793bbb9bbe4dbde40c90dcea9ac0298be835fea3a22c222dec28e1cb12741fe8564952201de30db3e5700204430104df475c99c514cca3d611
6
+ metadata.gz: 2c6b7c7f36252e0e9434dc77a82ffc043c39f6085b405440a988ae2b8145ca127b90eaceca1b07d63ffa66d5cded48477d53fb9b2178a9afc5825966ec2d4724
7
+ data.tar.gz: f662153d281cce2fa52dbc4e8e7ad0c5864c896168a403900ca621f9e691a1e84c72c70186342b613bda714638c368c8e51074a5388f8d1a2ab542d47eff1755
@@ -0,0 +1,46 @@
1
+ ---
2
+ name: Push gem
3
+
4
+ "on":
5
+ workflow_run:
6
+ workflows: ["Ruby"]
7
+ branches: [main]
8
+ types:
9
+ - completed
10
+
11
+ jobs:
12
+ push:
13
+ runs-on: ubuntu-latest
14
+ if: ${{ github.event.workflow_run.conclusion == 'success' }}
15
+
16
+ permissions:
17
+ contents: write
18
+ id-token: write
19
+
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+ with:
23
+ persist-credentials: false
24
+ submodules: recursive
25
+
26
+ - name: Set up Ruby
27
+ uses: ruby/setup-ruby@v1
28
+ with:
29
+ bundler-cache: true
30
+ ruby-version: ruby
31
+
32
+ - name: Check if version already exists
33
+ id: version-check
34
+ run: |
35
+ VERSION=$(cat VERSION)
36
+ if gem list -r sdk-reforge | grep -q "sdk-reforge ($VERSION)"; then
37
+ echo "version-exists=true" >> $GITHUB_OUTPUT
38
+ echo "Version $VERSION already exists on RubyGems, skipping publish"
39
+ else
40
+ echo "version-exists=false" >> $GITHUB_OUTPUT
41
+ echo "Version $VERSION not found, proceeding with publish"
42
+ fi
43
+
44
+ - name: Release gem
45
+ if: steps.version-check.outputs.version-exists == 'false'
46
+ uses: rubygems/release-gem@v1
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.9.1 - 2025-10-01
4
+
5
+ - Fix entrypoint
6
+
3
7
 
4
8
  ## 1.9.0 - 2025-08-23
5
9
 
data/Rakefile CHANGED
@@ -56,8 +56,9 @@ end
56
56
 
57
57
  # Add release task for CI
58
58
  task :release do
59
- sh 'gem build sdk-reforge.gemspec'
59
+ sh 'mkdir -p pkg'
60
60
  version = File.read('VERSION').strip
61
- gem_file = "sdk-reforge-#{version}.gem"
61
+ gem_file = "pkg/sdk-reforge-#{version}.gem"
62
+ sh "gem build sdk-reforge.gemspec --output #{gem_file}"
62
63
  sh "gem push #{gem_file}"
63
64
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.9.0
1
+ 1.9.1
data/dev/script_setup.rb CHANGED
@@ -12,7 +12,7 @@ spec.require_paths.each do |path|
12
12
  end
13
13
 
14
14
  spec.require_paths.each do |path|
15
- require "./lib/reforge-sdk"
15
+ require "./lib/sdk-reforge"
16
16
  end
17
17
 
18
18
  SemanticLogger.add_appender(io: $stdout)
@@ -140,6 +140,11 @@ module Reforge
140
140
  def load_url(conn, source)
141
141
  resp = conn.get('')
142
142
  if resp.status == 200
143
+ if resp.body.nil? || resp.body.empty?
144
+ LOG.warn "Checkpoint #{source} [#{conn.uri}] failed to load. Response body is empty"
145
+ return false
146
+ end
147
+
143
148
  configs = PrefabProto::Configs.decode(resp.body)
144
149
  load_configs(configs, source)
145
150
  cache_configs(configs)
@@ -218,7 +223,13 @@ module Reforge
218
223
  return false unless @options.use_local_cache
219
224
  File.open(cache_path) do |f|
220
225
  f.flock(File::LOCK_SH)
221
- configs = PrefabProto::Configs.decode_json(f.read)
226
+ content = f.read
227
+ if content.nil? || content.empty?
228
+ LOG.warn "Failed to read cached configs at #{cache_path}. File is empty"
229
+ return false
230
+ end
231
+
232
+ configs = PrefabProto::Configs.decode_json(content)
222
233
  load_configs(configs, :cache)
223
234
 
224
235
  hours_old = ((Time.now - File.mtime(f)) / 60 / 60).round(2)
@@ -235,7 +246,13 @@ module Reforge
235
246
  def load_json_file(file)
236
247
  File.open(file) do |f|
237
248
  f.flock(File::LOCK_SH)
238
- configs = PrefabProto::Configs.decode_json(f.read)
249
+ content = f.read
250
+ if content.nil? || content.empty?
251
+ LOG.warn "Failed to read datafile at #{file}. File is empty"
252
+ return false
253
+ end
254
+
255
+ configs = PrefabProto::Configs.decode_json(content)
239
256
  load_configs(configs, :datafile)
240
257
  end
241
258
  end
@@ -117,4 +117,4 @@ module Reforge
117
117
  raise Reforge::Errors::UninitializedError.new(key)
118
118
  end
119
119
  end
120
- end
120
+ end
@@ -73,7 +73,20 @@ module Reforge
73
73
  reconnect_time: @options.sse_default_reconnect_time,
74
74
  logger: Reforge::InternalLogger.new(SSE::Client)) do |client|
75
75
  client.on_event do |event|
76
- configs = PrefabProto::Configs.decode(Base64.decode64(event.data))
76
+ if event.data.nil? || event.data.empty?
77
+ @logger.error "SSE Streaming Error: Received empty data for url #{url}"
78
+ client.close
79
+ return
80
+ end
81
+
82
+ decoded_data = Base64.decode64(event.data)
83
+ if decoded_data.nil? || decoded_data.empty?
84
+ @logger.error "SSE Streaming Error: Decoded data is empty for url #{url}"
85
+ client.close
86
+ return
87
+ end
88
+
89
+ configs = PrefabProto::Configs.decode(decoded_data)
77
90
  load_configs.call(configs, event, :sse)
78
91
  end
79
92
 
@@ -49,7 +49,7 @@ require 'reforge/client'
49
49
  require 'reforge/config_client_presenter'
50
50
  require 'reforge/config_client'
51
51
  require 'reforge/feature_flag_client'
52
- require 'reforge/prefab'
52
+ require 'reforge/reforge'
53
53
  require 'reforge/murmer3'
54
54
  require 'reforge/javascript_stub'
55
55
  require 'reforge/semver'
@@ -0,0 +1,149 @@
1
+ # Generated by juwelier
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+ # stub: sdk-reforge 1.9.1 ruby lib
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = "sdk-reforge".freeze
9
+ s.version = "1.9.1"
10
+
11
+ s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
+ s.require_paths = ["lib".freeze]
13
+ s.authors = ["Jeff Dwyer".freeze]
14
+ s.date = "2025-10-01"
15
+ s.description = "Feature Flags, Live Config as a service".freeze
16
+ s.email = "jeff.dwyer@reforge.com.cloud".freeze
17
+ s.extra_rdoc_files = [
18
+ "CHANGELOG.md",
19
+ "LICENSE.txt",
20
+ "README.md"
21
+ ]
22
+ s.files = [
23
+ ".envrc.sample",
24
+ ".github/CODEOWNERS",
25
+ ".github/pull_request_template.md",
26
+ ".github/workflows/push_gem.yml",
27
+ ".github/workflows/ruby.yml",
28
+ ".gitmodules",
29
+ ".rubocop.yml",
30
+ ".tool-versions",
31
+ "CHANGELOG.md",
32
+ "CODEOWNERS",
33
+ "Gemfile",
34
+ "Gemfile.lock",
35
+ "LICENSE.txt",
36
+ "README.md",
37
+ "Rakefile",
38
+ "VERSION",
39
+ "compile_protos.sh",
40
+ "dev/allocation_stats",
41
+ "dev/benchmark",
42
+ "dev/console",
43
+ "dev/script_setup.rb",
44
+ "lib/prefab_pb.rb",
45
+ "lib/reforge/caching_http_connection.rb",
46
+ "lib/reforge/client.rb",
47
+ "lib/reforge/config_client.rb",
48
+ "lib/reforge/config_client_presenter.rb",
49
+ "lib/reforge/config_loader.rb",
50
+ "lib/reforge/config_resolver.rb",
51
+ "lib/reforge/config_value_unwrapper.rb",
52
+ "lib/reforge/config_value_wrapper.rb",
53
+ "lib/reforge/context.rb",
54
+ "lib/reforge/context_shape.rb",
55
+ "lib/reforge/context_shape_aggregator.rb",
56
+ "lib/reforge/criteria_evaluator.rb",
57
+ "lib/reforge/duration.rb",
58
+ "lib/reforge/encryption.rb",
59
+ "lib/reforge/error.rb",
60
+ "lib/reforge/errors/env_var_parse_error.rb",
61
+ "lib/reforge/errors/initialization_timeout_error.rb",
62
+ "lib/reforge/errors/invalid_sdk_key_error.rb",
63
+ "lib/reforge/errors/missing_default_error.rb",
64
+ "lib/reforge/errors/missing_env_var_error.rb",
65
+ "lib/reforge/errors/uninitialized_error.rb",
66
+ "lib/reforge/evaluation.rb",
67
+ "lib/reforge/evaluation_summary_aggregator.rb",
68
+ "lib/reforge/example_contexts_aggregator.rb",
69
+ "lib/reforge/exponential_backoff.rb",
70
+ "lib/reforge/feature_flag_client.rb",
71
+ "lib/reforge/fixed_size_hash.rb",
72
+ "lib/reforge/http_connection.rb",
73
+ "lib/reforge/internal_logger.rb",
74
+ "lib/reforge/javascript_stub.rb",
75
+ "lib/reforge/local_config_parser.rb",
76
+ "lib/reforge/murmer3.rb",
77
+ "lib/reforge/options.rb",
78
+ "lib/reforge/periodic_sync.rb",
79
+ "lib/reforge/rate_limit_cache.rb",
80
+ "lib/reforge/reforge.rb",
81
+ "lib/reforge/resolved_config_presenter.rb",
82
+ "lib/reforge/semver.rb",
83
+ "lib/reforge/sse_config_client.rb",
84
+ "lib/reforge/time_helpers.rb",
85
+ "lib/reforge/weighted_value_resolver.rb",
86
+ "lib/reforge/yaml_config_parser.rb",
87
+ "lib/sdk-reforge.rb",
88
+ "sdk-reforge.gemspec",
89
+ "test/fixtures/datafile.json",
90
+ "test/integration_test.rb",
91
+ "test/integration_test_helpers.rb",
92
+ "test/support/common_helpers.rb",
93
+ "test/support/mock_base_client.rb",
94
+ "test/support/mock_config_client.rb",
95
+ "test/support/mock_config_loader.rb",
96
+ "test/test_caching_http_connection.rb",
97
+ "test/test_client.rb",
98
+ "test/test_config_client.rb",
99
+ "test/test_config_loader.rb",
100
+ "test/test_config_resolver.rb",
101
+ "test/test_config_value_unwrapper.rb",
102
+ "test/test_config_value_wrapper.rb",
103
+ "test/test_context.rb",
104
+ "test/test_context_shape.rb",
105
+ "test/test_context_shape_aggregator.rb",
106
+ "test/test_criteria_evaluator.rb",
107
+ "test/test_duration.rb",
108
+ "test/test_encryption.rb",
109
+ "test/test_evaluation_summary_aggregator.rb",
110
+ "test/test_example_contexts_aggregator.rb",
111
+ "test/test_exponential_backoff.rb",
112
+ "test/test_feature_flag_client.rb",
113
+ "test/test_fixed_size_hash.rb",
114
+ "test/test_helper.rb",
115
+ "test/test_integration.rb",
116
+ "test/test_internal_logger.rb",
117
+ "test/test_javascript_stub.rb",
118
+ "test/test_local_config_parser.rb",
119
+ "test/test_logger_initialization.rb",
120
+ "test/test_options.rb",
121
+ "test/test_prefab.rb",
122
+ "test/test_rate_limit_cache.rb",
123
+ "test/test_semver.rb",
124
+ "test/test_sse_config_client.rb",
125
+ "test/test_weighted_value_resolver.rb"
126
+ ]
127
+ s.homepage = "http://github.com/ReforgeHQ/sdk-ruby".freeze
128
+ s.licenses = ["MIT".freeze]
129
+ s.rubygems_version = "3.4.19".freeze
130
+ s.summary = "Reforge Launch Ruby Infrastructure".freeze
131
+
132
+ s.specification_version = 4
133
+
134
+ s.add_runtime_dependency(%q<concurrent-ruby>.freeze, ["~> 1.0", ">= 1.0.5"])
135
+ s.add_runtime_dependency(%q<faraday>.freeze, [">= 0"])
136
+ s.add_runtime_dependency(%q<googleapis-common-protos-types>.freeze, [">= 0"])
137
+ s.add_runtime_dependency(%q<google-protobuf>.freeze, [">= 0"])
138
+ s.add_runtime_dependency(%q<ld-eventsource>.freeze, [">= 0"])
139
+ s.add_runtime_dependency(%q<uuid>.freeze, [">= 0"])
140
+ s.add_runtime_dependency(%q<activesupport>.freeze, [">= 4"])
141
+ s.add_runtime_dependency(%q<semantic_logger>.freeze, ["!= 4.16.0"])
142
+ s.add_development_dependency(%q<allocation_stats>.freeze, [">= 0"])
143
+ s.add_development_dependency(%q<benchmark-ips>.freeze, [">= 0"])
144
+ s.add_development_dependency(%q<bundler>.freeze, [">= 0"])
145
+ s.add_development_dependency(%q<juwelier>.freeze, ["~> 2.4.9"])
146
+ s.add_development_dependency(%q<rdoc>.freeze, [">= 0"])
147
+ s.add_development_dependency(%q<simplecov>.freeze, [">= 0"])
148
+ end
149
+
@@ -81,4 +81,71 @@ class TestConfigClient < Minitest::Test
81
81
  end
82
82
  end
83
83
 
84
+ def test_load_url_with_empty_body
85
+ options = Reforge::Options.new(
86
+ prefab_datasources: Reforge::Options::DATASOURCES::LOCAL_ONLY,
87
+ x_use_local_cache: true,
88
+ sdk_key: "123-ENV-KEY-SDK",)
89
+
90
+ config_client = Reforge::ConfigClient.new(MockBaseClient.new(options), 10)
91
+
92
+ # Mock connection with empty response body
93
+ mock_conn = Minitest::Mock.new
94
+ mock_resp = Minitest::Mock.new
95
+ mock_resp.expect(:status, 200)
96
+ mock_resp.expect(:body, '')
97
+ mock_resp.expect(:body, '')
98
+ mock_conn.expect(:get, mock_resp, [''])
99
+ mock_conn.expect(:uri, 'http://test.example.com')
100
+
101
+ result = config_client.send(:load_url, mock_conn, :test_source)
102
+
103
+ assert_equal false, result, 'Expected load_url to return false for empty body'
104
+ mock_conn.verify
105
+ mock_resp.verify
106
+
107
+ assert_logged [/Response body is empty/]
108
+ end
109
+
110
+ def test_load_cache_with_empty_file
111
+ options = Reforge::Options.new(
112
+ prefab_datasources: Reforge::Options::DATASOURCES::LOCAL_ONLY,
113
+ x_use_local_cache: true,
114
+ sdk_key: "123-ENV-KEY-SDK",)
115
+
116
+ config_client = Reforge::ConfigClient.new(MockBaseClient.new(options), 10)
117
+ cache_path = config_client.send(:cache_path)
118
+
119
+ # Create an empty cache file
120
+ FileUtils.mkdir_p(File.dirname(cache_path))
121
+ File.write(cache_path, '')
122
+
123
+ result = config_client.send(:load_cache)
124
+
125
+ assert_equal false, result, 'Expected load_cache to return false for empty file'
126
+ assert_logged [/File is empty/]
127
+ ensure
128
+ File.delete(cache_path) if File.exist?(cache_path)
129
+ end
130
+
131
+ def test_load_json_file_with_empty_file
132
+ options = Reforge::Options.new(
133
+ prefab_datasources: Reforge::Options::DATASOURCES::LOCAL_ONLY,
134
+ x_use_local_cache: true,
135
+ sdk_key: "123-ENV-KEY-SDK",)
136
+
137
+ config_client = Reforge::ConfigClient.new(MockBaseClient.new(options), 10)
138
+
139
+ # Create a temporary empty datafile
140
+ temp_file = File.join(Dir.tmpdir, 'test_empty_datafile.json')
141
+ File.write(temp_file, '')
142
+
143
+ result = config_client.send(:load_json_file, temp_file)
144
+
145
+ assert_equal false, result, 'Expected load_json_file to return false for empty file'
146
+ assert_logged [/File is empty/]
147
+ ensure
148
+ File.delete(temp_file) if File.exist?(temp_file)
149
+ end
150
+
84
151
  end
data/test/test_helper.rb CHANGED
@@ -5,7 +5,7 @@ require 'minitest/focus'
5
5
  require 'minitest/reporters'
6
6
  Minitest::Reporters.use! unless ENV['RM_INFO']
7
7
 
8
- require 'reforge-sdk'
8
+ require 'sdk-reforge'
9
9
 
10
10
  Dir.glob(File.join(File.dirname(__FILE__), 'support', '**', '*.rb')).each do |file|
11
11
  require file
@@ -208,4 +208,43 @@ class TestSSEConfigClient < Minitest::Test
208
208
  output << "data: CmYIu8fh4YaO0x4QZBo0bG9nLWxldmVsLmNsb3VkLnByZWZhYi5zZXJ2ZXIubG9nZ2luZy5FdmVudFByb2Nlc3NvciIfCAESG2phbWVzLmtlYmluZ2VyQHByZWZhYi5jbG91ZDgGSAkSDQhkELvH4eGGjtMeGGU=\n\n"
209
209
  end
210
210
  end
211
+
212
+ def test_empty_data_validation
213
+ # Unit test to verify that empty data is properly detected and handled
214
+ log_output = StringIO.new
215
+ logger = Logger.new(log_output)
216
+
217
+ # Test that empty event.data is detected
218
+ mock_event = OpenStruct.new(data: '')
219
+ mock_client = Minitest::Mock.new
220
+ mock_client.expect(:close, nil)
221
+
222
+ # Simulate the on_event handler logic
223
+ if mock_event.data.nil? || mock_event.data.empty?
224
+ logger.error "SSE Streaming Error: Received empty data for url http://test"
225
+ mock_client.close
226
+ end
227
+
228
+ log_lines = log_output.string.split("\n")
229
+ assert log_lines.any? { |line| line.include?('SSE Streaming Error') && line.include?('empty data') },
230
+ 'Expected to have logged an error about empty data'
231
+ mock_client.verify
232
+
233
+ # Test that nil event.data is detected
234
+ log_output = StringIO.new
235
+ logger = Logger.new(log_output)
236
+ mock_event = OpenStruct.new(data: nil)
237
+ mock_client = Minitest::Mock.new
238
+ mock_client.expect(:close, nil)
239
+
240
+ if mock_event.data.nil? || mock_event.data.empty?
241
+ logger.error "SSE Streaming Error: Received empty data for url http://test"
242
+ mock_client.close
243
+ end
244
+
245
+ log_lines = log_output.string.split("\n")
246
+ assert log_lines.any? { |line| line.include?('SSE Streaming Error') && line.include?('empty data') },
247
+ 'Expected to have logged an error about empty data for nil'
248
+ mock_client.verify
249
+ end
211
250
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sdk-reforge
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.0
4
+ version: 1.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeff Dwyer
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-09-23 00:00:00.000000000 Z
10
+ date: 2025-10-01 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: concurrent-ruby
@@ -223,6 +223,7 @@ files:
223
223
  - ".envrc.sample"
224
224
  - ".github/CODEOWNERS"
225
225
  - ".github/pull_request_template.md"
226
+ - ".github/workflows/push_gem.yml"
226
227
  - ".github/workflows/ruby.yml"
227
228
  - ".gitmodules"
228
229
  - ".rubocop.yml"
@@ -241,7 +242,6 @@ files:
241
242
  - dev/console
242
243
  - dev/script_setup.rb
243
244
  - lib/prefab_pb.rb
244
- - lib/reforge-sdk.rb
245
245
  - lib/reforge/caching_http_connection.rb
246
246
  - lib/reforge/client.rb
247
247
  - lib/reforge/config_client.rb
@@ -276,14 +276,16 @@ files:
276
276
  - lib/reforge/murmer3.rb
277
277
  - lib/reforge/options.rb
278
278
  - lib/reforge/periodic_sync.rb
279
- - lib/reforge/prefab.rb
280
279
  - lib/reforge/rate_limit_cache.rb
280
+ - lib/reforge/reforge.rb
281
281
  - lib/reforge/resolved_config_presenter.rb
282
282
  - lib/reforge/semver.rb
283
283
  - lib/reforge/sse_config_client.rb
284
284
  - lib/reforge/time_helpers.rb
285
285
  - lib/reforge/weighted_value_resolver.rb
286
286
  - lib/reforge/yaml_config_parser.rb
287
+ - lib/sdk-reforge.rb
288
+ - sdk-reforge.gemspec
287
289
  - test/fixtures/datafile.json
288
290
  - test/integration_test.rb
289
291
  - test/integration_test_helpers.rb