runbox 1.2.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 662927e34aedb615bcd9f472fd2d2841c75c0e4bc1cca64ef4122d46402a7acb
4
+ data.tar.gz: a2d0d12e759f2417194f22c8b4a6747c6a45bd66e1c0c50d629024924738f92e
5
+ SHA512:
6
+ metadata.gz: 77ef8ccb39538cd98d3d5f95c62dc5f615e2204100c3a098bc896f714d70cae82b77282ab6ae329eb868bd311797ba1dbd61c7aa012cb3bc9594588a47c7a4df
7
+ data.tar.gz: ddb7f6e43783578a936cffb783965b4da7b48d2b5860517a93b5d9d633c54827d917aaeb9888996a02474fe58d227545ff414517bab3af0231a40db7512d1d9b
data/CHANGELOG.md ADDED
@@ -0,0 +1,148 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.2.1] - 2025-12-17
9
+
10
+ ### Changed
11
+
12
+ - **Shell image updated**: Added bats-core (Bash Automated Testing System) to the shell execution environment for bash script testing support.
13
+
14
+ ## [1.2.0] - 2025-12-11
15
+
16
+ ### Breaking Changes
17
+
18
+ - **Renamed `entrypoint` to `run_command`** in `run()` method.
19
+ - The `entrypoint` parameter (file path) is removed.
20
+ - New `run_command` parameter accepts a full shell command string.
21
+ - Example: `run_command: "python main.py"` instead of `entrypoint: "main.py"`.
22
+
23
+ ### Added
24
+
25
+ - **`run_command` support**: Execute arbitrary shell commands (e.g. `pytest tests/`, `bundle exec rails test`).
26
+
27
+ ### Migration Guide
28
+
29
+ **Before:**
30
+ ```ruby
31
+ client.run(
32
+ entrypoint: "main.py",
33
+ # ...
34
+ )
35
+ ```
36
+
37
+ **After:**
38
+ ```ruby
39
+ client.run(
40
+ run_command: "python main.py",
41
+ # ...
42
+ )
43
+ ```
44
+
45
+ ## [1.1.0] - 2025-12-10
46
+
47
+ ### Added
48
+
49
+ - **`new_dependencies` parameter** in `run()` method: Install dependencies on-the-fly before code execution
50
+ - Python: `["requests==2.31.0", "pytest"]`
51
+ - Ruby: `["rake", "rspec"]`
52
+ - Shell: `["curl", "jq", "git"]` (uses apk)
53
+ - **`packages` field** in `Result`: Returns updated package list when dependencies are installed
54
+ - Only included when `new_dependencies` were provided
55
+ - Hash of package names to versions
56
+
57
+ ### Example
58
+
59
+ ```ruby
60
+ # Install dependencies and run code
61
+ result = client.run(
62
+ container_id: container_id,
63
+ files: [{ path: "main.py", content: "import requests; print(requests.__version__)" }],
64
+ entrypoint: "main.py",
65
+ new_dependencies: ["requests==2.31.0"]
66
+ )
67
+
68
+ puts result.packages # {"pip"=>"23.0.1", "requests"=>"2.31.0", ...}
69
+ ```
70
+
71
+ ## [1.0.0] - 2025-12-10
72
+
73
+ ### Breaking Changes
74
+
75
+ - **New API workflow**: The client now uses a two-step `setup()` + `run()` workflow instead of a single `run()` call
76
+ - `run()` method signature changed:
77
+ - Now requires `container_id` (from `setup()` response) instead of `identifier` and `language`
78
+ - Removed `memory` and `network_allow` parameters (these are now in `setup()`)
79
+ - `Result` object no longer includes `container_id` and `cached` fields (these are now in `SetupResult`)
80
+
81
+ ### Added
82
+
83
+ - **`setup()` method**: Creates or reuses a container and returns environment information
84
+ - Returns `SetupResult` with `container_id`, `cached?`, and `environment_snapshot`
85
+ - `environment_snapshot` includes OS name/version, runtime name/version, and installed packages
86
+ - **`SetupResult` class**: New result object for `setup()` responses
87
+ - **`EnvironmentSnapshot` class**: Contains detailed environment information
88
+ - `os_name`, `os_version`: Operating system details
89
+ - `runtime_name`, `runtime_version`: Runtime details (e.g., Python 3.11.6)
90
+ - `packages`: Hash of installed package names and versions
91
+ - **`NotFoundError`**: New error class for 404 responses (e.g., container not found)
92
+
93
+ ### Changed
94
+
95
+ - Renamed `ExecutionError` to `RunError` to match new terminology
96
+ - Updated error handling to include 404 Not Found responses
97
+ - `run()` method is now focused solely on code execution in pre-setup containers
98
+
99
+ ### Migration Guide
100
+
101
+ **Before (v0.1.0):**
102
+ ```ruby
103
+ result = client.run(
104
+ identifier: "session-123",
105
+ language: "python",
106
+ files: [{ path: "main.py", content: "print('hi')" }],
107
+ entrypoint: "main.py"
108
+ )
109
+
110
+ puts result.container_id # "runbox-session-123-python"
111
+ puts result.cached? # true/false
112
+ ```
113
+
114
+ **After (v1.0.0):**
115
+ ```ruby
116
+ # Step 1: Setup container
117
+ setup = client.setup(
118
+ identifier: "session-123",
119
+ language: "python"
120
+ )
121
+
122
+ puts setup.container_id # "runbox-session-123-python"
123
+ puts setup.cached? # true/false
124
+ puts setup.environment_snapshot.runtime_version # "3.11.6"
125
+
126
+ # Step 2: Run code
127
+ result = client.run(
128
+ container_id: setup.container_id,
129
+ files: [{ path: "main.py", content: "print('hi')" }],
130
+ entrypoint: "main.py"
131
+ )
132
+
133
+ # Note: result no longer has container_id or cached fields
134
+ ```
135
+
136
+ ## [0.1.0] - 2024-12-01
137
+
138
+ ### Added
139
+
140
+ - Initial release
141
+ - `run()` method for executing code in isolated containers
142
+ - Support for Python, Ruby, and Shell languages
143
+ - Container reuse via identifiers
144
+ - Environment variables, timeouts, memory limits, network allowlisting
145
+ - `delete_containers()` method for cleanup
146
+ - `health()` method for health checks
147
+ - Comprehensive error handling
148
+ - Configuration via environment variables or config block
data/README.md ADDED
@@ -0,0 +1,285 @@
1
+ ## README.md
2
+
3
+ # Runbox Ruby Client
4
+
5
+ Official Ruby client for [Runbox](https://github.com/anywaye/runbox) - a fast, secure API for running code in isolated containers.
6
+
7
+ [![Gem Version](https://badge.fury.io/rb/runbox.svg)](https://badge.fury.io/rb/runbox)
8
+ [![CI](https://github.com/anywaye/runbox-rb/actions/workflows/ci.yml/badge.svg)](https://github.com/anywaye/runbox-rb/actions/workflows/ci.yml)
9
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
10
+
11
+ ## Installation
12
+
13
+ Add to your Gemfile:
14
+
15
+ ```ruby
16
+ gem "runbox"
17
+ ```
18
+
19
+ Or install directly:
20
+
21
+ ```bash
22
+ gem install runbox
23
+ ```
24
+
25
+ ## Quick Start
26
+
27
+ ```ruby
28
+ require "runbox"
29
+
30
+ # Configure (or use environment variables)
31
+ Runbox.configure do |config|
32
+ config.url = "http://localhost:8080"
33
+ config.api_key = "your-api-key"
34
+ end
35
+
36
+ # Create a client
37
+ client = Runbox::Client.new
38
+
39
+ # Step 1: Set up a container and get environment info
40
+ setup = client.setup(
41
+ identifier: "my-session",
42
+ language: "python"
43
+ )
44
+
45
+ puts setup.container_id # => "runbox-my-session-python"
46
+ puts setup.environment_snapshot.runtime_version # => "3.11.6"
47
+ puts setup.environment_snapshot.packages["requests"] # => "2.31.0"
48
+
49
+ # Step 2: Run code in the container
50
+ result = client.run(
51
+ container_id: setup.container_id,
52
+ files: [{ path: "main.py", content: "print('Hello!')" }],
53
+ run_command: "python main.py"
54
+ )
55
+
56
+ puts result.stdout # => "Hello!\n"
57
+ puts result.success? # => true
58
+ ```
59
+
60
+ ## Configuration
61
+
62
+ ### Using Environment Variables
63
+
64
+ ```bash
65
+ export RUNBOX_URL=http://localhost:8080
66
+ export RUNBOX_API_KEY=your-api-key
67
+ ```
68
+
69
+ ### Using Configuration Block
70
+
71
+ ```ruby
72
+ Runbox.configure do |config|
73
+ config.url = "http://localhost:8080"
74
+ config.api_key = "your-api-key"
75
+ config.timeout = 60 # HTTP timeout
76
+ end
77
+ ```
78
+
79
+ ### Per-Client Configuration
80
+
81
+ ```ruby
82
+ client = Runbox::Client.new(
83
+ url: "http://localhost:8080",
84
+ api_key: "your-api-key"
85
+ )
86
+ ```
87
+
88
+ ## Usage
89
+
90
+ ### Setting Up a Container
91
+
92
+ ```ruby
93
+ setup = client.setup(
94
+ identifier: "session-123",
95
+ language: "python",
96
+ env: { "API_KEY" => "secret" }, # Optional: set environment variables
97
+ memory: "512m", # Optional: memory limit
98
+ network_allow: ["api.stripe.com"] # Optional: allowed network destinations
99
+ )
100
+
101
+ # Access environment information
102
+ puts setup.container_id # => "runbox-session-123-python"
103
+ puts setup.cached? # => false (true if container was reused)
104
+
105
+ env = setup.environment_snapshot
106
+ puts env.os_name # => "debian"
107
+ puts env.os_version # => "12"
108
+ puts env.runtime_name # => "python"
109
+ puts env.runtime_version # => "3.11.6"
110
+ puts env.packages # => {"pip" => "23.0.1", "requests" => "2.31.0", ...}
111
+ ```
112
+
113
+ ### Running Code
114
+
115
+ ```ruby
116
+ result = client.run(
117
+ container_id: "runbox-session-123-python",
118
+ files: [{ path: "main.py", content: "print('Hello!')" }],
119
+ run_command: "python main.py",
120
+ env: { "DEBUG" => "true" }, # Optional: runtime environment variables
121
+ timeout: 30 # Optional: execution timeout in seconds
122
+ )
123
+
124
+ puts result.success? # => true
125
+ puts result.exit_code # => 0
126
+ puts result.stdout # => "Hello!\n"
127
+ puts result.stderr # => ""
128
+ puts result.execution_time_ms # => 42
129
+ puts result.timeout? # => false
130
+ ```
131
+
132
+ ### Installing Dependencies On-The-Fly
133
+
134
+ Install new dependencies before running code:
135
+
136
+ ```ruby
137
+ # Python example
138
+ result = client.run(
139
+ container_id: container_id,
140
+ files: [{ path: "main.py", content: "import requests; print(requests.__version__)" }],
141
+ run_command: "python main.py",
142
+ new_dependencies: ["requests==2.31.0", "pytest"]
143
+ )
144
+
145
+ puts result.packages # => {"pip"=>"23.0.1", "requests"=>"2.31.0", "pytest"=>"7.4.0", ...}
146
+
147
+ # Ruby example
148
+ result = client.run(
149
+ container_id: container_id,
150
+ files: [{ path: "main.rb", content: "require 'rake'; puts Rake::VERSION" }],
151
+ run_command: "ruby main.rb",
152
+ new_dependencies: ["rake", "rspec"]
153
+ )
154
+
155
+ # Shell example (uses apk)
156
+ result = client.run(
157
+ container_id: container_id,
158
+ files: [{ path: "script.sh", content: "#!/bin/sh\ncurl --version" }],
159
+ run_command: "sh script.sh",
160
+ new_dependencies: ["curl", "jq", "git"]
161
+ )
162
+ ```
163
+
164
+ **Note:** The `packages` field is only included in the result when `new_dependencies` are provided.
165
+
166
+ ### Cleanup Containers
167
+
168
+ ```ruby
169
+ deleted = client.delete_containers("session-123")
170
+ puts deleted # => ["runbox-session-123-python"]
171
+ ```
172
+
173
+ ### Check Health
174
+
175
+ ```ruby
176
+ health = client.health
177
+ puts health[:status] # => "healthy"
178
+ puts health[:version] # => "1.0.0"
179
+ ```
180
+
181
+ ## API Reference
182
+
183
+ ### `client.setup(identifier:, language:, **options)`
184
+
185
+ Set up a container and get environment information.
186
+
187
+ **Parameters:**
188
+ - `identifier` (String, required): Unique identifier for container reuse
189
+ - `language` (String, required): Programming language (`"python"`, `"ruby"`, `"shell"`)
190
+ - `env` (Hash, optional): Environment variables to set
191
+ - `timeout` (Integer, optional): Default timeout in seconds
192
+ - `memory` (String, optional): Memory limit (e.g., `"256m"`, `"1g"`)
193
+ - `network_allow` (Array<String>, optional): Allowed network destinations
194
+
195
+ **Returns:** `SetupResult` with:
196
+ - `container_id`: Container ID to use in `run()` calls
197
+ - `cached?`: Whether container was reused
198
+ - `environment_snapshot`: Environment information (OS, runtime, packages)
199
+
200
+ ### `client.run(container_id:, files:, run_command:, **options)`
201
+
202
+ Run code in a container that was set up via `setup()`.
203
+
204
+ **Parameters:**
205
+ - `container_id` (String, required): Container ID from `setup()` response
206
+ - `files` (Array<Hash>, required): Files to write before running
207
+ - `run_command` (String, required): Command to run
208
+ - `env` (Hash, optional): Runtime environment variables
209
+ - `timeout` (Integer, optional): Execution timeout in seconds
210
+
211
+ **Returns:** `Result` with:
212
+ - `success?`: Whether run succeeded (exit code 0)
213
+ - `exit_code`: Process exit code
214
+ - `stdout`: Standard output
215
+ - `stderr`: Standard error
216
+ - `execution_time_ms`: Execution time in milliseconds
217
+ - `timeout?`: Whether run timed out
218
+
219
+ ## Error Handling
220
+
221
+ ```ruby
222
+ begin
223
+ setup = client.setup(identifier: "test", language: "python")
224
+ result = client.run(
225
+ container_id: setup.container_id,
226
+ files: [{ path: "main.py", content: "print('hi')" }],
227
+ run_command: "python main.py"
228
+ )
229
+ rescue Runbox::AuthenticationError => e
230
+ puts "Invalid API key"
231
+ rescue Runbox::NotFoundError => e
232
+ puts "Container not found (did you call setup first?)"
233
+ rescue Runbox::ValidationError => e
234
+ puts "Invalid request: #{e.message}"
235
+ rescue Runbox::RunError => e
236
+ puts "Run failed: #{e.message}"
237
+ rescue Runbox::ConnectionError => e
238
+ puts "Could not connect to Runbox"
239
+ end
240
+ ```
241
+
242
+ ## Development
243
+
244
+ ### Running Tests
245
+
246
+ ```bash
247
+ bundle install
248
+ bundle exec rake test
249
+ ```
250
+
251
+ ### Running Integration Tests
252
+
253
+ Integration tests require a running Runbox server:
254
+
255
+ ```bash
256
+ # Start Runbox server (in another terminal)
257
+ cd ../runbox
258
+ docker-compose up
259
+
260
+ # Run integration tests
261
+ export RUNBOX_URL=http://localhost:8080
262
+ export RUNBOX_API_KEY=your-api-key
263
+ ruby examples/integration_test.rb
264
+ ```
265
+
266
+ ### Linting
267
+
268
+ ```bash
269
+ bundle exec rubocop
270
+ ```
271
+
272
+ ### CI/CD
273
+
274
+ - **CI**: Runs on every push and PR
275
+ - Tests on Ruby 3.1, 3.2, 3.3
276
+ - Linting with RuboCop
277
+ - Integration tests against live Runbox server
278
+
279
+ - **CD**: Publishes to RubyGems on version tags
280
+ - Create a tag: `git tag v1.0.0 && git push --tags`
281
+ - Requires `RUBYGEMS_API_KEY` secret in GitHub
282
+
283
+ ## License
284
+
285
+ MIT License - see [LICENSE](LICENSE) for details.
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "faraday"
4
+ require "json"
5
+
6
+ module Runbox
7
+ class Client
8
+ attr_reader :url, :api_key
9
+
10
+ def initialize(url: nil, api_key: nil, timeout: nil, open_timeout: nil)
11
+ config = Runbox.configuration
12
+ @url = url || config.url
13
+ @api_key = api_key || config.api_key
14
+ @timeout = timeout || config.timeout
15
+ @open_timeout = open_timeout || config.open_timeout
16
+
17
+ validate_configuration!
18
+ end
19
+
20
+ # Set up a container and get environment information
21
+ #
22
+ # @param identifier [String] Unique identifier for container reuse
23
+ # @param language [String] Programming language (python, ruby, shell)
24
+ # @param env [Hash] Environment variables to set
25
+ # @param timeout [Integer] Default timeout in seconds
26
+ # @param memory [String] Memory limit (e.g., "256m")
27
+ # @param network_allow [Array<String>] Allowed network destinations
28
+ # @return [SetupResult] Setup result with container_id and environment_snapshot
29
+ def setup(identifier:, language:, env: {}, timeout: nil, memory: nil, network_allow: nil)
30
+ payload = {
31
+ identifier: identifier,
32
+ language: language,
33
+ env: env
34
+ }
35
+
36
+ payload[:timeout] = timeout if timeout
37
+ payload[:memory] = memory if memory
38
+ payload[:network_allow] = network_allow if network_allow
39
+
40
+ response = post("/v1/setup", payload)
41
+ SetupResult.new(response)
42
+ end
43
+
44
+ # Run code in a container that was set up via setup()
45
+ #
46
+ # @param container_id [String] Container ID from setup response
47
+ # @param files [Array<Hash>] Files to write before running
48
+ # @param run_command [String] Command to execute
49
+ # @param env [Hash] Runtime environment variables
50
+ # @param timeout [Integer] Timeout in seconds
51
+ # @param new_dependencies [Array<String>] New dependencies to install before running
52
+ # @return [Result] Run result with stdout, stderr, exit_code, and optional packages
53
+ def run(container_id:, files:, run_command:, env: {}, timeout: nil, new_dependencies: nil)
54
+ payload = {
55
+ container_id: container_id,
56
+ files: normalize_files(files),
57
+ run_command: run_command,
58
+ env: env
59
+ }
60
+
61
+ payload[:timeout] = timeout if timeout
62
+ payload[:new_dependencies] = new_dependencies if new_dependencies
63
+
64
+ response = post("/v1/run", payload)
65
+ Result.new(response)
66
+ end
67
+
68
+ def delete_containers(identifier)
69
+ response = delete("/v1/containers/#{identifier}")
70
+ response["deleted"] || []
71
+ end
72
+
73
+ def health
74
+ response = get("/v1/health", auth: false)
75
+ {
76
+ status: response["status"],
77
+ version: response["version"]
78
+ }
79
+ end
80
+
81
+ private
82
+
83
+ def validate_configuration!
84
+ raise ConfigurationError, "URL is required" if @url.nil? || @url.empty?
85
+ raise ConfigurationError, "API key is required" if @api_key.nil? || @api_key.empty?
86
+ end
87
+
88
+ def normalize_files(files)
89
+ files.map do |file|
90
+ if file.is_a?(Hash)
91
+ { path: file[:path] || file["path"], content: file[:content] || file["content"] }
92
+ else
93
+ file
94
+ end
95
+ end
96
+ end
97
+
98
+ def connection
99
+ @connection ||= Faraday.new(url: @url) do |f|
100
+ f.request :json
101
+ f.response :json
102
+ f.options.timeout = @timeout
103
+ f.options.open_timeout = @open_timeout
104
+ end
105
+ end
106
+
107
+ def get(path, auth: true)
108
+ response = connection.get(path) do |req|
109
+ req.headers["Authorization"] = "Bearer #{@api_key}" if auth
110
+ end
111
+ handle_response(response)
112
+ rescue Faraday::ConnectionFailed, Faraday::TimeoutError => e
113
+ raise ConnectionError, "Failed to connect to Runbox: #{e.message}"
114
+ end
115
+
116
+ def post(path, body)
117
+ response = connection.post(path) do |req|
118
+ req.headers["Authorization"] = "Bearer #{@api_key}"
119
+ req.body = body
120
+ end
121
+ handle_response(response)
122
+ rescue Faraday::ConnectionFailed, Faraday::TimeoutError => e
123
+ raise ConnectionError, "Failed to connect to Runbox: #{e.message}"
124
+ end
125
+
126
+ def delete(path)
127
+ response = connection.delete(path) do |req|
128
+ req.headers["Authorization"] = "Bearer #{@api_key}"
129
+ end
130
+ handle_response(response)
131
+ rescue Faraday::ConnectionFailed, Faraday::TimeoutError => e
132
+ raise ConnectionError, "Failed to connect to Runbox: #{e.message}"
133
+ end
134
+
135
+ def handle_response(response)
136
+ case response.status
137
+ when 200..299
138
+ response.body
139
+ when 401
140
+ raise AuthenticationError, "Invalid API key"
141
+ when 404
142
+ body = response.body || {}
143
+ raise NotFoundError, body["detail"] || "Container not found"
144
+ when 400
145
+ body = response.body || {}
146
+ raise ValidationError.new(body["detail"] || "Validation error", details: body)
147
+ when 422
148
+ body = response.body || {}
149
+ raise ValidationError.new(body["detail"] || "Validation error", details: body)
150
+ when 500..599
151
+ body = response.body || {}
152
+ raise RunError, body["detail"] || "Run failed"
153
+ else
154
+ raise Error, "Unexpected response: #{response.status}"
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Runbox
4
+ class Configuration
5
+ attr_accessor :url, :api_key, :timeout, :open_timeout
6
+
7
+ def initialize
8
+ @url = ENV.fetch("RUNBOX_URL", "http://localhost:8080")
9
+ @api_key = ENV.fetch("RUNBOX_API_KEY", nil)
10
+ @timeout = 120 # HTTP timeout (longer than execution timeout)
11
+ @open_timeout = 10
12
+ end
13
+
14
+ def validate!
15
+ raise ConfigurationError, "API key is required" if api_key.nil? || api_key.empty?
16
+ raise ConfigurationError, "URL is required" if url.nil? || url.empty?
17
+ end
18
+ end
19
+ end
20
+
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Runbox
4
+ # Base error class
5
+ class Error < StandardError; end
6
+
7
+ # Configuration errors
8
+ class ConfigurationError < Error; end
9
+
10
+ # Connection errors
11
+ class ConnectionError < Error; end
12
+
13
+ # Authentication errors (401)
14
+ class AuthenticationError < Error; end
15
+
16
+ # Not found errors (404)
17
+ class NotFoundError < Error; end
18
+
19
+ # Validation errors (400)
20
+ class ValidationError < Error
21
+ attr_reader :details
22
+
23
+ def initialize(message, details: nil)
24
+ super(message)
25
+ @details = details
26
+ end
27
+ end
28
+
29
+ # Run errors (500)
30
+ class RunError < Error; end
31
+
32
+ # Timeout errors
33
+ class TimeoutError < Error; end
34
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Runbox
4
+ class Result
5
+ attr_reader :success, :exit_code, :stdout, :stderr,
6
+ :execution_time_ms, :timeout_exceeded, :packages
7
+
8
+ def initialize(data)
9
+ @success = data["success"] || data[:success]
10
+ @exit_code = data["exit_code"] || data[:exit_code]
11
+ @stdout = data["stdout"] || data[:stdout] || ""
12
+ @stderr = data["stderr"] || data[:stderr] || ""
13
+ @execution_time_ms = data["execution_time_ms"] || data[:execution_time_ms]
14
+ @timeout_exceeded = data["timeout_exceeded"] || data[:timeout_exceeded] || false
15
+ @packages = data["packages"] || data[:packages]
16
+ end
17
+
18
+ def success?
19
+ @success == true
20
+ end
21
+
22
+ def failed?
23
+ !success?
24
+ end
25
+
26
+ def timeout?
27
+ @timeout_exceeded == true
28
+ end
29
+
30
+ def to_h
31
+ hash = {
32
+ success: @success,
33
+ exit_code: @exit_code,
34
+ stdout: @stdout,
35
+ stderr: @stderr,
36
+ execution_time_ms: @execution_time_ms,
37
+ timeout_exceeded: @timeout_exceeded
38
+ }
39
+ hash[:packages] = @packages if @packages
40
+ hash
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Runbox
4
+ class SetupResult
5
+ attr_reader :container_id, :cached, :environment_snapshot
6
+
7
+ def initialize(data)
8
+ @container_id = data["container_id"] || data[:container_id]
9
+ @cached = data["cached"] || data[:cached] || false
10
+ @environment_snapshot = EnvironmentSnapshot.new(
11
+ data["environment_snapshot"] || data[:environment_snapshot] || {}
12
+ )
13
+ end
14
+
15
+ def cached?
16
+ @cached == true
17
+ end
18
+
19
+ def to_h
20
+ {
21
+ container_id: @container_id,
22
+ cached: @cached,
23
+ environment_snapshot: @environment_snapshot.to_h
24
+ }
25
+ end
26
+ end
27
+
28
+ class EnvironmentSnapshot
29
+ attr_reader :os_name, :os_version, :runtime_name, :runtime_version, :packages
30
+
31
+ def initialize(data)
32
+ @os_name = data["os_name"] || data[:os_name]
33
+ @os_version = data["os_version"] || data[:os_version]
34
+ @runtime_name = data["runtime_name"] || data[:runtime_name]
35
+ @runtime_version = data["runtime_version"] || data[:runtime_version]
36
+ @packages = data["packages"] || data[:packages] || {}
37
+ end
38
+
39
+ def to_h
40
+ {
41
+ os_name: @os_name,
42
+ os_version: @os_version,
43
+ runtime_name: @runtime_name,
44
+ runtime_version: @runtime_version,
45
+ packages: @packages
46
+ }
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Runbox
4
+ VERSION = "1.2.1"
5
+ end
data/lib/runbox.rb ADDED
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "runbox/version"
4
+ require_relative "runbox/configuration"
5
+ require_relative "runbox/error"
6
+ require_relative "runbox/result"
7
+ require_relative "runbox/setup_result"
8
+ require_relative "runbox/client"
9
+
10
+ module Runbox
11
+ class << self
12
+ attr_writer :configuration
13
+
14
+ def configuration
15
+ @configuration ||= Configuration.new
16
+ end
17
+
18
+ def configure
19
+ yield(configuration)
20
+ end
21
+
22
+ def reset_configuration!
23
+ @configuration = Configuration.new
24
+ end
25
+ end
26
+ end
27
+
metadata ADDED
@@ -0,0 +1,123 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: runbox
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.2.1
5
+ platform: ruby
6
+ authors:
7
+ - Anywaye
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: faraday
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '2.9'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '2.9'
26
+ - !ruby/object:Gem::Dependency
27
+ name: minitest
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '5.21'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '5.21'
40
+ - !ruby/object:Gem::Dependency
41
+ name: rake
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '13.0'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '13.0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: rubocop
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '1.60'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '1.60'
68
+ - !ruby/object:Gem::Dependency
69
+ name: webmock
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '3.19'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '3.19'
82
+ description: Official Ruby client for Runbox, a fast and secure API for running code
83
+ in isolated containers.
84
+ email:
85
+ - hello@anywaye.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - CHANGELOG.md
91
+ - README.md
92
+ - lib/runbox.rb
93
+ - lib/runbox/client.rb
94
+ - lib/runbox/configuration.rb
95
+ - lib/runbox/error.rb
96
+ - lib/runbox/result.rb
97
+ - lib/runbox/setup_result.rb
98
+ - lib/runbox/version.rb
99
+ homepage: https://github.com/anywaye/runbox-rb
100
+ licenses:
101
+ - MIT
102
+ metadata:
103
+ homepage_uri: https://github.com/anywaye/runbox-rb
104
+ source_code_uri: https://github.com/anywaye/runbox-rb
105
+ changelog_uri: https://github.com/anywaye/runbox-rb/blob/main/CHANGELOG.md
106
+ rdoc_options: []
107
+ require_paths:
108
+ - lib
109
+ required_ruby_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: 3.1.0
114
+ required_rubygems_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ requirements: []
120
+ rubygems_version: 4.0.3
121
+ specification_version: 4
122
+ summary: Ruby client for Runbox - secure code execution API
123
+ test_files: []