nonnative 3.19.0 → 3.22.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: 011e40916b32666c33c2a0374ee97c3e96d563cf950f5c68e12b8afa7c18e986
4
- data.tar.gz: 7b21a33ad8fc14077751e679b66a14523c79c0783004934f55fa61ea5608e477
3
+ metadata.gz: 6fa10a31c57c45551a417924990d2d870e137d7561c1581577a6e965099472cc
4
+ data.tar.gz: dcd07b46e9ce599562c9c0f517ff7b6f46b529dfcf70c8f462cef3fd548704eb
5
5
  SHA512:
6
- metadata.gz: 0e24fea055ed0e972507bfcb8ebbdf2c604d41e1a6908333a1863f1ba44dd356ea518936d3d6076f73fb37d0c3a9f593b6321ef1392d51d6d1cdf4c145b5ad6d
7
- data.tar.gz: ec70302587120b093bb506d5d324821e880b10e406974822f44e1efa7c3fdee29465847eb8f71466ab3806ddd0eb328f2e0788bca28196066c6b3636ce0edb83
6
+ metadata.gz: cb3cf0ffe148327fb056ad6a16851156b060373d69a06dc13b615197bb6a3aec5cb70faf412c4f31a68f8e92ce297646201fd3151c068758cc22017f5c1a745f
7
+ data.tar.gz: 8caa697ae5eb5c0b005da119b5ff518f68407c52cf876ca065a522147bc98d3064fff7899e54da1897408b9b963b87124ba1f62fd729913c073e3e1de8016dab
data/README.md CHANGED
@@ -211,6 +211,15 @@ Nonnative::Token.http_audience('GET', '/v1/things') # => "GET /v1/things"
211
211
  Nonnative::Token.grpc_audience('/health.v1.Health/Check') # => "/health.v1.Health/Check"
