nightona 0.191.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.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +22 -0
  4. data/.ruby-version +1 -0
  5. data/CODE_OF_CONDUCT.md +132 -0
  6. data/LICENSE +190 -0
  7. data/README.md +184 -0
  8. data/Rakefile +12 -0
  9. data/lib/nightona/code_interpreter.rb +359 -0
  10. data/lib/nightona/common/charts.rb +124 -0
  11. data/lib/nightona/common/code_interpreter.rb +56 -0
  12. data/lib/nightona/common/code_language.rb +14 -0
  13. data/lib/nightona/common/file_system.rb +26 -0
  14. data/lib/nightona/common/git.rb +19 -0
  15. data/lib/nightona/common/image.rb +500 -0
  16. data/lib/nightona/common/nightona.rb +230 -0
  17. data/lib/nightona/common/process.rb +149 -0
  18. data/lib/nightona/common/pty.rb +309 -0
  19. data/lib/nightona/common/resources.rb +39 -0
  20. data/lib/nightona/common/response.rb +83 -0
  21. data/lib/nightona/common/snapshot.rb +124 -0
  22. data/lib/nightona/computer_use.rb +919 -0
  23. data/lib/nightona/config.rb +116 -0
  24. data/lib/nightona/file_system.rb +451 -0
  25. data/lib/nightona/file_transfer.rb +383 -0
  26. data/lib/nightona/git.rb +334 -0
  27. data/lib/nightona/lsp_server.rb +139 -0
  28. data/lib/nightona/nightona.rb +336 -0
  29. data/lib/nightona/object_storage.rb +172 -0
  30. data/lib/nightona/otel.rb +183 -0
  31. data/lib/nightona/process.rb +550 -0
  32. data/lib/nightona/sandbox.rb +751 -0
  33. data/lib/nightona/sdk/version.rb +10 -0
  34. data/lib/nightona/sdk.rb +56 -0
  35. data/lib/nightona/snapshot_service.rb +238 -0
  36. data/lib/nightona/util.rb +80 -0
  37. data/lib/nightona/volume.rb +46 -0
  38. data/lib/nightona/volume_service.rb +61 -0
  39. data/lib/nightona.rb +10 -0
  40. data/project.json +100 -0
  41. data/scripts/generate-docs.rb +402 -0
  42. data/sig/nightona/sdk.rbs +6 -0
  43. metadata +242 -0
