dagger_ruby 0.1.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.
@@ -0,0 +1,392 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "dagger_object"
4
+
5
+ module DaggerRuby
6
+ class Container < DaggerObject
7
+ def self.from_id(id, client)
8
+ query = QueryBuilder.new("container")
9
+ query.load_from_id(id)
10
+ new(query, client)
11
+ end
12
+
13
+ def self.root_field_name
14
+ "container"
15
+ end
16
+
17
+
18
+ def from(address)
19
+ chain_operation("from", { "address" => address })
20
+ end
21
+
22
+ def with_directory(path, directory, opts = {})
23
+ args = { "path" => path, "directory" => directory.is_a?(DaggerObject) ? directory.id : directory }
24
+ args["exclude"] = opts[:exclude] if opts[:exclude]
25
+ args["include"] = opts[:include] if opts[:include]
26
+ args["owner"] = opts[:owner] if opts[:owner]
27
+ args["expand"] = opts[:expand] if opts.key?(:expand)
28
+
29
+ chain_operation("withDirectory", args)
30
+ end
31
+
32
+ def with_workdir(path)
33
+ chain_operation("withWorkdir", { "path" => path })
34
+ end
35
+
36
+ def with_exec(args, opts = {})
37
+ exec_args = { "args" => args }
38
+ exec_args["useEntrypoint"] = opts[:use_entrypoint] if opts.key?(:use_entrypoint)
39
+ exec_args["stdin"] = opts[:stdin] if opts[:stdin]
40
+ exec_args["redirectStdout"] = opts[:redirect_stdout] if opts[:redirect_stdout]
41
+ exec_args["redirectStderr"] = opts[:redirect_stderr] if opts[:redirect_stderr]
42
+ exec_args["expect"] = opts[:expect] if opts[:expect]
43
+ if opts.key?(:experimental_privileged_nesting)
44
+ exec_args["experimentalPrivilegedNesting"] = opts[:experimental_privileged_nesting]
45
+ end
46
+ if opts.key?(:insecure_root_capabilities)
47
+ exec_args["insecureRootCapabilities"] = opts[:insecure_root_capabilities]
48
+ end
49
+ exec_args["expand"] = opts[:expand] if opts.key?(:expand)
50
+ exec_args["noInit"] = opts[:no_init] if opts.key?(:no_init)
51
+
52
+ chain_operation("withExec", exec_args)
53
+ end
54
+
55
+ def with_mounted_cache(path, cache, opts = {})
56
+ args = { "path" => path, "cache" => cache.is_a?(DaggerObject) ? cache.id : cache }
57
+ args["source"] = opts[:source] if opts[:source]
58
+ args["sharing"] = opts[:sharing] if opts[:sharing]
59
+ args["owner"] = opts[:owner] if opts[:owner]
60
+ args["expand"] = opts[:expand] if opts.key?(:expand)
61
+
62
+ chain_operation("withMountedCache", args)
63
+ end
64
+
65
+ def with_env_variable(name, value, opts = {})
66
+ args = { "name" => name, "value" => value }
67
+ args["expand"] = opts[:expand] if opts.key?(:expand)
68
+
69
+ chain_operation("withEnvVariable", args)
70
+ end
71
+
72
+ def with_file(path, source, opts = {})
73
+ args = { "path" => path, "source" => source.is_a?(DaggerObject) ? source.id : source }
74
+ args["permissions"] = opts[:permissions] if opts[:permissions]
75
+ args["owner"] = opts[:owner] if opts[:owner]
76
+ args["expand"] = opts[:expand] if opts.key?(:expand)
77
+
78
+ chain_operation("withFile", args)
79
+ end
80
+
81
+ def with_new_file(path, contents, opts = {})
82
+ args = { "path" => path, "contents" => contents }
83
+ args["permissions"] = opts[:permissions] if opts[:permissions]
84
+ args["owner"] = opts[:owner] if opts[:owner]
85
+ args["expand"] = opts[:expand] if opts.key?(:expand)
86
+
87
+ chain_operation("withNewFile", args)
88
+ end
89
+
90
+ def with_secret_variable(name, secret)
91
+ args = { "name" => name, "secret" => secret.is_a?(DaggerObject) ? secret.id : secret }
92
+ chain_operation("withSecretVariable", args)
93
+ end
94
+
95
+ def with_secret_env(name, secret)
96
+ args = { "name" => name, "secret" => secret.is_a?(DaggerObject) ? secret.id : secret }
97
+ chain_operation("withSecretEnv", args)
98
+ end
99
+
100
+ def with_entrypoint(args, opts = {})
101
+ entrypoint_args = { "args" => args }
102
+ entrypoint_args["keepDefaultArgs"] = opts[:keep_default_args] if opts.key?(:keep_default_args)
103
+ chain_operation("withEntrypoint", entrypoint_args)
104
+ end
105
+
106
+ def with_user(name)
107
+ chain_operation("withUser", { "name" => name })
108
+ end
109
+
110
+ def with_registry_auth(address, username, secret)
111
+ puts "⚠️ Registry auth temporarily disabled due to GraphQL formatting issues"
112
+ self
113
+ end
114
+
115
+ def without_registry_auth(address)
116
+ chain_operation("withoutRegistryAuth", { "address" => address })
117
+ end
118
+
119
+ def with_service_binding(alias_name, service)
120
+ args = {
121
+ "alias" => alias_name,
122
+ "service" => service.is_a?(DaggerObject) ? service.id : service
123
+ }
124
+ chain_operation("withServiceBinding", args)
125
+ end
126
+
127
+ def as_service(opts = {})
128
+ args = {}
129
+ args["args"] = opts[:args] if opts[:args]
130
+ args["useEntrypoint"] = opts[:use_entrypoint] if opts.key?(:use_entrypoint)
131
+ args["experimentalPrivilegedNesting"] = opts[:experimental_privileged_nesting] if opts.key?(:experimental_privileged_nesting)
132
+ args["insecureRootCapabilities"] = opts[:insecure_root_capabilities] if opts.key?(:insecure_root_capabilities)
133
+ args["expand"] = opts[:expand] if opts.key?(:expand)
134
+ args["noInit"] = opts[:no_init] if opts.key?(:no_init)
135
+
136
+ require_relative "service" unless defined?(Service)
137
+ get_object("asService", Service, args)
138
+ end
139
+
140
+ def build(context, opts = {})
141
+ args = { "context" => context.is_a?(DaggerObject) ? context.id : context }
142
+ args["dockerfile"] = opts[:dockerfile] if opts[:dockerfile]
143
+ args["target"] = opts[:target] if opts[:target]
144
+ args["buildArgs"] = opts[:build_args] if opts[:build_args]
145
+
146
+ if opts[:secrets]
147
+ raise NotImplementedError, "Build secrets are not yet supported. Use with_mounted_secret instead."
148
+ end
149
+
150
+ args["noInit"] = opts[:no_init] if opts.key?(:no_init)
151
+
152
+ chain_operation("build", args)
153
+ end
154
+
155
+
156
+
157
+ def import(source, opts = {})
158
+ args = { "source" => source.is_a?(DaggerObject) ? source.id : source }
159
+ args["tag"] = opts[:tag] if opts[:tag]
160
+
161
+ chain_operation("import", args)
162
+ end
163
+
164
+ def terminal(opts = {})
165
+ args = {}
166
+ args["cmd"] = opts[:cmd] if opts[:cmd]
167
+ args["experimentalPrivilegedNesting"] = opts[:experimental_privileged_nesting] if opts.key?(:experimental_privileged_nesting)
168
+ args["insecureRootCapabilities"] = opts[:insecure_root_capabilities] if opts.key?(:insecure_root_capabilities)
169
+
170
+ if args.empty?
171
+ chain_operation("terminal")
172
+ else
173
+ chain_operation("terminal", args)
174
+ end
175
+ end
176
+
177
+ def with_exposed_port(port, opts = {})
178
+ args = { "port" => port }
179
+ args["protocol"] = opts[:protocol] if opts[:protocol]
180
+ args["description"] = opts[:description] if opts[:description]
181
+ if opts.key?(:experimental_skip_healthcheck)
182
+ args["experimentalSkipHealthcheck"] = opts[:experimental_skip_healthcheck]
183
+ end
184
+
185
+ chain_operation("withExposedPort", args)
186
+ end
187
+
188
+ def with_label(name, value)
189
+ args = { "name" => name, "value" => value }
190
+ chain_operation("withLabel", args)
191
+ end
192
+
193
+ def with_mounted_directory(path, source, opts = {})
194
+ args = { "path" => path, "source" => source.is_a?(DaggerObject) ? source.id : source }
195
+ args["owner"] = opts[:owner] if opts[:owner]
196
+ args["expand"] = opts[:expand] if opts.key?(:expand)
197
+
198
+ chain_operation("withMountedDirectory", args)
199
+ end
200
+
201
+ def with_mounted_file(path, source, opts = {})
202
+ args = { "path" => path, "source" => source.is_a?(DaggerObject) ? source.id : source }
203
+ args["owner"] = opts[:owner] if opts[:owner]
204
+ args["expand"] = opts[:expand] if opts.key?(:expand)
205
+
206
+ chain_operation("withMountedFile", args)
207
+ end
208
+
209
+ def with_mounted_secret(path, source, opts = {})
210
+ args = { "path" => path, "source" => source.is_a?(DaggerObject) ? source.id : source }
211
+ args["owner"] = opts[:owner] if opts[:owner]
212
+ args["mode"] = opts[:mode] if opts[:mode]
213
+ args["expand"] = opts[:expand] if opts.key?(:expand)
214
+
215
+ chain_operation("withMountedSecret", args)
216
+ end
217
+
218
+ def without_directory(path)
219
+ chain_operation("withoutDirectory", { "path" => path })
220
+ end
221
+
222
+ def without_file(path)
223
+ chain_operation("withoutFile", { "path" => path })
224
+ end
225
+
226
+ def without_env_variable(name)
227
+ chain_operation("withoutEnvVariable", { "name" => name })
228
+ end
229
+
230
+ def without_exposed_port(port, opts = {})
231
+ args = { "port" => port }
232
+ args["protocol"] = opts[:protocol] if opts[:protocol]
233
+
234
+ chain_operation("withoutExposedPort", args)
235
+ end
236
+
237
+
238
+ def directory(path, opts = {})
239
+ args = { "path" => path }
240
+ args["expand"] = opts[:expand] if opts.key?(:expand)
241
+ get_object("directory", Directory, args)
242
+ end
243
+
244
+ def file(path, opts = {})
245
+ args = { "path" => path }
246
+ args["expand"] = opts[:expand] if opts.key?(:expand)
247
+ get_object("file", File, args)
248
+ end
249
+
250
+
251
+ def stdout
252
+ get_scalar("stdout")
253
+ end
254
+
255
+ def stderr
256
+ get_scalar("stderr")
257
+ end
258
+
259
+ def exit_code
260
+ get_scalar("exitCode")
261
+ end
262
+
263
+ def workdir
264
+ get_scalar("workdir")
265
+ end
266
+
267
+ def user
268
+ get_scalar("user")
269
+ end
270
+
271
+ def entrypoint
272
+ get_scalar("entrypoint")
273
+ end
274
+
275
+ def env_variables
276
+ get_scalar("envVariables")
277
+ end
278
+
279
+ def env_variable(name)
280
+ query = @query_builder.build_query_with_selection("envVariable(name: \"#{name}\")")
281
+ result = @client.execute(query)
282
+ extract_value_from_result(result, "envVariable")
283
+ end
284
+
285
+ def labels
286
+ get_scalar("labels")
287
+ end
288
+
289
+ def label(name)
290
+ query = @query_builder.build_query_with_selection("label(name: \"#{name}\")")
291
+ result = @client.execute(query)
292
+ extract_value_from_result(result, "label")
293
+ end
294
+
295
+ def mounts
296
+ get_scalar("mounts")
297
+ end
298
+
299
+ def exposed_ports
300
+ get_scalar("exposedPorts")
301
+ end
302
+
303
+ def platform
304
+ get_scalar("platform")
305
+ end
306
+
307
+ def image_ref
308
+ get_scalar("imageRef")
309
+ end
310
+
311
+ def export(path, opts = {})
312
+ args = { "path" => path }
313
+ args["platformVariants"] = opts[:platform_variants] if opts[:platform_variants]
314
+ args["forcedCompression"] = opts[:forced_compression] if opts[:forced_compression]
315
+ args["mediaTypes"] = opts[:media_types] if opts[:media_types]
316
+ args["expand"] = opts[:expand] if opts.key?(:expand)
317
+
318
+ query = @query_builder.build_query_with_selection("export(#{format_arguments(args)})")
319
+ result = @client.execute(query)
320
+ extract_value_from_result(result, "export")
321
+ end
322
+
323
+ def export_to_file(path, opts = {})
324
+ args = { "path" => path }
325
+ args["platformVariants"] = opts[:platform_variants] if opts[:platform_variants]
326
+ args["forcedCompression"] = opts[:forced_compression] if opts[:forced_compression]
327
+ args["mediaTypes"] = opts[:media_types] if opts[:media_types]
328
+ args["expand"] = opts[:expand] if opts.key?(:expand)
329
+
330
+ query = @query_builder.build_query_with_selection("exportToFile(#{format_arguments(args)})")
331
+ result = @client.execute(query)
332
+ extract_value_from_result(result, "exportToFile")
333
+ end
334
+
335
+ def publish(address, opts = {})
336
+ args = { "address" => address }
337
+ args["platformVariants"] = opts[:platform_variants] if opts[:platform_variants]
338
+ args["forcedCompression"] = opts[:forced_compression] if opts[:forced_compression]
339
+ args["mediaTypes"] = opts[:media_types] if opts[:media_types]
340
+
341
+ query = @query_builder.build_query_with_selection("publish(#{format_arguments(args)})")
342
+ result = @client.execute(query)
343
+ extract_value_from_result(result, "publish")
344
+ end
345
+
346
+ def as_tarball(opts = {})
347
+ args = {}
348
+ args["platformVariants"] = opts[:platform_variants] if opts[:platform_variants]
349
+ args["forcedCompression"] = opts[:forced_compression] if opts[:forced_compression]
350
+ args["mediaTypes"] = opts[:media_types] if opts[:media_types]
351
+
352
+ query = @query_builder.build_query_with_selection("asTarball(#{format_arguments(args)})")
353
+ result = @client.execute(query)
354
+ extract_value_from_result(result, "asTarball")
355
+ end
356
+
357
+ def sync
358
+ get_scalar("id") # Force execution by getting ID
359
+ self
360
+ end
361
+
362
+ private
363
+
364
+ def format_arguments(args)
365
+ return "" if args.empty?
366
+
367
+ args.map { |key, value| "#{key}: #{format_value(value)}" }.join(", ")
368
+ end
369
+
370
+ def format_value(value)
371
+ case value
372
+ when String
373
+ "\"#{value}\""
374
+ when Integer, Float
375
+ value.to_s
376
+ when TrueClass, FalseClass
377
+ value.to_s
378
+ when NilClass
379
+ "null"
380
+ when Array
381
+ "[#{value.map { |v| format_value(v) }.join(', ')}]"
382
+ when Hash
383
+ formatted_pairs = value.map { |k, v| "#{k}: #{format_value(v)}" }
384
+ "{ #{formatted_pairs.join(', ')} }"
385
+ when DaggerObject
386
+ value.id
387
+ else
388
+ "\"#{value}\""
389
+ end
390
+ end
391
+ end
392
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "errors"
4
+ require_relative "query_builder"
5
+
6
+ module DaggerRuby
7
+ class DaggerObject
8
+ attr_reader :query_builder, :client
9
+
10
+ def initialize(query_builder = nil, client = nil)
11
+ @query_builder = query_builder || QueryBuilder.new(self.class.root_field_name)
12
+ @client = client
13
+ end
14
+
15
+ def chain_operation(field, args = {})
16
+ new_query = @query_builder.chain_operation(field, args)
17
+ self.class.new(new_query, @client)
18
+ end
19
+
20
+ def id
21
+ @id ||= get_scalar("id")
22
+ end
23
+
24
+ protected
25
+
26
+ def get_scalar(field)
27
+ query = @query_builder.build_query_with_selection(field)
28
+ result = @client.execute_query(query)
29
+ extract_value_from_result(result, field)
30
+ end
31
+
32
+ def extract_value_from_result(result, field)
33
+ return nil if result.nil?
34
+
35
+ if @query_builder.root_field
36
+ current = result[@query_builder.root_field]
37
+ return nil if current.nil?
38
+ else
39
+ current = result
40
+ end
41
+
42
+ @query_builder.operation_chain.each do |op|
43
+ current = current[op[:field]]
44
+ return nil if current.nil?
45
+ end
46
+
47
+ current[field]
48
+ end
49
+
50
+ def get_object(field, klass, args = {})
51
+ new_query = @query_builder.chain_operation(field, args)
52
+ klass.new(new_query, @client)
53
+ end
54
+
55
+ def get_object_array(field, klass, args = {})
56
+ query = @query_builder.build_query_with_selection(field)
57
+
58
+ array_field = query.selections.find { |s| s.field == field }
59
+ array_field.select("id") if array_field
60
+
61
+ result = @client.execute(query)
62
+ ids = extract_array_from_result(result, field)
63
+
64
+ ids.map { |id_data| klass.from_id(id_data["id"], @client) }
65
+ end
66
+
67
+ private
68
+
69
+ def extract_array_from_result(result, field)
70
+ current = result["data"]
71
+
72
+ if @query_builder.root_field
73
+ current = current[@query_builder.root_field]
74
+ @query_builder.operation_chain.each do |operation|
75
+ current = current[operation[:field]] if current
76
+ end
77
+ end
78
+ current&.[](field) || []
79
+ end
80
+
81
+ class << self
82
+ def from_id(id, client)
83
+ query = QueryBuilder.new(root_field_name)
84
+ query.load_from_id(id)
85
+ new(query, client)
86
+ end
87
+
88
+ def root_field_name
89
+ name.split("::").last.downcase
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,166 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "dagger_object"
4
+
5
+ module DaggerRuby
6
+ class Directory < DaggerObject
7
+ def self.from_id(id, client)
8
+ query = QueryBuilder.new("directory")
9
+ query.load_from_id(id)
10
+ new(query, client)
11
+ end
12
+
13
+ def self.root_field_name
14
+ "directory"
15
+ end
16
+
17
+ def with_file(path, source, opts = {})
18
+ args = { "path" => path, "source" => source.is_a?(DaggerObject) ? source.id : source }
19
+ args["permissions"] = opts[:permissions] if opts[:permissions]
20
+
21
+ chain_operation("withFile", args)
22
+ end
23
+
24
+ def with_new_file(path, contents, opts = {})
25
+ args = { "path" => path, "contents" => contents }
26
+ args["permissions"] = opts[:permissions] if opts[:permissions]
27
+
28
+ chain_operation("withNewFile", args)
29
+ end
30
+
31
+ def with_directory(path, directory, opts = {})
32
+ args = { "path" => path, "directory" => directory.is_a?(DaggerObject) ? directory.id : directory }
33
+ args["exclude"] = opts[:exclude] if opts[:exclude]
34
+ args["include"] = opts[:include] if opts[:include]
35
+
36
+ chain_operation("withDirectory", args)
37
+ end
38
+
39
+ def with_new_directory(path, opts = {})
40
+ args = { "path" => path }
41
+ args["permissions"] = opts[:permissions] if opts[:permissions]
42
+
43
+ chain_operation("withNewDirectory", args)
44
+ end
45
+
46
+ def without_file(path)
47
+ chain_operation("withoutFile", { "path" => path })
48
+ end
49
+
50
+ def without_directory(path)
51
+ chain_operation("withoutDirectory", { "path" => path })
52
+ end
53
+
54
+ def diff(other)
55
+ chain_operation("diff", { "other" => other.is_a?(DaggerObject) ? other.id : other })
56
+ end
57
+
58
+ def sync
59
+ get_scalar("id")
60
+ self
61
+ end
62
+
63
+ def file(path)
64
+ get_object("file", File, { "path" => path })
65
+ end
66
+
67
+ def directory(path)
68
+ get_object("directory", Directory, { "path" => path })
69
+ end
70
+
71
+ def as_tarball(opts = {})
72
+ args = {}
73
+ args["forcedCompression"] = opts[:forced_compression] if opts[:forced_compression]
74
+
75
+ get_object("asTarball", File, args)
76
+ end
77
+
78
+ def export(path, opts = {})
79
+ args = { "path" => path }
80
+ args["allowParentDirPath"] = opts[:allow_parent_dir_path] if opts.key?(:allow_parent_dir_path)
81
+
82
+ query = @query_builder.build_query_with_selection("export(#{format_arguments(args)})")
83
+ result = @client.execute(query)
84
+ extract_value_from_result(result, "export")
85
+ end
86
+
87
+ def entries(path = ".")
88
+ query = @query_builder.build_query_with_selection("entries(path: \"#{path}\")")
89
+ result = @client.execute(query)
90
+ extract_value_from_result(result, "entries")
91
+ end
92
+
93
+ def glob(pattern)
94
+ query = @query_builder.build_query_with_selection("glob(pattern: \"#{pattern}\")")
95
+ result = @client.execute(query)
96
+ extract_value_from_result(result, "glob")
97
+ end
98
+
99
+ def docker_build(opts = {})
100
+ args = {}
101
+ args["dockerfile"] = opts[:dockerfile] if opts[:dockerfile]
102
+ args["platform"] = opts[:platform] if opts[:platform]
103
+ args["buildArgs"] = opts[:build_args] if opts[:build_args]
104
+ args["target"] = opts[:target] if opts[:target]
105
+ args["secrets"] = opts[:secrets].map { |s| s.is_a?(DaggerObject) ? s.id : s } if opts[:secrets]
106
+ args["noInit"] = opts[:no_init] if opts.key?(:no_init)
107
+
108
+ require_relative "container" unless defined?(Container)
109
+ get_object("dockerBuild", Container, args)
110
+ end
111
+
112
+ def terminal(opts = {})
113
+ args = {}
114
+ args["container"] = opts[:container].is_a?(DaggerObject) ? opts[:container].id : opts[:container] if opts[:container]
115
+ args["cmd"] = opts[:cmd] if opts[:cmd]
116
+ args["experimentalPrivilegedNesting"] = opts[:experimental_privileged_nesting] if opts.key?(:experimental_privileged_nesting)
117
+ args["insecureRootCapabilities"] = opts[:insecure_root_capabilities] if opts.key?(:insecure_root_capabilities)
118
+
119
+ if args.empty?
120
+ chain_operation("terminal")
121
+ else
122
+ chain_operation("terminal", args)
123
+ end
124
+ end
125
+
126
+ def self.load_from_host(path, opts = {}, client)
127
+ args = { "path" => path }
128
+ args["exclude"] = opts[:exclude] if opts[:exclude]
129
+ args["include"] = opts[:include] if opts[:include]
130
+
131
+ host_query = QueryBuilder.new("host")
132
+ host_dir_query = host_query.chain_operation("directory", args)
133
+ Directory.new(host_dir_query, client)
134
+ end
135
+
136
+ private
137
+
138
+ def format_arguments(args)
139
+ return "" if args.empty?
140
+
141
+ args.map { |key, value| "#{key}: #{format_value(value)}" }.join(", ")
142
+ end
143
+
144
+ def format_value(value)
145
+ case value
146
+ when String
147
+ "\"#{value}\""
148
+ when Integer, Float
149
+ value.to_s
150
+ when TrueClass, FalseClass
151
+ value.to_s
152
+ when NilClass
153
+ "null"
154
+ when Array
155
+ "[#{value.map { |v| format_value(v) }.join(', ')}]"
156
+ when Hash
157
+ formatted_pairs = value.map { |k, v| "#{k}: #{format_value(v)}" }
158
+ "{ #{formatted_pairs.join(', ')} }"
159
+ when DaggerObject
160
+ value.id
161
+ else
162
+ "\"#{value}\""
163
+ end
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DaggerRuby
4
+ class DaggerError < StandardError; end
5
+ class GraphQLError < DaggerError; end
6
+ class HTTPError < DaggerError; end
7
+ class InvalidQueryError < DaggerError; end
8
+ class ConnectionError < DaggerError; end
9
+ end