212
212
  ```
213
213
 
214
+ By default the time claims are pinned to the current time (`iat`/`nbf` at now, `exp` at `now + expiration`). To write negative auth tests, `generate` accepts optional absolute `Time` overrides — `issued_at`, `not_before`, and `expires_at` — for minting not-yet-valid or clock-skewed tokens:
215
+
216
+ ```ruby
217
+ # a token that is not valid until an hour from now
218
+ token.generate(aud: 'GET /v1/things', sub: 'user-1', not_before: Time.now + 3600)
219
+ ```
220
+
221
+ `ssh` tokens have no `nbf` claim, so passing `not_before` for the `ssh` kind raises `ArgumentError`.
222
+
214
223
  ### 🔁 Lifecycle strategies (Cucumber integration)
215
224
 
216
225
  Nonnative ships Cucumber hooks (when loaded) that support these tags/strategies:
@@ -789,9 +798,10 @@ Clients connect to the service `host`/`port`, while the proxy forwards traffic t
789
798
 
790
799
  - `close_all` - Closes the socket as soon as it connects.
791
800
  - `reset_peer` - Resets the socket as soon as it connects, so clients observe a TCP reset (`Errno::ECONNRESET`) rather than the graceful close performed by `close_all`.
792
- - `delay` - Delays traffic on the connection. Defaults to 2 seconds and can be configured through options.
801
+ - `delay` - Delays traffic on the connection. Defaults to 2 seconds and can be configured through `options.delay`. An optional `options.jitter` (seconds) adds a random offset in `-jitter..jitter` to each delay (a negative value uses its magnitude), so clients see variable, tail-latency-like timing instead of a flat value.
793
802
  - `timeout` - Accepts the connection and stalls traffic until reset or stop closes the connection, so clients exercise their own read timeout behavior.
794
803
  - `invalid_data` - Forwards client requests unchanged, then corrupts upstream responses before they reach the client.
804
+ - `bandwidth` - Throttles forwarded throughput to `options.rate` kilobytes per second (1 KB = 1024 bytes) by sleeping in proportion to the bytes read, in both directions, so clients see a slow-but-alive dependency. When `rate` is absent or not positive, traffic forwards at full speed.
795
805
 
796
806
  ###### 🧩 Fault Injection Services
797
807
 
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nonnative
4
+ # Socket-pair variant used by the fault-injection proxy to simulate a bandwidth-limited link.
5
+ #
6
+ # When active, each forwarded read is throttled so throughput does not exceed
7
+ # `proxy.options[:rate]` kilobytes per second (1 KB = 1024 bytes), by sleeping in proportion to the
8
+ # bytes read. Both directions are throttled, so a client under test sees a slow-but-alive
9
+ # dependency. When `rate` is absent or not positive the connection forwards at full speed.
10
+ #
11
+ # This behavior is enabled by calling {Nonnative::FaultInjectionProxy#bandwidth}.
12
+ #
13
+ # @see Nonnative::FaultInjectionProxy
14
+ # @see Nonnative::SocketPairFactory
15
+ # @see Nonnative::SocketPair
16
+ class BandwidthSocketPair < SocketPair
17
+ # One kilobyte in bytes; `proxy.options[:rate]` is expressed in KB/s.
18
+ KILOBYTE = 1024
19
+
20
+ # Reads from the socket, then sleeps so the forwarded throughput stays within the configured rate.
21
+ #
22
+ # @param socket [IO] the socket to read from
23
+ # @return [String] the bytes read from the socket
24
+ def read(socket)
25
+ super.tap { |data| throttle(data.bytesize) }
26
+ end
27
+
28
+ private
29
+
30
+ # Sleeps in proportion to the bytes read so throughput does not exceed `rate` KB/s. A missing or
31
+ # non-positive rate (or an empty read) forwards at full speed.
32
+ #
33
+ # @param bytes [Integer] the number of bytes just read
34
+ # @return [void]
35
+ def throttle(bytes)
36
+ rate = proxy.options[:rate]
37
+ return if rate.nil? || rate <= 0 || bytes.zero?
38
+
39
+ sleep(bytes / (rate * KILOBYTE.to_f))
40
+ end
41
+ end
42
+ end
@@ -59,6 +59,7 @@ module Nonnative
59
59
  'delay' => :delay,
60
60
  'timeout' => :timeout,
61
61
  'invalid_data' => :invalid_data,
62
+ 'bandwidth' => :bandwidth,
62
63
  'reset' => :reset
63
64
  }.freeze
64
65
 
@@ -5,7 +5,11 @@ module Nonnative
5
5
  #
6
6
  # When active, reads from the socket are delayed by a configured duration before being forwarded.
7
7
  #
8
- # The delay duration is controlled by `proxy.options[:delay]` and defaults to 2 seconds.
8
+ # The delay duration is controlled by `proxy.options[:delay]` and defaults to 2 seconds. An
9
+ # optional `proxy.options[:jitter]` (seconds) adds a random offset in `-jitter..jitter` to each
10
+ # delay so clients see variable, tail-latency-like timing instead of a flat value; a negative
11
+ # jitter uses its magnitude and the resulting delay is never negative. When `jitter` is absent the
12
+ # delay is the flat duration.
9
13
  #
10
14
  # This behavior is enabled by calling {Nonnative::FaultInjectionProxy#delay}.
11
15
  #
@@ -20,10 +24,22 @@ module Nonnative
20
24
  def read(socket)
21
25
  Nonnative.logger.info "delaying socket '#{socket.inspect}' for 'delay' pair"
22
26
 
23
- duration = proxy.options[:delay] || 2
24
- sleep duration
27
+ sleep delay_duration
25
28
 
26
29
  super
27
30
  end
31
+
32
+ private
33
+
34
+ # The delay applied before a read, optionally jittered by `proxy.options[:jitter]`.
35
+ #
36
+ # @return [Numeric] seconds to sleep (never negative)
37
+ def delay_duration
38
+ duration = proxy.options[:delay] || 2
39
+ jitter = proxy.options[:jitter]&.abs
40
+ return duration unless jitter
41
+
42
+ [duration + rand(-jitter..jitter), 0].max
43
+ end
28
44
  end
29
45
  end
@@ -14,6 +14,7 @@ module Nonnative
14
14
  # - {#delay}: delay reads by a configured duration (default: 2 seconds)
15
15
  # - {#timeout}: accept connections and keep them silent until clients time out
16
16
  # - {#invalid_data}: forward requests unchanged and mutate upstream responses before they reach clients
17
+ # - {#bandwidth}: throttle forwarded throughput to a configured rate (KB/s)
17
18
  # - {#reset}: return to healthy pass-through behavior
18
19
  #
19
20
  # State changes terminate any active connections so new connections observe the new behavior.
@@ -33,6 +34,8 @@ module Nonnative
33
34
  # - `wait`: sleep interval (seconds) applied after state changes
34
35
  # - `options`:
35
36
  # - `delay`: delay duration in seconds used by {#delay}
37
+ # - `jitter`: optional random offset (seconds) added in `-jitter..jitter` to each `delay` (a
38
+ # negative value uses its magnitude), so clients see variable latency instead of a flat value
36
39
  #
37
40
  # @see Nonnative::Proxy
38
41
  # @see Nonnative::SocketPairFactory
@@ -137,6 +140,16 @@ module Nonnative
137
140
  apply_state :invalid_data
138
141
  end
139
142
 
143
+ # Throttles forwarded throughput to a configured rate.
144
+ #
145
+ # The rate is controlled by `service.proxy.options[:rate]` (kilobytes per second); when it is
146
+ # absent or not positive the connection forwards at full speed.
147
+ #
148
+ # @return [void]
149
+ def bandwidth
150
+ apply_state :bandwidth
151
+ end
152
+
140
153
  # Resets the proxy back to healthy pass-through behavior.
141
154
  #
142
155
  # @return [void]
@@ -27,16 +27,19 @@ module Nonnative
27
27
  #
28
28
  # @param aud [String] the `aud` claim (for example `"GET /v1/things"` or a gRPC full method)
29
29
  # @param sub [String] the `sub` claim
30
+ # @param issued_at [Time, nil] overrides the `iat` claim (default: now)
31
+ # @param not_before [Time, nil] overrides the `nbf` claim (default: `issued_at`)
32
+ # @param expires_at [Time, nil] overrides the `exp` claim (default: `issued_at` plus `expiration`)
30
33
  # @return [String] the signed JWT
31
- def generate(aud:, sub:)
32
- now = Time.now.to_i
34
+ def generate(aud:, sub:, issued_at: nil, not_before: nil, expires_at: nil)
35
+ now = issued_at || Time.now
33
36
  payload = {
34
37
  iss: @issuer,
35
38
  aud: aud,
36
39
  sub: sub,
37
- iat: now,
38
- nbf: now,
39
- exp: now + @expiration,
40
+ iat: now.to_i,
41
+ nbf: (not_before || now).to_i,
42
+ exp: (expires_at || (now + @expiration)).to_i,
40
43
  jti: SecureRandom.uuid
41
44
  }
42
45
 
@@ -28,18 +28,21 @@ module Nonnative
28
28
  #
29
29
  # @param aud [String] the `aud` claim (for example `"GET /v1/things"` or a gRPC full method)
30
30
  # @param sub [String] the `sub` claim
31
+ # @param issued_at [Time, nil] overrides the `iat` claim (default: now)
32
+ # @param not_before [Time, nil] overrides the `nbf` claim (default: `issued_at`)
33
+ # @param expires_at [Time, nil] overrides the `exp` claim (default: `issued_at` plus `expiration`)
31
34
  # @return [String] the signed PASETO token
32
- def generate(aud:, sub:)
35
+ def generate(aud:, sub:, issued_at: nil, not_before: nil, expires_at: nil)
33
36
  load_dependencies!
34
37
 
35
- now = Time.now.utc
38
+ now = (issued_at || Time.now).utc
36
39
  claims = {
37
40
  'iss' => @issuer,
38
41
  'aud' => aud,
39
42
  'sub' => sub,
40
43
  'iat' => now.iso8601,
41
- 'nbf' => now.iso8601,
42
- 'exp' => (now + @expiration).iso8601,
44
+ 'nbf' => (not_before || now).utc.iso8601,
45
+ 'exp' => (expires_at || (now + @expiration)).utc.iso8601,
43
46
  'jti' => SecureRandom.uuid
44
47
  }
45
48
 
@@ -13,6 +13,7 @@ module Nonnative
13
13
  # - `:delay` -> {Nonnative::DelaySocketPair}
14
14
  # - `:timeout` -> {Nonnative::TimeoutSocketPair}
15
15
  # - `:invalid_data` -> {Nonnative::InvalidDataSocketPair}
16
+ # - `:bandwidth` -> {Nonnative::BandwidthSocketPair}
16
17
  #
17
18
  # @see Nonnative::FaultInjectionProxy
18
19
  # @see Nonnative::SocketPair
@@ -21,11 +22,12 @@ module Nonnative
21
22
  # @see Nonnative::DelaySocketPair
22
23
  # @see Nonnative::TimeoutSocketPair
23
24
  # @see Nonnative::InvalidDataSocketPair
25
+ # @see Nonnative::BandwidthSocketPair
24
26
  class SocketPairFactory
25
27
  class << self
26
28
  # Creates a socket-pair instance for the given proxy state.
27
29
  #
28
- # @param kind [Symbol] proxy state (e.g. `:none`, `:close_all`, `:reset_peer`, `:delay`, `:timeout`, `:invalid_data`)
30
+ # @param kind [Symbol] proxy state (e.g. `:none`, `:close_all`, `:reset_peer`, `:delay`, `:timeout`, `:invalid_data`, `:bandwidth`)
29
31
  # @param proxy [Nonnative::ConfigurationProxy] proxy configuration (host/port/options)
30
32
  # @return [Nonnative::SocketPair] a socket-pair implementation instance
31
33
  def create(kind, proxy)
@@ -40,6 +42,8 @@ module Nonnative
40
42
  TimeoutSocketPair
41
43
  when :invalid_data
42
44
  InvalidDataSocketPair
45
+ when :bandwidth
46
+ BandwidthSocketPair
43
47
  else
44
48
  SocketPair
45
49
  end
@@ -30,18 +30,25 @@ module Nonnative
30
30
 
31
31
  # Generates a signed SSH token.
32
32
  #
33
+ # SSH tokens carry only `iat`/`exp` (in Unix nanoseconds); there is no `nbf` claim, so
34
+ # `not_before` is rejected.
35
+ #
33
36
  # @param aud [String] the `aud` claim (for example `"GET /v1/things"` or a gRPC full method)
37
+ # @param issued_at [Time, nil] overrides the `iat` claim (default: now)
38
+ # @param expires_at [Time, nil] overrides the `exp` claim (default: `issued_at` plus `expiration`)
39
+ # @raise [ArgumentError] if `not_before` is given (SSH tokens have no `nbf` claim)
34
40
  # @return [String] the token, `"<base64(claims)>.<base64(signature)>"`
35
- def generate(aud:, **)
36
- now = Time.now
37
- issued_at = (now.to_i * 1_000_000_000) + now.nsec
41
+ def generate(aud:, issued_at: nil, expires_at: nil, not_before: nil, **)
42
+ raise ArgumentError, "ssh tokens do not support 'not_before' (no nbf claim)" unless not_before.nil?
43
+
44
+ iat = nanoseconds(issued_at || Time.now)
38
45
  claims = {
39
46
  ver: TOKEN_VERSION,
40
47
  kid: @key,
41
48
  sub: @key,
42
49
  aud: aud,
43
- iat: issued_at,
44
- exp: issued_at + (@expiration * 1_000_000_000)
50
+ iat: iat,
51
+ exp: expires_at ? nanoseconds(expires_at) : iat + (@expiration * 1_000_000_000)
45
52
  }.to_json
46
53
 
47
54
  signature = Ed25519::SigningKey.new(seed).sign(claims)
@@ -51,6 +58,10 @@ module Nonnative
51
58
 
52
59
  private
53
60
 
61
+ def nanoseconds(time)
62
+ (time.to_i * 1_000_000_000) + time.nsec
63
+ end
64
+
54
65
  def seed
55
66
  SSHData::PrivateKey.parse_openssh(File.read(@private_key)).first.sk[0, 32]
56
67
  end
@@ -54,11 +54,20 @@ module Nonnative
54
54
 
55
55
  # Generates a signed token.
56
56
  #
57
+ # The optional time claims default to the current time (and the constructor `expiration`), so
58
+ # omitting them reproduces the token's normal claims. Supply them to mint tokens with specific
59
+ # time claims for negative auth tests, such as a not-yet-valid (future `not_before`) or
60
+ # clock-skewed token. Times are absolute; the `ssh` kind has no `nbf` claim and rejects
61
+ # `not_before`.
62
+ #
57
63
  # @param aud [String] the `aud` claim
58
64
  # @param sub [String] the `sub` claim
65
+ # @param issued_at [Time, nil] overrides the `iat` claim (default: now)
66
+ # @param not_before [Time, nil] overrides the `nbf` claim (default: `issued_at`); unsupported by `ssh`
67
+ # @param expires_at [Time, nil] overrides the `exp` claim (default: `issued_at` plus `expiration`)
59
68
  # @return [String] the signed token
60
- def generate(aud:, sub:)
61
- @token.generate(aud: aud, sub: sub)
69
+ def generate(aud:, sub:, issued_at: nil, not_before: nil, expires_at: nil)
70
+ @token.generate(aud: aud, sub: sub, issued_at: issued_at, not_before: not_before, expires_at: expires_at)
62
71
  end
63
72
  end
64
73
  end
@@ -4,5 +4,5 @@ module Nonnative
4
4
  # The current gem version.
5
5
  #
6
6
  # @return [String]
7
- VERSION = '3.19.0'
7
+ VERSION = '3.22.0'
8
8
  end
data/lib/nonnative.rb CHANGED
@@ -115,6 +115,7 @@ require 'nonnative/reset_peer_socket_pair'
115
115
  require 'nonnative/delay_socket_pair'
116
116
  require 'nonnative/timeout_socket_pair'
117
117
  require 'nonnative/invalid_data_socket_pair'
118
+ require 'nonnative/bandwidth_socket_pair'
118
119
  require 'nonnative/socket_pair_factory'
119
120
  require 'nonnative/go_executable'
120
121
  require 'nonnative/cucumber'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nonnative
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.19.0
4
+ version: 3.22.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alejandro Falkowski
@@ -391,24 +391,10 @@ executables: []
391
391
  extensions: []
392
392
  extra_rdoc_files: []
393
393
  files:
394
- - ".circleci/config.yml"
395
- - ".claude/skills"
396
- - ".codecov.yml"
397
- - ".config/cucumber.yml"
398
- - ".editorconfig"
399
- - ".github/dependabot.yml"
400
- - ".gitignore"
401
- - ".gitmodules"
402
- - ".goreleaser.yml"
403
- - ".rubocop.yml"
404
- - AGENTS.md
405
- - CLAUDE.md
406
- - Gemfile
407
- - Gemfile.lock
408
394
  - LICENSE
409
- - Makefile
410
395
  - README.md
411
396
  - lib/nonnative.rb
397
+ - lib/nonnative/bandwidth_socket_pair.rb
412
398
  - lib/nonnative/close_all_socket_pair.rb
413
399
  - lib/nonnative/configuration.rb
414
400
  - lib/nonnative/configuration_file.rb
@@ -461,7 +447,6 @@ files:
461
447
  - lib/nonnative/timeout_socket_pair.rb
462
448
  - lib/nonnative/token.rb
463
449
  - lib/nonnative/version.rb
464
- - nonnative.gemspec
465
450
  homepage: https://github.com/alexfalkowski/nonnative
466
451
  licenses:
467
452
  - MIT
data/.circleci/config.yml DELETED
@@ -1,97 +0,0 @@
1
- version: 2.1
2
-
3
- commands:
4
- sync-submodules:
5
- steps:
6
- - run: git submodule sync
7
- - run: git submodule update --init
8
- checkout-submodules:
9
- steps:
10
- - checkout:
11
- method: blobless
12
- - sync-submodules
13
-
14
- executors:
15
- ruby:
16
- docker:
17
- - image: alexfalkowski/ruby:3.20
18
- release:
19
- docker:
20
- - image: alexfalkowski/release:8.22
21
- alpine:
22
- docker:
23
- - image: alpine:latest
24
-
25
- jobs:
26
- build:
27
- executor: ruby
28
- working_directory: ~/nonnative
29
- steps:
30
- - checkout-submodules
31
- - run: make source-key
32
- - restore_cache:
33
- name: restore deps
34
- keys:
35
- - nonnative-ruby-cache-{{ checksum "Gemfile.lock" }}-{{ checksum "~/.ruby-version" }}
36
- - nonnative-ruby-cache-
37
- - run: make dep
38
- - run: make clean-dep
39
- - save_cache:
40
- name: save deps
41
- key: nonnative-ruby-cache-{{ checksum "Gemfile.lock" }}-{{ checksum "~/.ruby-version" }}
42
- paths:
43
- - vendor
44
- - run: make lint
45
- - run: make sec
46
- - run: make features
47
- - run: make benchmarks
48
- - store_test_results:
49
- path: test/reports
50
- - store_artifacts:
51
- path: test/reports
52
- - run: make codecov-upload
53
- resource_class: arm.large
54
- sync:
55
- executor: release
56
- working_directory: ~/nonnative
57
- steps:
58
- - checkout-submodules
59
- - run: make sync push
60
- resource_class: arm.large
61
- version:
62
- executor: release
63
- working_directory: ~/nonnative
64
- steps:
65
- - checkout-submodules
66
- - run: version
67
- - run: package
68
- resource_class: arm.large
69
- wait-all:
70
- executor: alpine
71
- resource_class: small
72
- steps:
73
- - run: echo "all applicable jobs finished"
74
-
75
- workflows:
76
- nonnative:
77
- jobs:
78
- - build
79
- - sync:
80
- requires:
81
- - build
82
- filters:
83
- branches:
84
- ignore: master
85
- - version:
86
- context: gh
87
- serial-group: << pipeline.project.slug >>/version
88
- requires:
89
- - build
90
- filters:
91
- branches:
92
- only: master
93
- - wait-all:
94
- requires:
95
- - build
96
- - sync
97
- - version
data/.claude/skills DELETED
@@ -1 +0,0 @@
1
- ../bin/skills
data/.codecov.yml DELETED
@@ -1,6 +0,0 @@
1
- coverage:
2
- precision: 2
3
- round: down
4
- range: "80...100"
5
- status:
6
- patch: false
data/.config/cucumber.yml DELETED
@@ -1 +0,0 @@
1
- report: --format junit --out test/reports --format html --out test/reports/index.html --format pretty --publish-quiet
data/.editorconfig DELETED
@@ -1,13 +0,0 @@
1
- root = true
2
-
3
- [*]
4
- charset = utf-8
5
- end_of_line = lf
6
- indent_style = space
7
- indent_size = 2
8
- insert_final_newline = true
9
- trim_trailing_whitespace = true
10
-
11
- [Makefile]
12
- indent_style = tab
13
- indent_size = 8
@@ -1,20 +0,0 @@
1
- version: 2
2
- updates:
3
- - package-ecosystem: "bundler"
4
- directory: "/"
5
- schedule:
6
- interval: "daily"
7
- commit-message:
8
- prefix: "test"
9
- prefix-development: "build"
10
- include: "scope"
11
- - package-ecosystem: "gitsubmodule"
12
- directory: "/"
13
- schedule:
14
- interval: "daily"
15
- commit-message:
16
- prefix: "build"
17
- prefix-development: "build"
18
- include: "scope"
19
- allow:
20
- - dependency-name: "bin"
data/.gitignore DELETED
@@ -1,18 +0,0 @@
1
- /.bundle/
2
- .crush/
3
- .ruby-lsp/
4
- .source-key
5
- /.yardoc
6
- /_yardoc/
7
- /doc/
8
- /pkg/
9
- ISSUES.md
10
- TESTS.md
11
- DOCS.md
12
- FEATURES.md
13
- PROJECTS.md
14
- RELIABILITY.md
15
- test/reports/*
16
- !test/reports/.keep
17
- /tmp/
18
- vendor/
data/.gitmodules DELETED
@@ -1,3 +0,0 @@
1
- [submodule "bin"]
2
- path = bin
3
- url = git@github.com:alexfalkowski/bin.git
data/.goreleaser.yml DELETED
@@ -1,17 +0,0 @@
1
- version: 2
2
-
3
- builds:
4
- - skip: true
5
-
6
- changelog:
7
- sort: asc
8
- filters:
9
- exclude:
10
- - "^chore"
11
-
12
- release:
13
- footer: >-
14
-
15
- ---
16
-
17
- Released by [GoReleaser](https://github.com/goreleaser/goreleaser).
data/.rubocop.yml DELETED
@@ -1,35 +0,0 @@
1
- AllCops:
2
- TargetRubyVersion: 4.0
3
- DisplayCopNames: true
4
- NewCops: enable
5
- Exclude:
6
- - "bin/**/*"
7
- - "vendor/**/*"
8
- - "test/grpc/**/*"
9
-
10
- Layout/LineLength:
11
- Max: 150
12
-
13
- Lint/HashCompareByIdentity:
14
- Enabled: false
15
-
16
- Metrics/MethodLength:
17
- Max: 20
18
-
19
- Metrics/BlockLength:
20
- Max: 90
21
-
22
- Metrics/AbcSize:
23
- Max: 25
24
-
25
- Metrics/ClassLength:
26
- Max: 150
27
-
28
- Style/Documentation:
29
- Enabled: false
30
-
31
- Style/NumericPredicate:
32
- Enabled: false
33
-
34
- Style/FileOpen:
35
- Enabled: false
data/AGENTS.md DELETED
@@ -1,208 +0,0 @@
1
- # AGENTS.md
2
-
3
- ## Shared guidance
4
-
5
- Use `bin/AGENTS.md` for shared skills and cross-repository defaults.
6
-
7
- `nonnative` is a Ruby gem for end-to-end testing systems implemented in other
8
- languages. It starts processes, in-process servers, and proxy-only services,
9
- waits on TCP readiness/shutdown, and can place fault-injection proxies in front
10
- of dependencies.
11
-
12
- ## Map And Commands
13
-
14
- - Library: `lib/nonnative/**/*.rb`
15
- - Cucumber features/support: `features/**/*.feature`, `features/support/**/*.rb`, `features/step_definitions/**/*.rb`
16
- - Generated gRPC test stubs: `test/grpc/**/*`
17
- - Test protos: `test/nonnative/v1/*.proto`
18
- - Build wiring: root `Makefile` includes `bin/build/make/*.mak`
19
- - Required submodule: `bin/`; missing submodule setup breaks `make`
20
- - Install deps: `make dep`
21
- - Lint: `make lint`
22
- - Security checks: `make sec`
23
- - Features: `make features`
24
- - Benchmarks only: `make benchmarks`
25
- - Cleanup: `make clean-dep`, `make clean-reports`
26
-
27
- ## Intentional Design Choices
28
-
29
- - The configured HTTP proxy feature intentionally uses the external
30
- `www.afalkowski.com` host through `features/support/http_proxy_server.rb`.
31
- Do not flag this external dependency as a code issue unless the task is
32
- explicitly about making HTTP proxy fixtures fully hermetic.
33
- - `test/Makefile` intentionally consumes the shared Buf make fragment as-is.
34
- Do not flag its inherited `breaking` target's `subdir=api` comparison as a
35
- code issue unless the task is explicitly about changing test proto breaking
36
- checks.
37
- - `nonnative` is a test-support gem used across many downstream projects.
38
- Do not flag the absence of an isolated gem build/install smoke test as a
39
- test gap unless the task is explicitly about release packaging or gem
40
- publication validation.
41
- - The Cucumber `@startup` lifecycle tag is exercised by downstream projects
42
- that use this gem. Do not flag missing in-repository `@startup` acceptance
43
- coverage as a test gap unless the task is explicitly about changing
44
- Cucumber startup hook behavior.
45
- - `Nonnative.go_argv` and `Nonnative.go_command` flag combinations are checked
46
- by external/downstream usage. Do not flag missing in-repository exhaustive
47
- `-test.*` flag or tool-filtering assertions as a test gap unless the task is
48
- explicitly about changing Go command generation.
49
- - Generated gRPC test stubs under `test/grpc/` are updated on demand when the
50
- test proto changes. Do not flag the absence of an automatic generated-stub
51
- freshness check as a test gap unless the task is explicitly about changing
52
- test proto generation or generated-file validation.
53
- - Generated gRPC test stubs may carry manual require-path adjustments after
54
- generation. Do not treat `make -C test stale` as a required validation target
55
- unless the task is explicitly about changing test proto generation; verify
56
- the generated Ruby loads before accepting generator-only rewrites.
57
- - Buf linting for the test-only proto module is intentionally not part of the
58
- required local validation surface. Do not flag missing root-level validation
59
- for `test/buf.yaml` as a test gap unless the task is explicitly about test
60
- proto linting or Buf validation.
61
- - The public proxy reset Cucumber steps and `@reset` hook are tested by
62
- external/downstream suites. Do not flag missing in-repository direct proxy
63
- reset step coverage as a test gap unless the task is explicitly about
64
- changing proxy reset behavior.
65
- - `Nonnative.clear` configuration and pool reset behavior is covered by
66
- external/downstream suites. Do not flag missing in-repository assertions for
67
- configuration or pool clearing as a test gap unless the task is explicitly
68
- about changing clear/reset lifecycle behavior.
69
- - Full repository validation is expected to run both regular features and
70
- benchmark-tagged lifecycle scenarios. Do not flag rollback/timeout lifecycle
71
- coverage living under `features/benchmark.feature` as a test gap unless the
72
- task is explicitly about changing validation target composition.
73
- - Service proxy round-trip behavior is validated by downstream projects that
74
- use `nonnative` against real dependencies. Do not flag the in-repository
75
- service proxy success scenario's connection-only assertion as a test gap
76
- unless the task is explicitly about changing service proxy forwarding.
77
- - `Nonnative::FaultInjectionProxy` owns its listener thread and stops it by
78
- closing the owned `TCPServer`, which wakes the accept loop before joining the
79
- thread. Do not flag the listener `join` as an unbounded reliability gap unless
80
- the task is explicitly about proxy shutdown behavior or there is a normal-use
81
- reproducer, failing CI evidence, or platform-specific hang evidence.
82
- - Service readiness is intentionally TCP-only for externally managed
83
- dependencies. Do not flag missing HTTP/gRPC service readiness as a feature gap
84
- unless the task is explicitly about changing the `Nonnative::Service`
85
- readiness model. HTTP/gRPC readiness belongs to managed processes, where the
86
- user explicitly models the application health endpoints.
87
- - Process runners intentionally inherit the parent working directory. Nonnative
88
- is normally run from the test folder that owns `test/nonnative.yml`, relative
89
- config paths, logs, and reports. Do not flag missing process cwd/chdir support
90
- as a feature gap unless the task is explicitly about changing process working
91
- directory behavior.
92
- - Message-specific start/stop assertions and attempted-stop success assertions
93
- in `features/step_definitions/lifecycle_steps.rb` are repository-local
94
- Cucumber helpers. Do not flag their absence from `lib/nonnative/cucumber.rb`
95
- as a feature gap unless downstream usage evidence exists or the task is
96
- explicitly about expanding public lifecycle assertion steps.
97
- - Liveness, readiness, and metrics endpoint assertion steps in
98
- `features/step_definitions/servers_steps.rb` are repository-local Cucumber
99
- helpers for testing nonnative's own framework assumptions. Do not flag their
100
- absence from `lib/nonnative/cucumber.rb` as a feature gap unless downstream
101
- usage evidence exists or the task is explicitly about expanding public
102
- observability assertion steps.
103
- - `Nonnative::Configuration` intentionally exposes `process_by_name` without
104
- matching `server_by_name` or `service_by_name` helpers. Do not flag missing
105
- server/service configuration lookup helpers as a feature gap unless downstream
106
- usage evidence exists or the task is explicitly about changing the
107
- pre-start configuration lookup API.
108
-
109
- ## Runtime Model
110
-
111
- Public entry point: `lib/nonnative.rb`.
112
-
113
- Main API: `configure`, `start`, `stop`, `clear`, `reset`, `pool`,
114
- `go_argv`, `go_command`, `token`.
115
-
116
- Configuration is `Nonnative::Configuration`, built with
117
- `config.process`, `config.server`, `config.service`, or
118
- `config.load_file(...)`.
119
-
120
- Runners:
121
-
122
- - `Nonnative::Process`: OS process
123
- - `Nonnative::Server`: in-process Ruby server thread
124
- - `Nonnative::Service`: proxy lifecycle for an externally managed dependency
125
-
126
- `Nonnative::Pool` starts services first, then servers/processes, and stops in
127
- reverse. Readiness and shutdown checks are TCP-only via
128
- `Nonnative::Port#open?` and `#closed?`.
129
-
130
- Token generation: `Nonnative.token(kind:, issuer:, key:, private_key:, expiration:)`
131
- returns a `Nonnative::Token` whose `generate(aud:, sub:)` produces a signed token for
132
- authenticating against services under test; it feeds `Nonnative::Header.auth_bearer`.
133
- Kinds are `jwt` (EdDSA, `kid` header), `paseto` (v4.public, `kid` footer), and `ssh`
134
- (go-service style raw-Ed25519 `base64(claims).base64(signature)`). All Ed25519 and
135
- generation-only. `jwt`/`paseto` take a PKCS#8 PEM key; `ssh` takes an OpenSSH-format
136
- key. PASETO needs system libsodium (via `rbnacl`), required lazily so `require
137
- 'nonnative'` works without it until a PASETO token is generated.
138
- `Nonnative::Token.http_audience` / `grpc_audience` build the endpoint-scoped `aud`.
139
-
140
- ## Cucumber Surface
141
-
142
- `lib/nonnative/cucumber.rb` is public compatibility surface. Do not remove or
143
- rename hooks/step text unless the user explicitly requests a breaking change.
144
-
145
- Lifecycle tags:
146
-
147
- - `@startup`: start before scenario, stop after scenario
148
- - `@manual`: scenario starts manually, stop after scenario
149
- - `@clear`: call `Nonnative.clear` before scenario
150
- - `@reset`: reset proxies after scenario
151
-
152
- Suite taxonomy tags: `@acceptance`, `@contract`, `@proxy`, `@config`,
153
- `@service`, `@benchmark`, `@slow`. `make features` excludes `@benchmark`;
154
- `make benchmarks` runs only `@benchmark`.
155
-
156
- `Nonnative.clear` clears configuration, logger, observability client, and pool.
157
- `require 'nonnative'` loads Cucumber integration lazily and is safe outside a
158
- booted Cucumber runtime. For start-once-per-test-run, use
159
- `require 'nonnative/startup'`.
160
-
161
- ## Proxy And Config Gotchas
162
-
163
- Proxy wiring is the easiest mistake:
164
-
165
- - Runner `host` / `port` are client-facing and used for readiness/shutdown
166
- - For `fault_injection`, nested `proxy.host` / `proxy.port` are the upstream target
167
- - Clients connect to runner `host` / `port` when a proxy is enabled
168
-
169
- Proxy kinds: `none`, `fault_injection`.
170
- Fault-injection states: `none`, `close_all`, `delay`, `invalid_data`.
171
-
172
- Config rules:
173
-
174
- - YAML config is loaded as data only via `Nonnative::ConfigurationFile`; ERB is not evaluated and arbitrary Ruby object tags are rejected
175
- - Runner `host` and nested `proxy.host` default to `127.0.0.1`; use explicit `0.0.0.0` only when external access is intended
176
- - Process `command` can be a legacy shell string or an argv array; prefer argv arrays for new config, and `go:` config builds argv internally with `Nonnative.go_argv`
177
- - Use `Nonnative.go_argv` for no-shell Go executable argv entries and `Nonnative.go_command` only when a caller needs Ruby shell-style command string spawning
178
- - YAML services belong under `services:`, not `processes:`
179
- - There is no top-level `config.wait`; `wait` is per runner
180
- - Programmatic service config uses `config.service do |s| ... end`, so use `s.host` / `s.port`
181
- - Proxy examples need both endpoint sides: runner `host` / `port` for the proxy, nested `proxy.host` / `proxy.port` for upstream
182
-
183
- ## Fixtures And Limitations
184
-
185
- Useful fixtures:
186
-
187
- - Process: `features/support/bin/start`
188
- - HTTP: `features/support/http_server.rb`, `features/support/http_proxy_server.rb`
189
- - TCP: `features/support/tcp_server.rb`
190
- - gRPC: `features/support/grpc_server.rb`, generated stubs in `test/grpc/`
191
-
192
- Limitations:
193
-
194
- - The `grpc` Ruby library uses a global logger; per-server gRPC loggers are not really supported
195
- - Local Ruby/Bundler mismatches can break native extensions on macOS
196
- - Coverage and Cucumber reports go under `test/reports`
197
- - Port checks can be flaky if tests reuse ports unexpectedly
198
-
199
- ## Look First
200
-
201
- - Lifecycle: `lib/nonnative.rb`, `lib/nonnative/pool.rb`
202
- - Readiness/timeouts: `lib/nonnative/port.rb`, `lib/nonnative/timeout.rb`
203
- - Process lifecycle: `lib/nonnative/process.rb`
204
- - Go executable command/argv building: `lib/nonnative/go_executable.rb`
205
- - Token generation: `lib/nonnative/token.rb`, `lib/nonnative/jwt_token.rb`, `lib/nonnative/paseto_token.rb`, `lib/nonnative/ssh_token.rb`, `lib/nonnative/ed25519_key.rb`
206
- - Proxies: `lib/nonnative/fault_injection_proxy.rb`, `lib/nonnative/socket_pair_factory.rb`
207
- - Cucumber: `lib/nonnative/cucumber.rb`, `lib/nonnative/startup.rb`, `features/support/env.rb`
208
- - Config loading: `lib/nonnative/configuration.rb`, `lib/nonnative/configuration_file.rb`, `lib/nonnative/configuration_runner.rb`, `lib/nonnative/configuration_proxy.rb`
data/CLAUDE.md DELETED
@@ -1,4 +0,0 @@
1
- <!-- BEGIN bin claude-init -->
2
- @AGENTS.md
3
- @bin/AGENTS.md
4
- <!-- END bin claude-init -->
data/Gemfile DELETED
@@ -1,14 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- source 'https://rubygems.org'
4
-
5
- git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
-
7
- # Specify your gem's dependencies in nonnative.gemspec
8
- gemspec
9
-
10
- gem 'bundler'
11
- gem 'rubocop'
12
- gem 'ruby-lsp'
13
- gem 'simplecov'
14
- gem 'simplecov-cobertura'
data/Gemfile.lock DELETED
@@ -1,232 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- nonnative (3.19.0)
5
- concurrent-ruby (>= 1, < 2)
6
- config (>= 5, < 6)
7
- cucumber (>= 7, < 12)
8
- cucumber-cucumber-expressions (< 19)
9
- ed25519 (>= 1, < 2)
10
- get_process_mem (>= 1, < 2)
11
- grpc (>= 1, < 2)
12
- jwt (>= 3, < 4)
13
- jwt-eddsa (>= 0, < 1)
14
- puma (>= 7, < 8)
15
- rbnacl (>= 7, < 8)
16
- rest-client (>= 2, < 3)
17
- retriable (>= 3, < 4)
18
- rspec-benchmark (>= 0, < 1)
19
- rspec-expectations (>= 3, < 4)
20
- rspec-wait (>= 1, < 2)
21
- ruby-paseto (>= 0, < 1)
22
- sinatra (>= 4, < 5)
23
- ssh_data (>= 1, < 2)
24
-
25
- GEM
26
- remote: https://rubygems.org/
27
- specs:
28
- ast (2.4.3)
29
- base64 (0.3.0)
30
- benchmark-malloc (0.2.0)
31
- benchmark-perf (0.6.0)
32
- benchmark-trend (0.4.0)
33
- bigdecimal (4.1.2)
34
- builder (3.3.0)
35
- concurrent-ruby (1.3.7)
36
- config (5.6.1)
37
- deep_merge (~> 1.2, >= 1.2.1)
38
- ostruct
39
- cucumber (11.1.1)
40
- base64 (~> 0.2)
41
- builder (~> 3.2)
42
- cucumber-ci-environment (> 9, < 12)
43
- cucumber-core (>= 16.2.0, < 17)
44
- cucumber-cucumber-expressions (> 17, < 20)
45
- cucumber-html-formatter (> 21, < 24)
46
- diff-lcs (~> 1.5)
47
- logger (~> 1.6)
48
- mini_mime (~> 1.1)
49
- multi_test (~> 1.1)
50
- sys-uname (~> 1.5)
51
- cucumber-ci-environment (11.0.0)
52
- cucumber-core (16.2.0)
53
- cucumber-gherkin (> 36, < 40)
54
- cucumber-messages (> 31, < 33)
55
- cucumber-tag-expressions (> 6, < 9)
56
- cucumber-cucumber-expressions (18.1.0)
57
- bigdecimal
58
- cucumber-gherkin (39.1.0)
59
- cucumber-messages (>= 31, < 33)
60
- cucumber-html-formatter (23.1.0)
61
- cucumber-messages (> 23, < 33)
62
- cucumber-messages (32.3.1)
63
- cucumber-tag-expressions (8.1.0)
64
- deep_merge (1.2.2)
65
- diff-lcs (1.6.2)
66
- docile (1.4.1)
67
- domain_name (0.6.20240107)
68
- ed25519 (1.4.0)
69
- ffi (1.17.4-x86_64-darwin)
70
- ffi (1.17.4-x86_64-linux-gnu)
71
- get_process_mem (1.0.0)
72
- bigdecimal (>= 2.0)
73
- ffi (~> 1.0)
74
- google-protobuf (4.35.1-x86_64-darwin)
75
- bigdecimal
76
- rake (~> 13.3)
77
- google-protobuf (4.35.1-x86_64-linux-gnu)
78
- bigdecimal
79
- rake (~> 13.3)
80
- googleapis-common-protos-types (1.23.0)
81
- google-protobuf (~> 4.26)
82
- grpc (1.82.0-x86_64-darwin)
83
- google-protobuf (>= 3.25, < 5.0)
84
- googleapis-common-protos-types (~> 1.0)
85
- grpc (1.82.0-x86_64-linux-gnu)
86
- google-protobuf (>= 3.25, < 5.0)
87
- googleapis-common-protos-types (~> 1.0)
88
- http-accept (1.7.0)
89
- http-cookie (1.1.6)
90
- domain_name (~> 0.5)
91
- json (2.20.0)
92
- jwt (3.2.0)
93
- base64
94
- jwt-eddsa (0.9.0)
95
- base64
96
- ed25519
97
- jwt (>= 2.9.0)
98
- language_server-protocol (3.17.0.6)
99
- lint_roller (1.1.0)
100
- logger (1.7.0)
101
- memoist3 (1.0.0)
102
- mime-types (3.7.0)
103
- logger
104
- mime-types-data (~> 3.2025, >= 3.2025.0507)
105
- mime-types-data (3.2026.0701)
106
- mini_mime (1.1.5)
107
- multi_json (1.15.0)
108
- multi_test (1.1.0)
109
- mustermann (3.1.1)
110
- netrc (0.11.0)
111
- nio4r (2.7.5)
112
- openssl (3.3.3)
113
- ostruct (0.6.3)
114
- parallel (2.1.0)
115
- parser (3.3.11.1)
116
- ast (~> 2.4.1)
117
- racc
118
- prism (1.9.0)
119
- puma (7.2.1)
120
- nio4r (~> 2.0)
121
- racc (1.8.1)
122
- rack (3.2.6)
123
- rack-protection (4.2.1)
124
- base64 (>= 0.1.0)
125
- logger (>= 1.6.0)
126
- rack (>= 3.0.0, < 4)
127
- rack-session (2.1.2)
128
- base64 (>= 0.1.0)
129
- rack (>= 3.0.0)
130
- rainbow (3.1.1)
131
- rake (13.4.2)
132
- rbnacl (7.1.2)
133
- ffi (~> 1)
134
- rbs (4.0.3)
135
- logger
136
- prism (>= 1.6.0)
137
- tsort
138
- regexp_parser (2.12.0)
139
- rest-client (2.1.0)
140
- http-accept (>= 1.7.0, < 2.0)
141
- http-cookie (>= 1.0.2, < 2.0)
142
- mime-types (>= 1.16, < 4.0)
143
- netrc (~> 0.8)
144
- retriable (3.8.0)
145
- rexml (3.4.4)
146
- rspec (3.13.2)
147
- rspec-core (~> 3.13.0)
148
- rspec-expectations (~> 3.13.0)
149
- rspec-mocks (~> 3.13.0)
150
- rspec-benchmark (0.6.0)
151
- benchmark-malloc (~> 0.2)
152
- benchmark-perf (~> 0.6)
153
- benchmark-trend (~> 0.4)
154
- rspec (>= 3.0)
155
- rspec-core (3.13.6)
156
- rspec-support (~> 3.13.0)
157
- rspec-expectations (3.13.5)
158
- diff-lcs (>= 1.2.0, < 2.0)
159
- rspec-support (~> 3.13.0)
160
- rspec-mocks (3.13.8)
161
- diff-lcs (>= 1.2.0, < 2.0)
162
- rspec-support (~> 3.13.0)
163
- rspec-support (3.13.7)
164
- rspec-wait (1.0.2)
165
- rspec (>= 3.4)
166
- rubocop (1.88.1)
167
- json (~> 2.3)
168
- language_server-protocol (~> 3.17.0.2)
169
- lint_roller (~> 1.1.0)
170
- parallel (>= 1.10)
171
- parser (>= 3.3.0.2)
172
- rainbow (>= 2.2.2, < 4.0)
173
- regexp_parser (>= 2.9.3, < 3.0)
174
- rubocop-ast (>= 1.49.0, < 2.0)
175
- ruby-progressbar (~> 1.7)
176
- unicode-display_width (>= 2.4.0, < 4.0)
177
- rubocop-ast (1.50.0)
178
- parser (>= 3.3.7.2)
179
- prism (~> 1.7)
180
- ruby-lsp (0.26.9)
181
- language_server-protocol (~> 3.17.0)
182
- prism (>= 1.2, < 2.0)
183
- rbs (>= 3, < 5)
184
- ruby-paseto (0.2.0)
185
- multi_json (~> 1.15.0)
186
- openssl (~> 3.3)
187
- sorbet-runtime
188
- zeitwerk
189
- ruby-progressbar (1.13.0)
190
- simplecov (0.22.0)
191
- docile (~> 1.1)
192
- simplecov-html (~> 0.11)
193
- simplecov_json_formatter (~> 0.1)
194
- simplecov-cobertura (3.2.0)
195
- rexml
196
- simplecov (~> 0.19)
197
- simplecov-html (0.13.2)
198
- simplecov_json_formatter (0.1.4)
199
- sinatra (4.2.1)
200
- logger (>= 1.6.0)
201
- mustermann (~> 3.0)
202
- rack (>= 3.0.0, < 4)
203
- rack-protection (= 4.2.1)
204
- rack-session (>= 2.0.0, < 3)
205
- tilt (~> 2.0)
206
- sorbet-runtime (0.6.13323)
207
- ssh_data (1.3.0)
208
- sys-uname (1.5.1)
209
- ffi (~> 1.1)
210
- memoist3 (~> 1.0.0)
211
- tilt (2.8.0)
212
- tsort (0.2.0)
213
- unicode-display_width (3.2.0)
214
- unicode-emoji (~> 4.1)
215
- unicode-emoji (4.2.0)
216
- zeitwerk (2.8.2)
217
-
218
- PLATFORMS
219
- x86_64-darwin-21
220
- x86_64-darwin-23
221
- x86_64-linux
222
-
223
- DEPENDENCIES
224
- bundler
225
- nonnative!
226
- rubocop
227
- ruby-lsp
228
- simplecov
229
- simplecov-cobertura
230
-
231
- BUNDLED WITH
232
- 4.0.11
data/Makefile DELETED
@@ -1,4 +0,0 @@
1
- include bin/build/make/help.mak
2
- include bin/build/make/ruby.mak
3
- include bin/build/make/git.mak
4
- include bin/build/make/claude.mak
data/nonnative.gemspec DELETED
@@ -1,46 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- lib = File.expand_path('lib', __dir__)
4
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
-
6
- require 'nonnative/version'
7
-
8
- Gem::Specification.new do |spec|
9
- spec.name = 'nonnative'
10
- spec.version = Nonnative::VERSION
11
- spec.authors = ['Alejandro Falkowski']
12
- spec.email = ['alexrfalkowski@gmail.com']
13
-
14
- spec.summary = 'Ruby-first end-to-end harness for testing systems implemented in other languages'
15
- spec.description = 'Starts OS processes, in-process Ruby servers, and proxy-only services with TCP readiness checks and fault-injection proxies.'
16
- spec.homepage = 'https://github.com/alexfalkowski/nonnative'
17
- spec.license = 'MIT'
18
- spec.files = Dir.chdir(File.expand_path(__dir__)) do
19
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
20
- end
21
- spec.bindir = 'exe'
22
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
- spec.require_paths = ['lib']
24
- spec.required_ruby_version = ['>= 4.0.0', '< 5.0.0']
25
- spec.metadata['rubygems_mfa_required'] = 'true'
26
-
27
- spec.add_dependency 'concurrent-ruby', '>= 1', '< 2'
28
- spec.add_dependency 'config', '>= 5', '< 6'
29
- spec.add_dependency 'cucumber', '>= 7', '< 12'
30
- spec.add_dependency 'cucumber-cucumber-expressions', '< 19'
31
- spec.add_dependency 'ed25519', '>= 1', '< 2'
32
- spec.add_dependency 'get_process_mem', '>= 1', '< 2'
33
- spec.add_dependency 'grpc', '>= 1', '< 2'
34
- spec.add_dependency 'jwt', '>= 3', '< 4'
35
- spec.add_dependency 'jwt-eddsa', '>= 0', '< 1'
36
- spec.add_dependency 'puma', '>= 7', '< 8'
37
- spec.add_dependency 'rbnacl', '>= 7', '< 8'
38
- spec.add_dependency 'rest-client', '>= 2', '< 3'
39
- spec.add_dependency 'retriable', '>= 3', '< 4'
40
- spec.add_dependency 'rspec-benchmark', '>= 0', '< 1'
41
- spec.add_dependency 'rspec-expectations', '>= 3', '< 4'
42
- spec.add_dependency 'rspec-wait', '>= 1', '< 2'
43
- spec.add_dependency 'ruby-paseto', '>= 0', '< 1'
44
- spec.add_dependency 'sinatra', '>= 4', '< 5'
45
- spec.add_dependency 'ssh_data', '>= 1', '< 2'
46
- end