@@ -0,0 +1,10 @@
1
+ # Copyright Daytona Platforms Inc.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ # frozen_string_literal: true
5
+
6
+ module Nightona
7
+ module Sdk
8
+ VERSION = '0.191.0'
9
+ end
10
+ end
@@ -0,0 +1,56 @@
1
+ # Copyright Daytona Platforms Inc.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ # frozen_string_literal: true
5
+
6
+ require 'logger'
7
+
8
+ require 'nightona_api_client'
9
+ require 'nightona_toolbox_api_client'
10
+ require 'toml'
11
+ require 'websocket-client-simple'
12
+
13
+ require_relative 'sdk/version'
14
+ require_relative 'config'
15
+ require_relative 'otel'
16
+ require_relative 'common/charts'
17
+ require_relative 'common/code_interpreter'
18
+ require_relative 'common/code_language'
19
+ require_relative 'common/nightona'
20
+ require_relative 'common/file_system'
21
+ require_relative 'common/image'
22
+ require_relative 'common/git'
23
+ require_relative 'common/process'
24
+ require_relative 'common/pty'
25
+ require_relative 'common/resources'
26
+ require_relative 'common/response'
27
+ require_relative 'common/snapshot'
28
+ require_relative 'code_interpreter'
29
+ require_relative 'computer_use'
30
+ require_relative 'nightona'
31
+ require_relative 'file_system'
32
+ require_relative 'git'
33
+ require_relative 'lsp_server'
34
+ require_relative 'object_storage'
35
+ require_relative 'sandbox'
36
+ require_relative 'snapshot_service'
37
+ require_relative 'util'
38
+ require_relative 'volume'
39
+ require_relative 'volume_service'
40
+ require_relative 'process'
41
+
42
+ module Nightona
43
+ module Sdk
44
+ class Error < StandardError; end
45
+ class TimeoutError < Error; end
46
+ class AuthenticationError < Error; end
47
+ class ForbiddenError < Error; end
48
+ class NotFoundError < Error; end
49
+ class ConflictError < Error; end
50
+ class ValidationError < Error; end
51
+ class RateLimitError < Error; end
52
+ class ServerError < Error; end
53
+
54
+ def self.logger = @logger ||= Logger.new($stdout, level: Logger::INFO)
55
+ end
56
+ end
@@ -0,0 +1,238 @@
1
+ # Copyright Daytona Platforms Inc.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ # frozen_string_literal: true
5
+
6
+ require 'uri'
7
+
8
+ module Nightona
9
+ class SnapshotService
10
+ include Instrumentation
11
+
12
+ SNAPSHOTS_FETCH_LIMIT = 200
13
+
14
+ # @param snapshots_api [NightonaApiClient::SnapshotsApi] The snapshots API client
15
+ # @param object_storage_api [NightonaApiClient::ObjectStorageApi] The object storage API client
16
+ # @param default_region_id [String, nil] Default region ID for snapshot creation
17
+ # @param otel_state [Nightona::OtelState, nil]
18
+ def initialize(snapshots_api:, object_storage_api:, default_region_id: nil, otel_state: nil)
19
+ @snapshots_api = snapshots_api
20
+ @object_storage_api = object_storage_api
21
+ @default_region_id = default_region_id
22
+ @otel_state = otel_state
23
+ end
24
+
25
+ # List all Snapshots.
26
+ #
27
+ # @param page [Integer, Nil]
28
+ # @param limit [Integer, Nil]
29
+ # @return [Nightona::PaginatedResource] Paginated list of all Snapshots
30
+ # @raise [Nightona::Sdk::Error]
31
+ #
32
+ # @example
33
+ # nightona = Nightona::Nightona.new
34
+ # response = nightona.snapshot.list(page: 1, limit: 10)
35
+ # snapshots.items.each { |snapshot| puts "#{snapshot.name} (#{snapshot.image_name})" }
36
+ def list(page: nil, limit: nil)
37
+ raise Sdk::Error, 'page must be positive integer' if page && page < 1
38
+
39
+ raise Sdk::Error, 'limit must be positive integer' if limit && limit < 1
40
+
41
+ response = snapshots_api.get_all_snapshots(page:, limit:)
42
+ PaginatedResource.new(
43
+ total: response.total,
44
+ page: response.page,
45
+ total_pages: response.total_pages,
46
+ items: response.items.map { |snapshot_dto| Snapshot.from_dto(snapshot_dto) }
47
+ )
48
+ end
49
+
50
+ # Delete a Snapshot.
51
+ #
52
+ # @param snapshot [Nightona::Snapshot] Snapshot to delete
53
+ # @return [void]
54
+ #
55
+ # @example
56
+ # nightona = Nightona::Nightona.new
57
+ # snapshot = nightona.snapshot.get("demo")
58
+ # nightona.snapshot.delete(snapshot)
59
+ # puts "Snapshot deleted"
60
+ def delete(snapshot) = snapshots_api.remove_snapshot(snapshot.id)
61
+
62
+ # Get a Snapshot by name.
63
+ #
64
+ # @param name [String] Name of the Snapshot to get
65
+ # @return [Nightona::Snapshot] The Snapshot object
66
+ #
67
+ # @example
68
+ # nightona = Nightona::Nightona.new
69
+ # snapshot = nightona.snapshot.get("demo")
70
+ # puts "#{snapshot.name} (#{snapshot.image_name})"
71
+ def get(name) = Snapshot.from_dto(snapshots_api.get_snapshot(name))
72
+
73
+ # Creates and registers a new snapshot from the given Image definition.
74
+ #
75
+ # @param params [Nightona::CreateSnapshotParams] Parameters for snapshot creation
76
+ # @param on_logs [Proc, Nil] Callback proc handling snapshot creation logs
77
+ # @return [Nightona::Snapshot] The created snapshot
78
+ #
79
+ # @example
80
+ # image = Image.debianSlim('3.12').pipInstall('numpy')
81
+ # params = CreateSnapshotParams.new(name: 'my-snapshot', image: image)
82
+ # snapshot = nightona.snapshot.create(params) do |chunk|
83
+ # print chunk
84
+ # end
85
+ def create(params, on_logs: nil) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
86
+ create_snapshot_req = NightonaApiClient::CreateSnapshot.new(name: params.name)
87
+
88
+ if params.image.is_a?(String)
89
+ create_snapshot_req.image_name = params.image
90
+ create_snapshot_req.entrypoint = params.entrypoint
91
+ else
92
+ create_snapshot_req.build_info = NightonaApiClient::CreateBuildInfo.new(
93
+ context_hashes: self.class.process_image_context(object_storage_api, params.image),
94
+ dockerfile_content: if params.entrypoint
95
+ params.image.entrypoint(params.entrypoint).dockerfile
96
+ else
97
+ params.image.dockerfile
98
+ end
99
+ )
100
+ end
101
+
102
+ if params.resources
103
+ create_snapshot_req.cpu = params.resources.cpu
104
+ create_snapshot_req.gpu = params.resources.gpu
105
+ if params.resources.gpu_type
106
+ create_snapshot_req.gpu_type =
107
+ params.resources.gpu_type.is_a?(Array) ? params.resources.gpu_type : [params.resources.gpu_type]
108
+ end
109
+ create_snapshot_req.memory = params.resources.memory
110
+ create_snapshot_req.disk = params.resources.disk
111
+ end
112
+
113
+ create_snapshot_req.region_id = params.region_id || @default_region_id
114
+ create_snapshot_req.sandbox_class = params.sandbox_class
115
+
116
+ snapshot = snapshots_api.create_snapshot(create_snapshot_req)
117
+
118
+ # Always wait for snapshot to be ready, regardless of on_logs
119
+ snapshot = wait_for_snapshot(snapshot, on_logs:)
120
+
121
+ if [NightonaApiClient::SnapshotState::ERROR, NightonaApiClient::SnapshotState::BUILD_FAILED].include?(snapshot.state)
122
+ raise Sdk::Error, "Failed to create snapshot #{snapshot.name}, reason: #{snapshot.error_reason}"
123
+ end
124
+
125
+ Snapshot.from_dto(snapshot)
126
+ end
127
+
128
+ # Activate a snapshot
129
+ #
130
+ # @param snapshot [Nightona::Snapshot] The snapshot instance
131
+ # @return [Nightona::Snapshot]
132
+ def activate(snapshot) = Snapshot.from_dto(snapshots_api.activate_snapshot(snapshot.id))
133
+
134
+ instrument :list, :delete, :get, :create, :activate, component: 'SnapshotService'
135
+
136
+ # Processes the image context by uploading it to object storage
137
+ #
138
+ # @param image [Nightona::Image] The Image instance
139
+ # @return [Array<String>] List of context hashes stored in object storage
140
+ def self.process_image_context(object_storage_api, image) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
141
+ return [] unless image.context_list && !image.context_list.empty?
142
+
143
+ push_access_creds = object_storage_api.get_push_access
144
+
145
+ object_storage = ObjectStorage.new(
146
+ endpoint_url: push_access_creds.storage_url,
147
+ aws_access_key_id: push_access_creds.access_key,
148
+ aws_secret_access_key: push_access_creds.secret,
149
+ aws_session_token: push_access_creds.session_token,
150
+ bucket_name: push_access_creds.bucket
151
+ )
152
+
153
+ image.context_list.map do |context|
154
+ object_storage.upload(
155
+ context.source_path,
156
+ push_access_creds.organization_id,
157
+ context.archive_path
158
+ )
159
+ end
160
+ end
161
+
162
+ private
163
+
164
+ # @return [NightonaApiClient::SnapshotsApi] The snapshots API client
165
+ attr_reader :snapshots_api
166
+
167
+ # @return [NightonaApiClient::ObjectStorageApi, nil] The object storage API client
168
+ attr_reader :object_storage_api
169
+
170
+ # @return [String, nil] Default region ID for snapshot creation
171
+ attr_reader :default_region_id
172
+
173
+ # @return [Nightona::OtelState, nil]
174
+ attr_reader :otel_state
175
+
176
+ # Wait for snapshot to reach a terminal state (ACTIVE, ERROR, or BUILD_FAILED)
177
+ # Optionally streams logs if on_logs callback is provided
178
+ #
179
+ # @param snapshot [NightonaApiClient::SnapshotDto]
180
+ # @param on_logs [Proc, nil]
181
+ # @return [NightonaApiClient::SnapshotDto]
182
+ def wait_for_snapshot(snapshot, on_logs:) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
183
+ terminal_states = [
184
+ NightonaApiClient::SnapshotState::ACTIVE,
185
+ NightonaApiClient::SnapshotState::ERROR,
186
+ NightonaApiClient::SnapshotState::BUILD_FAILED
187
+ ]
188
+
189
+ thread = nil
190
+ previous_state = snapshot.state
191
+
192
+ # Log initial state if callback provided
193
+ on_logs&.call("Creating snapshot #{snapshot.name} (#{snapshot.state})")
194
+
195
+ until terminal_states.include?(snapshot.state)
196
+ Sdk.logger.debug("Waiting for snapshot to be created: #{snapshot.state}")
197
+
198
+ # Start log streaming thread if callback provided and snapshot is building
199
+ if on_logs && thread.nil? && snapshot.state != NightonaApiClient::SnapshotState::PENDING
200
+ thread = start_log_streaming(snapshot, on_logs:)
201
+ end
202
+
203
+ # Log state changes if callback provided
204
+ if on_logs && previous_state != snapshot.state
205
+ if snapshot.state != NightonaApiClient::SnapshotState::PENDING && thread.nil?
206
+ thread = start_log_streaming(snapshot, on_logs:)
207
+ end
208
+ on_logs.call("Creating snapshot #{snapshot.name} (#{snapshot.state})")
209
+ end
210
+
211
+ sleep(1)
212
+ previous_state = snapshot.state
213
+ snapshot = snapshots_api.get_snapshot(snapshot.id)
214
+ end
215
+
216
+ thread&.join
217
+
218
+ if on_logs && snapshot.state == NightonaApiClient::SnapshotState::ACTIVE
219
+ on_logs.call("Created snapshot #{snapshot.name} (#{snapshot.state})")
220
+ end
221
+
222
+ snapshot
223
+ end
224
+
225
+ # @param snapshot [NightonaApiClient::SnapshotDto]
226
+ # @param on_logs [Proc]
227
+ # @return [Thread]
228
+ def start_log_streaming(snapshot, on_logs:)
229
+ # Get build logs URL from API
230
+ build_logs_response = snapshots_api.get_snapshot_build_logs_url(snapshot.id)
231
+ uri = URI.parse("#{build_logs_response.url}?follow=true")
232
+
233
+ headers = {}
234
+ snapshots_api.api_client.update_params_for_auth!(headers, nil, ['bearer'])
235
+ Util.stream_async(uri:, headers:, on_chunk: on_logs)
236
+ end
237
+ end
238
+ end
@@ -0,0 +1,80 @@
1
+ # Copyright Daytona Platforms Inc.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ # frozen_string_literal: true
5
+
6
+ require 'net/http'
7
+
8
+ module Nightona
9
+ module Util
10
+ STDOUT_PREFIX = "\x01\x01\01"
11
+ private_constant :STDOUT_PREFIX
12
+
13
+ STDERR_PREFIX = "\x02\x02\02"
14
+ private_constant :STDERR_PREFIX
15
+
16
+ PREFIX_LEN = STDOUT_PREFIX.bytesize
17
+ private_constant :PREFIX_LEN
18
+
19
+ def self.demux(line)
20
+ out_parts = []
21
+ err_parts = []
22
+ state = nil
23
+ pos = 0
24
+
25
+ while pos < line.bytesize
26
+ si = line.index(STDOUT_PREFIX, pos)
27
+ ei = line.index(STDERR_PREFIX, pos)
28
+
29
+ if si && (ei.nil? || si < ei)
30
+ next_idx = si
31
+ next_state = :stdout
32
+ elsif ei
33
+ next_idx = ei
34
+ next_state = :stderr
35
+ else
36
+ case state
37
+ when :stdout then out_parts << line[pos..]
38
+ when :stderr then err_parts << line[pos..]
39
+ end
40
+ break
41
+ end
42
+
43
+ if pos < next_idx
44
+ chunk = line[pos...next_idx]
45
+ case state
46
+ when :stdout then out_parts << chunk
47
+ when :stderr then err_parts << chunk
48
+ end
49
+ end
50
+
51
+ state = next_state
52
+ pos = next_idx + PREFIX_LEN
53
+ end
54
+
55
+ [out_parts.join, err_parts.join]
56
+ end
57
+
58
+ # @param uri [URI]
59
+ # @param on_chunk [Proc]
60
+ # @param headers [Hash<String, String>]
61
+ # @return [Thread]
62
+ def self.stream_async(uri:, on_chunk:, headers: nil) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
63
+ Sdk.logger.debug("Starting async stream: #{uri}")
64
+ Thread.new do
65
+ Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
66
+ request = Net::HTTP::Get.new(uri, headers)
67
+
68
+ http.request(request) do |response|
69
+ response.read_body do |chunk|
70
+ Sdk.logger.debug("Chunked response received: #{chunk.inspect}")
71
+ on_chunk.call(chunk)
72
+ end
73
+ end
74
+ end
75
+ rescue Net::ReadTimeout => e
76
+ Sdk.logger.debug("Async stream (#{uri}) timeout: #{e.inspect}")
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,46 @@
1
+ # Copyright Daytona Platforms Inc.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ # frozen_string_literal: true
5
+
6
+ module Nightona
7
+ class Volume
8
+ # @return [String]
9
+ attr_reader :id
10
+
11
+ # @return [String]
12
+ attr_reader :name
13
+
14
+ # @return [String]
15
+ attr_reader :organization_id
16
+
17
+ # @return [String]
18
+ attr_reader :state
19
+
20
+ # @return [String]
21
+ attr_reader :created_at
22
+
23
+ # @return [String]
24
+ attr_reader :updated_at
25
+
26
+ # @return [String]
27
+ attr_reader :last_used_at
28
+
29
+ # @return [String, nil]
30
+ attr_reader :error_reason
31
+
32
+ # Initialize volume from DTO
33
+ #
34
+ # @param volume_dto [NightonaApiClient::SandboxVolume]
35
+ def initialize(volume_dto)
36
+ @id = volume_dto.id
37
+ @name = volume_dto.name
38
+ @organization_id = volume_dto.organization_id
39
+ @state = volume_dto.state
40
+ @created_at = volume_dto.created_at
41
+ @updated_at = volume_dto.updated_at
42
+ @last_used_at = volume_dto.last_used_at
43
+ @error_reason = volume_dto.error_reason
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,61 @@
1
+ # Copyright Daytona Platforms Inc.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ # frozen_string_literal: true
5
+
6
+ module Nightona
7
+ class VolumeService
8
+ include Instrumentation
9
+
10
+ # Service for managing Nightona Volumes. Can be used to list, get, create and delete Volumes.
11
+ #
12
+ # @param volumes_api [NightonaApiClient::VolumesApi]
13
+ # @param otel_state [Nightona::OtelState, nil]
14
+ def initialize(volumes_api, otel_state: nil)
15
+ @volumes_api = volumes_api
16
+ @otel_state = otel_state
17
+ end
18
+
19
+ # Create new Volume.
20
+ #
21
+ # @param name [String]
22
+ # @return [Nightona::Volume]
23
+ def create(name) = Volume.new(volumes_api.create_volume(NightonaApiClient::CreateVolume.new(name:)))
24
+
25
+ # Delete a Volume.
26
+ #
27
+ # @param volume [Nightona::Volume]
28
+ # @return [void]
29
+ def delete(volume) = volumes_api.delete_volume(volume.id)
30
+
31
+ # Get a Volume by name.
32
+ #
33
+ # @param name [String]
34
+ # @param create [Boolean]
35
+ # @return [Nightona::Volume]
36
+ def get(name, create: false)
37
+ Volume.new(volumes_api.get_volume_by_name(name))
38
+ rescue NightonaApiClient::ApiError => e
39
+ raise unless create && e.code == 404 && e.message.include?("Volume with name #{name} not found")
40
+
41
+ create(name)
42
+ end
43
+
44
+ # List all Volumes.
45
+ #
46
+ # @return [Array<Nightona::Volume>]
47
+ def list
48
+ volumes_api.list_volumes.map { |volume| Volume.new(volume) }
49
+ end
50
+
51
+ instrument :create, :delete, :get, :list, component: 'VolumeService'
52
+
53
+ private
54
+
55
+ # @return [NightonaApiClient::VolumesApi]
56
+ attr_reader :volumes_api
57
+
58
+ # @return [Nightona::OtelState, nil]
59
+ attr_reader :otel_state
60
+ end
61
+ end
data/lib/nightona.rb ADDED
@@ -0,0 +1,10 @@
1
+ # Copyright Daytona Platforms Inc.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ # frozen_string_literal: true
5
+
6
+ # Main entry point for the Nightona Ruby SDK
7
+ # This file provides a convenient way to require the entire SDK with:
8
+ # require 'nightona'
9
+
10
+ require_relative 'nightona/sdk'
data/project.json ADDED
@@ -0,0 +1,100 @@
1
+ {
2
+ "name": "sdk-ruby",
3
+ "$schema": "../../node_modules/nx/schemas/project-schema.json",
4
+ "projectType": "library",
5
+ "sourceRoot": "libs/sdk-ruby",
6
+ "tags": [],
7
+ "targets": {
8
+ "build": {
9
+ "executor": "nx:run-commands",
10
+ "options": {
11
+ "cwd": "{projectRoot}",
12
+ "commands": ["rm -f *.gem", "bundle install", "gem build nightona.gemspec"],
13
+ "parallel": false
14
+ },
15
+ "dependsOn": [
16
+ {
17
+ "target": "build",
18
+ "projects": ["api-client-ruby", "toolbox-api-client-ruby"]
19
+ },
20
+ "set-version"
21
+ ],
22
+ "outputs": ["{projectRoot}/**/*.gem"]
23
+ },
24
+ "set-version": {
25
+ "executor": "nx:run-commands",
26
+ "options": {
27
+ "cwd": "{projectRoot}",
28
+ "command": "if [ -n \"$RUBYGEMS_PKG_VERSION\" ]; then sed -i \"s/VERSION = '[^']*'/VERSION = '${RUBYGEMS_PKG_VERSION#v}'/\" lib/nightona/sdk/version.rb && echo \"Changed version to $RUBYGEMS_PKG_VERSION\"; else echo \"Using version from version.rb\"; fi"
29
+ },
30
+ "cache": true,
31
+ "inputs": [
32
+ { "env": "RUBYGEMS_PKG_VERSION" },
33
+ { "env": "VERSION" }
34
+ ],
35
+ "outputs": ["{projectRoot}/lib/nightona/sdk/version.rb"]
36
+ },
37
+ "lint": {
38
+ "executor": "nx:run-commands",
39
+ "options": {
40
+ "cwd": "{projectRoot}",
41
+ "command": "bundle exec rubocop"
42
+ }
43
+ },
44
+ "test": {
45
+ "executor": "nx:run-commands",
46
+ "cache": true,
47
+ "inputs": [
48
+ "default",
49
+ "{workspaceRoot}/libs/api-client-ruby/**/*",
50
+ "{workspaceRoot}/libs/toolbox-api-client-ruby/**/*"
51
+ ],
52
+ "outputs": [],
53
+ "options": {
54
+ "cwd": "{projectRoot}",
55
+ "command": "bundle exec rspec --format documentation --tag '~e2e'"
56
+ }
57
+ },
58
+ "test:e2e": {
59
+ "executor": "nx:run-commands",
60
+ "options": {
61
+ "cwd": "{projectRoot}",
62
+ "command": "bundle exec rspec spec/e2e_spec.rb --format documentation"
63
+ }
64
+ },
65
+ "format": {
66
+ "executor": "nx:run-commands",
67
+ "options": {
68
+ "cwd": "{projectRoot}",
69
+ "commands": ["bundle exec rubocop -a --fail-level=E"],
70
+ "parallel": false
71
+ }
72
+ },
73
+ "docs": {
74
+ "executor": "nx:run-commands",
75
+ "outputs": ["{workspaceRoot}/apps/docs/src/content/docs/en/ruby-sdk/**/*"],
76
+ "options": {
77
+ "cwd": "{projectRoot}",
78
+ "command": "bash -O extglob -c 'rm -rf ../../apps/docs/src/content/docs/en/ruby-sdk/!(index.mdx)' && bundle exec ruby scripts/generate-docs.rb"
79
+ }
80
+ },
81
+ "publish": {
82
+ "executor": "nx:run-commands",
83
+ "options": {
84
+ "cwd": "{projectRoot}",
85
+ "commands": [
86
+ "mkdir -p ~/.gem && echo \":rubygems: $RUBYGEMS_API_KEY\" > ~/.gem/credentials && chmod 0600 ~/.gem/credentials",
87
+ "gem push nightona-*.gem --key rubygems --host https://rubygems.org"
88
+ ],
89
+ "parallel": false
90
+ },
91
+ "dependsOn": [
92
+ {
93
+ "target": "publish",
94
+ "projects": ["api-client-ruby", "toolbox-api-client-ruby"]
95
+ },
96
+ "build"
97
+ ]
98
+ }
99
+ }
100
+ }