From f291b9605760add258fc3e596a664800663d9e7f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 27 Feb 2026 13:27:02 +0000 Subject: [PATCH 1/4] fix: properly mock time in ruby ci tests --- test/docker_engine_ruby/client_test.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/docker_engine_ruby/client_test.rb b/test/docker_engine_ruby/client_test.rb index fbab27c..6d61342 100644 --- a/test/docker_engine_ruby/client_test.rb +++ b/test/docker_engine_ruby/client_test.rb @@ -161,19 +161,21 @@ def test_client_retry_after_seconds end def test_client_retry_after_date + time_now = Time.now + stub_request(:get, "http://localhost/containers/json").to_return_json( status: 500, - headers: {"retry-after" => (Time.now + 10).httpdate}, + headers: {"retry-after" => (time_now + 10).httpdate}, body: {} ) docker = DockerEngineRuby::Client.new(base_url: "http://localhost", max_retries: 1) + Thread.current.thread_variable_set(:time_now, time_now) assert_raises(DockerEngineRuby::Errors::InternalServerError) do - Thread.current.thread_variable_set(:time_now, Time.now) docker.containers.list - Thread.current.thread_variable_set(:time_now, nil) end + Thread.current.thread_variable_set(:time_now, nil) assert_requested(:any, /./, times: 2) assert_in_delta(10, Thread.current.thread_variable_get(:mock_sleep).last, 1.0) From ab550815719723e748965fb23c2547aee2b1de91 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 4 Mar 2026 07:43:57 +0000 Subject: [PATCH 2/4] chore(internal): codegen related update --- lib/docker_engine_ruby/internal/util.rb | 31 +++++++++++ lib/docker_engine_ruby/resources/configs.rb | 6 ++- .../resources/containers.rb | 51 ++++++++++++------- lib/docker_engine_ruby/resources/exec.rb | 3 +- lib/docker_engine_ruby/resources/images.rb | 49 +++++++++++------- lib/docker_engine_ruby/resources/networks.rb | 9 ++-- lib/docker_engine_ruby/resources/nodes.rb | 9 ++-- lib/docker_engine_ruby/resources/plugins.rb | 24 ++++++--- lib/docker_engine_ruby/resources/secrets.rb | 6 ++- lib/docker_engine_ruby/resources/services.rb | 12 +++-- lib/docker_engine_ruby/resources/swarm.rb | 10 ++-- lib/docker_engine_ruby/resources/system.rb | 6 ++- lib/docker_engine_ruby/resources/tasks.rb | 6 ++- lib/docker_engine_ruby/resources/volumes.rb | 14 +++-- rbi/docker_engine_ruby/internal/util.rbi | 20 ++++++++ sig/docker_engine_ruby/internal/util.rbs | 10 ++++ 16 files changed, 194 insertions(+), 72 deletions(-) diff --git a/lib/docker_engine_ruby/internal/util.rb b/lib/docker_engine_ruby/internal/util.rb index e11605f..bee8312 100644 --- a/lib/docker_engine_ruby/internal/util.rb +++ b/lib/docker_engine_ruby/internal/util.rb @@ -490,6 +490,37 @@ def writable_enum(&blk) JSONL_CONTENT = %r{^application/(:?x-(?:n|l)djson)|(:?(?:x-)?jsonl)} class << self + # @api private + # + # @param query [Hash{Symbol=>Object}] + # + # @return [Hash{Symbol=>Object}] + def encode_query_params(query) + out = {} + query.each { write_query_param_element!(out, _1, _2) } + out + end + + # @api private + # + # @param collection [Hash{Symbol=>Object}] + # @param key [String] + # @param element [Object] + # + # @return [nil] + private def write_query_param_element!(collection, key, element) + case element + in Hash + element.each do |name, value| + write_query_param_element!(collection, "#{key}[#{name}]", value) + end + in Array + collection[key] = element.map(&:to_s).join(",") + else + collection[key] = element.to_s + end + end + # @api private # # @param y [Enumerator::Yielder] diff --git a/lib/docker_engine_ruby/resources/configs.rb b/lib/docker_engine_ruby/resources/configs.rb index 869d77a..3ec9fd6 100644 --- a/lib/docker_engine_ruby/resources/configs.rb +++ b/lib/docker_engine_ruby/resources/configs.rb @@ -41,10 +41,11 @@ def create(params) # @see DockerEngineRuby::Models::ConfigUpdateParams def update(id, params) parsed, options = DockerEngineRuby::ConfigUpdateParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed.except(:spec)) @client.request( method: :post, path: ["configs/%1$s/update", id], - query: parsed.except(:spec), + query: query, body: parsed[:spec], model: NilClass, options: options @@ -63,10 +64,11 @@ def update(id, params) # @see DockerEngineRuby::Models::ConfigListParams def list(params = {}) parsed, options = DockerEngineRuby::ConfigListParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :get, path: "configs", - query: parsed, + query: query, model: DockerEngineRuby::Internal::Type::ArrayOf[DockerEngineRuby::ConfigObject], options: options ) diff --git a/lib/docker_engine_ruby/resources/containers.rb b/lib/docker_engine_ruby/resources/containers.rb index 47be0da..20abbf7 100644 --- a/lib/docker_engine_ruby/resources/containers.rb +++ b/lib/docker_engine_ruby/resources/containers.rb @@ -20,10 +20,11 @@ class Containers # @see DockerEngineRuby::Models::ContainerCreateParams def create(params) parsed, options = DockerEngineRuby::ContainerCreateParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed.except(:config)) @client.request( method: :post, path: "containers/create", - query: parsed.except(:config), + query: query, body: parsed[:config], model: DockerEngineRuby::CreateResponse, options: options @@ -130,10 +131,11 @@ def update(id, params = {}) # @see DockerEngineRuby::Models::ContainerListParams def list(params = {}) parsed, options = DockerEngineRuby::ContainerListParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :get, path: "containers/json", - query: parsed, + query: query, model: DockerEngineRuby::Internal::Type::ArrayOf[DockerEngineRuby::Summary], options: options ) @@ -154,10 +156,11 @@ def list(params = {}) # @see DockerEngineRuby::Models::ContainerDeleteParams def delete(id, params = {}) parsed, options = DockerEngineRuby::ContainerDeleteParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :delete, path: ["containers/%1$s", id], - query: parsed, + query: query, model: NilClass, options: options ) @@ -176,10 +179,11 @@ def delete(id, params = {}) # @see DockerEngineRuby::Models::ContainerArchiveParams def archive(id, params) parsed, options = DockerEngineRuby::ContainerArchiveParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :get, path: ["containers/%1$s/archive", id], - query: parsed, + query: query, model: NilClass, options: options ) @@ -203,10 +207,11 @@ def archive(id, params) # @see DockerEngineRuby::Models::ContainerAttachParams def attach(id, params = {}) parsed, options = DockerEngineRuby::ContainerAttachParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :post, path: ["containers/%1$s/attach", id], - query: parsed.transform_keys(detach_keys: "detachKeys"), + query: query.transform_keys(detach_keys: "detachKeys"), model: NilClass, options: options ) @@ -295,10 +300,11 @@ def export(id, params = {}) # @see DockerEngineRuby::Models::ContainerInspectParams def inspect_(id, params = {}) parsed, options = DockerEngineRuby::ContainerInspectParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :get, path: ["containers/%1$s/json", id], - query: parsed, + query: query, model: DockerEngineRuby::Container, options: options ) @@ -317,10 +323,11 @@ def inspect_(id, params = {}) # @see DockerEngineRuby::Models::ContainerKillParams def kill(id, params = {}) parsed, options = DockerEngineRuby::ContainerKillParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :post, path: ["containers/%1$s/kill", id], - query: parsed, + query: query, model: NilClass, options: options ) @@ -345,10 +352,11 @@ def kill(id, params = {}) # @see DockerEngineRuby::Models::ContainerLogsParams def logs(id, params = {}) parsed, options = DockerEngineRuby::ContainerLogsParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :get, path: ["containers/%1$s/logs", id], - query: parsed.transform_keys(until_: "until"), + query: query.transform_keys(until_: "until"), headers: {"accept" => "application/octet-stream"}, model: StringIO, options: options @@ -386,10 +394,11 @@ def pause(id, params = {}) # @see DockerEngineRuby::Models::ContainerPruneParams def prune(params = {}) parsed, options = DockerEngineRuby::ContainerPruneParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :post, path: "containers/prune", - query: parsed, + query: query, model: DockerEngineRuby::Models::ContainerPruneResponse, options: options ) @@ -408,10 +417,11 @@ def prune(params = {}) # @see DockerEngineRuby::Models::ContainerRenameParams def rename(id, params) parsed, options = DockerEngineRuby::ContainerRenameParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :post, path: ["containers/%1$s/rename", id], - query: parsed, + query: query, model: NilClass, options: options ) @@ -431,10 +441,11 @@ def rename(id, params) # @see DockerEngineRuby::Models::ContainerResizeParams def resize(id, params) parsed, options = DockerEngineRuby::ContainerResizeParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :post, path: ["containers/%1$s/resize", id], - query: parsed, + query: query, model: NilClass, options: options ) @@ -454,10 +465,11 @@ def resize(id, params) # @see DockerEngineRuby::Models::ContainerRestartParams def restart(id, params = {}) parsed, options = DockerEngineRuby::ContainerRestartParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :post, path: ["containers/%1$s/restart", id], - query: parsed, + query: query, model: NilClass, options: options ) @@ -476,10 +488,11 @@ def restart(id, params = {}) # @see DockerEngineRuby::Models::ContainerStartParams def start(id, params = {}) parsed, options = DockerEngineRuby::ContainerStartParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :post, path: ["containers/%1$s/start", id], - query: parsed.transform_keys(detach_keys: "detachKeys"), + query: query.transform_keys(detach_keys: "detachKeys"), model: NilClass, options: options ) @@ -502,10 +515,11 @@ def start(id, params = {}) # @see DockerEngineRuby::Models::ContainerStatsParams def stats(id, params = {}) parsed, options = DockerEngineRuby::ContainerStatsParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :get, path: ["containers/%1$s/stats", id], - query: parsed.transform_keys(one_shot: "one-shot"), + query: query.transform_keys(one_shot: "one-shot"), model: DockerEngineRuby::StatsResponse, options: options ) @@ -525,10 +539,11 @@ def stats(id, params = {}) # @see DockerEngineRuby::Models::ContainerStopParams def stop(id, params = {}) parsed, options = DockerEngineRuby::ContainerStopParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :post, path: ["containers/%1$s/stop", id], - query: parsed, + query: query, model: NilClass, options: options ) @@ -550,10 +565,11 @@ def stop(id, params = {}) # @see DockerEngineRuby::Models::ContainerTopParams def top(id, params = {}) parsed, options = DockerEngineRuby::ContainerTopParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :get, path: ["containers/%1$s/top", id], - query: parsed, + query: query, model: DockerEngineRuby::TopResponse, options: options ) @@ -591,10 +607,11 @@ def unpause(id, params = {}) # @see DockerEngineRuby::Models::ContainerWaitParams def wait(id, params = {}) parsed, options = DockerEngineRuby::ContainerWaitParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :post, path: ["containers/%1$s/wait", id], - query: parsed, + query: query, model: DockerEngineRuby::WaitResponse, options: options ) diff --git a/lib/docker_engine_ruby/resources/exec.rb b/lib/docker_engine_ruby/resources/exec.rb index 256d8bd..890daa9 100644 --- a/lib/docker_engine_ruby/resources/exec.rb +++ b/lib/docker_engine_ruby/resources/exec.rb @@ -36,10 +36,11 @@ def inspect_(id, params = {}) # @see DockerEngineRuby::Models::ExecResizeParams def resize(id, params) parsed, options = DockerEngineRuby::ExecResizeParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :post, path: ["exec/%1$s/resize", id], - query: parsed, + query: query, model: NilClass, options: options ) diff --git a/lib/docker_engine_ruby/resources/images.rb b/lib/docker_engine_ruby/resources/images.rb index e944517..da8a42b 100644 --- a/lib/docker_engine_ruby/resources/images.rb +++ b/lib/docker_engine_ruby/resources/images.rb @@ -19,10 +19,11 @@ class Images # @see DockerEngineRuby::Models::ImageListParams def list(params = {}) parsed, options = DockerEngineRuby::ImageListParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :get, path: "images/json", - query: parsed.transform_keys(shared_size: "shared-size"), + query: query.transform_keys(shared_size: "shared-size"), model: DockerEngineRuby::Internal::Type::ArrayOf[DockerEngineRuby::ImageSummary], options: options ) @@ -43,10 +44,11 @@ def list(params = {}) # @see DockerEngineRuby::Models::ImageDeleteParams def delete(name, params = {}) parsed, options = DockerEngineRuby::ImageDeleteParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :delete, path: ["images/%1$s", name], - query: parsed, + query: query, model: DockerEngineRuby::Internal::Type::ArrayOf[DockerEngineRuby::DeleteItem], options: options ) @@ -116,7 +118,6 @@ def delete(name, params = {}) # # @see DockerEngineRuby::Models::ImageBuildParams def build(params) - parsed, options = DockerEngineRuby::ImageBuildParams.dump_request(params) query_params = [ :buildargs, @@ -145,6 +146,8 @@ def build(params) :target, :version ] + parsed, options = DockerEngineRuby::ImageBuildParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :post, path: "build", @@ -177,10 +180,11 @@ def build(params) # @see DockerEngineRuby::Models::ImageBuildPruneParams def build_prune(params = {}) parsed, options = DockerEngineRuby::ImageBuildPruneParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :post, path: "build/prune", - query: parsed.transform_keys( + query: query.transform_keys( max_used_space: "max-used-space", min_free_space: "min-free-space", reserved_space: "reserved-space" @@ -265,12 +269,13 @@ def build_prune(params = {}) # # @see DockerEngineRuby::Models::ImageCommitParams def commit(params = {}) - parsed, options = DockerEngineRuby::ImageCommitParams.dump_request(params) query_params = [:author, :changes, :comment, :container, :pause, :repo, :tag] + parsed, options = DockerEngineRuby::ImageCommitParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed.slice(*query_params)) @client.request( method: :post, path: "commit", - query: parsed.slice(*query_params), + query: query, body: parsed.except(*query_params), model: DockerEngineRuby::Models::ImageCommitResponse, options: options @@ -290,10 +295,11 @@ def commit(params = {}) # @see DockerEngineRuby::Models::ImageGetParams def get(name, params = {}) parsed, options = DockerEngineRuby::ImageGetParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :get, path: ["images/%1$s/get", name], - query: parsed, + query: query, headers: {"accept" => "application/octet-stream"}, model: StringIO, options: options @@ -313,10 +319,11 @@ def get(name, params = {}) # @see DockerEngineRuby::Models::ImageGetAllParams def get_all(params = {}) parsed, options = DockerEngineRuby::ImageGetAllParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :get, path: "images/get", - query: parsed, + query: query, headers: {"accept" => "application/octet-stream"}, model: StringIO, options: options @@ -336,10 +343,11 @@ def get_all(params = {}) # @see DockerEngineRuby::Models::ImageHistoryParams def history(name, params = {}) parsed, options = DockerEngineRuby::ImageHistoryParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :get, path: ["images/%1$s/history", name], - query: parsed, + query: query, model: DockerEngineRuby::Internal::Type::ArrayOf[DockerEngineRuby::HistoryItem], options: options ) @@ -358,10 +366,11 @@ def history(name, params = {}) # @see DockerEngineRuby::Models::ImageInspectParams def inspect_(name, params = {}) parsed, options = DockerEngineRuby::ImageInspectParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :get, path: ["images/%1$s/json", name], - query: parsed, + query: query, model: DockerEngineRuby::Image, options: options ) @@ -384,10 +393,11 @@ def inspect_(name, params = {}) # @see DockerEngineRuby::Models::ImageLoadParams def load_(params) parsed, options = DockerEngineRuby::ImageLoadParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed.except(:body)) @client.request( method: :post, path: "images/load", - query: parsed.except(:body), + query: query, headers: {"content-type" => "application/octet-stream"}, body: parsed[:body], model: NilClass, @@ -407,10 +417,11 @@ def load_(params) # @see DockerEngineRuby::Models::ImagePruneParams def prune(params = {}) parsed, options = DockerEngineRuby::ImagePruneParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :post, path: "images/prune", - query: parsed, + query: query, model: DockerEngineRuby::Models::ImagePruneResponse, options: options ) @@ -444,8 +455,9 @@ def prune(params = {}) # # @see DockerEngineRuby::Models::ImagePullParams def pull(params) - parsed, options = DockerEngineRuby::ImagePullParams.dump_request(params) query_params = [:changes, :from_image, :from_src, :message, :platform, :repo, :tag] + parsed, options = DockerEngineRuby::ImagePullParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :post, path: "images/create", @@ -477,12 +489,13 @@ def pull(params) # # @see DockerEngineRuby::Models::ImagePushParams def push(name, params) - parsed, options = DockerEngineRuby::ImagePushParams.dump_request(params) query_params = [:platform, :tag] + parsed, options = DockerEngineRuby::ImagePushParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed.slice(*query_params)) @client.request( method: :post, path: ["images/%1$s/push", name], - query: parsed.slice(*query_params), + query: query, headers: parsed.except(*query_params).transform_keys(x_registry_auth: "x-registry-auth"), model: NilClass, options: options @@ -503,10 +516,11 @@ def push(name, params) # @see DockerEngineRuby::Models::ImageSearchParams def search(params) parsed, options = DockerEngineRuby::ImageSearchParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :get, path: "images/search", - query: parsed, + query: query, model: DockerEngineRuby::Internal::Type::ArrayOf[DockerEngineRuby::Models::ImageSearchResponseItem], options: options ) @@ -526,10 +540,11 @@ def search(params) # @see DockerEngineRuby::Models::ImageTagParams def tag(name, params = {}) parsed, options = DockerEngineRuby::ImageTagParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :post, path: ["images/%1$s/tag", name], - query: parsed, + query: query, model: NilClass, options: options ) diff --git a/lib/docker_engine_ruby/resources/networks.rb b/lib/docker_engine_ruby/resources/networks.rb index 4b64417..246fa05 100644 --- a/lib/docker_engine_ruby/resources/networks.rb +++ b/lib/docker_engine_ruby/resources/networks.rb @@ -64,10 +64,11 @@ def create(params) # @see DockerEngineRuby::Models::NetworkListParams def list(params = {}) parsed, options = DockerEngineRuby::NetworkListParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :get, path: "networks", - query: parsed, + query: query, model: DockerEngineRuby::Internal::Type::ArrayOf[DockerEngineRuby::NetworkSummary], options: options ) @@ -158,10 +159,11 @@ def disconnect(id, params) # @see DockerEngineRuby::Models::NetworkInspectParams def inspect_(id, params = {}) parsed, options = DockerEngineRuby::NetworkInspectParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :get, path: ["networks/%1$s", id], - query: parsed, + query: query, model: DockerEngineRuby::Models::NetworkInspectResponse, options: options ) @@ -179,10 +181,11 @@ def inspect_(id, params = {}) # @see DockerEngineRuby::Models::NetworkPruneParams def prune(params = {}) parsed, options = DockerEngineRuby::NetworkPruneParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :post, path: "networks/prune", - query: parsed, + query: query, model: DockerEngineRuby::Models::NetworkPruneResponse, options: options ) diff --git a/lib/docker_engine_ruby/resources/nodes.rb b/lib/docker_engine_ruby/resources/nodes.rb index 16ddcbc..2ba8f9f 100644 --- a/lib/docker_engine_ruby/resources/nodes.rb +++ b/lib/docker_engine_ruby/resources/nodes.rb @@ -20,10 +20,11 @@ class Nodes # @see DockerEngineRuby::Models::NodeUpdateParams def update(id, params) parsed, options = DockerEngineRuby::NodeUpdateParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed.except(:spec)) @client.request( method: :post, path: ["nodes/%1$s/update", id], - query: parsed.except(:spec), + query: query, body: parsed[:spec], model: NilClass, options: options @@ -42,10 +43,11 @@ def update(id, params) # @see DockerEngineRuby::Models::NodeListParams def list(params = {}) parsed, options = DockerEngineRuby::NodeListParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :get, path: "nodes", - query: parsed, + query: query, model: DockerEngineRuby::Internal::Type::ArrayOf[DockerEngineRuby::Node], options: options ) @@ -64,10 +66,11 @@ def list(params = {}) # @see DockerEngineRuby::Models::NodeDeleteParams def delete(id, params = {}) parsed, options = DockerEngineRuby::NodeDeleteParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :delete, path: ["nodes/%1$s", id], - query: parsed, + query: query, model: NilClass, options: options ) diff --git a/lib/docker_engine_ruby/resources/plugins.rb b/lib/docker_engine_ruby/resources/plugins.rb index 9bf6196..a57da35 100644 --- a/lib/docker_engine_ruby/resources/plugins.rb +++ b/lib/docker_engine_ruby/resources/plugins.rb @@ -18,10 +18,11 @@ class Plugins # @see DockerEngineRuby::Models::PluginCreateParams def create(params) parsed, options = DockerEngineRuby::PluginCreateParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed.except(:body)) @client.request( method: :post, path: "plugins/create", - query: parsed.except(:body), + query: query, headers: {"content-type" => "application/octet-stream"}, body: parsed[:body], model: NilClass, @@ -41,10 +42,11 @@ def create(params) # @see DockerEngineRuby::Models::PluginListParams def list(params = {}) parsed, options = DockerEngineRuby::PluginListParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :get, path: "plugins", - query: parsed, + query: query, model: DockerEngineRuby::Internal::Type::ArrayOf[DockerEngineRuby::Plugin], options: options ) @@ -63,10 +65,11 @@ def list(params = {}) # @see DockerEngineRuby::Models::PluginDeleteParams def delete(name, params = {}) parsed, options = DockerEngineRuby::PluginDeleteParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :delete, path: ["plugins/%1$s", name], - query: parsed, + query: query, model: DockerEngineRuby::Plugin, options: options ) @@ -85,10 +88,11 @@ def delete(name, params = {}) # @see DockerEngineRuby::Models::PluginDisableParams def disable(name, params = {}) parsed, options = DockerEngineRuby::PluginDisableParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :post, path: ["plugins/%1$s/disable", name], - query: parsed, + query: query, model: NilClass, options: options ) @@ -107,10 +111,11 @@ def disable(name, params = {}) # @see DockerEngineRuby::Models::PluginEnableParams def enable(name, params = {}) parsed, options = DockerEngineRuby::PluginEnableParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :post, path: ["plugins/%1$s/enable", name], - query: parsed, + query: query, model: NilClass, options: options ) @@ -147,10 +152,11 @@ def inspect_(name, params = {}) # @see DockerEngineRuby::Models::PluginPrivilegesParams def privileges(params) parsed, options = DockerEngineRuby::PluginPrivilegesParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :get, path: "plugins/privileges", - query: parsed, + query: query, model: DockerEngineRuby::Internal::Type::ArrayOf[DockerEngineRuby::Privilege], options: options ) @@ -174,8 +180,9 @@ def privileges(params) # # @see DockerEngineRuby::Models::PluginPullParams def pull(params) - parsed, options = DockerEngineRuby::PluginPullParams.dump_request(params) query_params = [:remote, :name] + parsed, options = DockerEngineRuby::PluginPullParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :post, path: "plugins/pull", @@ -246,8 +253,9 @@ def set(name, params) # # @see DockerEngineRuby::Models::PluginUpgradeParams def upgrade(name, params) - parsed, options = DockerEngineRuby::PluginUpgradeParams.dump_request(params) query_params = [:remote] + parsed, options = DockerEngineRuby::PluginUpgradeParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :post, path: ["plugins/%1$s/upgrade", name], diff --git a/lib/docker_engine_ruby/resources/secrets.rb b/lib/docker_engine_ruby/resources/secrets.rb index 257c0d7..653fc91 100644 --- a/lib/docker_engine_ruby/resources/secrets.rb +++ b/lib/docker_engine_ruby/resources/secrets.rb @@ -41,10 +41,11 @@ def create(params) # @see DockerEngineRuby::Models::SecretUpdateParams def update(id, params) parsed, options = DockerEngineRuby::SecretUpdateParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed.except(:spec)) @client.request( method: :post, path: ["secrets/%1$s/update", id], - query: parsed.except(:spec), + query: query, body: parsed[:spec], model: NilClass, options: options @@ -63,10 +64,11 @@ def update(id, params) # @see DockerEngineRuby::Models::SecretListParams def list(params = {}) parsed, options = DockerEngineRuby::SecretListParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :get, path: "secrets", - query: parsed, + query: query, model: DockerEngineRuby::Internal::Type::ArrayOf[DockerEngineRuby::Secret], options: options ) diff --git a/lib/docker_engine_ruby/resources/services.rb b/lib/docker_engine_ruby/resources/services.rb index 2bd8f88..27ade59 100644 --- a/lib/docker_engine_ruby/resources/services.rb +++ b/lib/docker_engine_ruby/resources/services.rb @@ -50,8 +50,9 @@ def create(params) # # @see DockerEngineRuby::Models::ServiceUpdateParams def update(id, params) - parsed, options = DockerEngineRuby::ServiceUpdateParams.dump_request(params) query_params = [:version, :registry_auth_from, :rollback] + parsed, options = DockerEngineRuby::ServiceUpdateParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :post, path: ["services/%1$s/update", id], @@ -76,10 +77,11 @@ def update(id, params) # @see DockerEngineRuby::Models::ServiceListParams def list(params = {}) parsed, options = DockerEngineRuby::ServiceListParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :get, path: "services", - query: parsed, + query: query, model: DockerEngineRuby::Internal::Type::ArrayOf[DockerEngineRuby::Service], options: options ) @@ -117,10 +119,11 @@ def delete(id, params = {}) # @see DockerEngineRuby::Models::ServiceInspectParams def inspect_(id, params = {}) parsed, options = DockerEngineRuby::ServiceInspectParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :get, path: ["services/%1$s", id], - query: parsed.transform_keys(insert_defaults: "insertDefaults"), + query: query.transform_keys(insert_defaults: "insertDefaults"), model: DockerEngineRuby::Service, options: options ) @@ -145,10 +148,11 @@ def inspect_(id, params = {}) # @see DockerEngineRuby::Models::ServiceLogsParams def logs(id, params = {}) parsed, options = DockerEngineRuby::ServiceLogsParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :get, path: ["services/%1$s/logs", id], - query: parsed, + query: query, headers: {"accept" => "application/octet-stream"}, model: StringIO, options: options diff --git a/lib/docker_engine_ruby/resources/swarm.rb b/lib/docker_engine_ruby/resources/swarm.rb index 02e76ef..c13da18 100644 --- a/lib/docker_engine_ruby/resources/swarm.rb +++ b/lib/docker_engine_ruby/resources/swarm.rb @@ -24,14 +24,11 @@ class Swarm # @see DockerEngineRuby::Models::SwarmUpdateParams def update(params) parsed, options = DockerEngineRuby::SwarmUpdateParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed.except(:spec)) @client.request( method: :post, path: "swarm/update", - query: parsed.except(:spec).transform_keys( - rotate_manager_token: "rotateManagerToken", - rotate_manager_unlock_key: "rotateManagerUnlockKey", - rotate_worker_token: "rotateWorkerToken" - ), + query: query, body: parsed[:spec], model: NilClass, options: options @@ -124,7 +121,8 @@ def join(params = {}) # @see DockerEngineRuby::Models::SwarmLeaveParams def leave(params = {}) parsed, options = DockerEngineRuby::SwarmLeaveParams.dump_request(params) - @client.request(method: :post, path: "swarm/leave", query: parsed, model: NilClass, options: options) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) + @client.request(method: :post, path: "swarm/leave", query: query, model: NilClass, options: options) end # Unlock a locked manager diff --git a/lib/docker_engine_ruby/resources/system.rb b/lib/docker_engine_ruby/resources/system.rb index 276c638..d826c0c 100644 --- a/lib/docker_engine_ruby/resources/system.rb +++ b/lib/docker_engine_ruby/resources/system.rb @@ -16,10 +16,11 @@ class System # @see DockerEngineRuby::Models::SystemDataUsageParams def data_usage(params = {}) parsed, options = DockerEngineRuby::SystemDataUsageParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :get, path: "system/df", - query: parsed, + query: query, model: DockerEngineRuby::Models::SystemDataUsageResponse, options: options ) @@ -39,10 +40,11 @@ def data_usage(params = {}) # @see DockerEngineRuby::Models::SystemEventsParams def events(params = {}) parsed, options = DockerEngineRuby::SystemEventsParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :get, path: "events", - query: parsed.transform_keys(until_: "until"), + query: query.transform_keys(until_: "until"), model: DockerEngineRuby::Models::SystemEventsResponse, options: options ) diff --git a/lib/docker_engine_ruby/resources/tasks.rb b/lib/docker_engine_ruby/resources/tasks.rb index 37bad58..eb34383 100644 --- a/lib/docker_engine_ruby/resources/tasks.rb +++ b/lib/docker_engine_ruby/resources/tasks.rb @@ -15,10 +15,11 @@ class Tasks # @see DockerEngineRuby::Models::TaskListParams def list(params = {}) parsed, options = DockerEngineRuby::TaskListParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :get, path: "tasks", - query: parsed, + query: query, model: DockerEngineRuby::Internal::Type::ArrayOf[DockerEngineRuby::Task], options: options ) @@ -62,10 +63,11 @@ def inspect_(id, params = {}) # @see DockerEngineRuby::Models::TaskLogsParams def logs(id, params = {}) parsed, options = DockerEngineRuby::TaskLogsParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :get, path: ["tasks/%1$s/logs", id], - query: parsed, + query: query, headers: {"accept" => "application/octet-stream"}, model: StringIO, options: options diff --git a/lib/docker_engine_ruby/resources/volumes.rb b/lib/docker_engine_ruby/resources/volumes.rb index eb61f2f..14d136d 100644 --- a/lib/docker_engine_ruby/resources/volumes.rb +++ b/lib/docker_engine_ruby/resources/volumes.rb @@ -41,12 +41,13 @@ def create(params) # # @see DockerEngineRuby::Models::VolumeUpdateParams def update(name, params) - parsed, options = DockerEngineRuby::VolumeUpdateParams.dump_request(params) query_params = [:version] + parsed, options = DockerEngineRuby::VolumeUpdateParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed.slice(*query_params)) @client.request( method: :put, path: ["volumes/%1$s", name], - query: parsed.slice(*query_params), + query: query, body: parsed.except(*query_params), model: NilClass, options: options @@ -65,10 +66,11 @@ def update(name, params) # @see DockerEngineRuby::Models::VolumeListParams def list(params = {}) parsed, options = DockerEngineRuby::VolumeListParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :get, path: "volumes", - query: parsed, + query: query, model: DockerEngineRuby::ListResponse, options: options ) @@ -87,10 +89,11 @@ def list(params = {}) # @see DockerEngineRuby::Models::VolumeDeleteParams def delete(name, params = {}) parsed, options = DockerEngineRuby::VolumeDeleteParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :delete, path: ["volumes/%1$s", name], - query: parsed, + query: query, model: NilClass, options: options ) @@ -127,10 +130,11 @@ def inspect_(name, params = {}) # @see DockerEngineRuby::Models::VolumePruneParams def prune(params = {}) parsed, options = DockerEngineRuby::VolumePruneParams.dump_request(params) + query = DockerEngineRuby::Internal::Util.encode_query_params(parsed) @client.request( method: :post, path: "volumes/prune", - query: parsed, + query: query, model: DockerEngineRuby::Models::VolumePruneResponse, options: options ) diff --git a/rbi/docker_engine_ruby/internal/util.rbi b/rbi/docker_engine_ruby/internal/util.rbi index 260e8e9..a39b75d 100644 --- a/rbi/docker_engine_ruby/internal/util.rbi +++ b/rbi/docker_engine_ruby/internal/util.rbi @@ -301,6 +301,26 @@ module DockerEngineRuby T.let(%r{^application/(:?x-(?:n|l)djson)|(:?(?:x-)?jsonl)}, Regexp) class << self + # @api private + sig do + params(query: DockerEngineRuby::Internal::AnyHash).returns( + DockerEngineRuby::Internal::AnyHash + ) + end + def encode_query_params(query) + end + + # @api private + sig do + params( + collection: DockerEngineRuby::Internal::AnyHash, + key: String, + element: T.anything + ).void + end + private def write_query_param_element!(collection, key, element) + end + # @api private sig do params( diff --git a/sig/docker_engine_ruby/internal/util.rbs b/sig/docker_engine_ruby/internal/util.rbs index 321540a..d39b40d 100644 --- a/sig/docker_engine_ruby/internal/util.rbs +++ b/sig/docker_engine_ruby/internal/util.rbs @@ -106,6 +106,16 @@ module DockerEngineRuby JSON_CONTENT: Regexp JSONL_CONTENT: Regexp + def encode_query_params: ( + ::Hash[Symbol, top] query + ) -> ::Hash[Symbol, top] + + private def write_query_param_element!: ( + ::Hash[Symbol, top] collection, + String key, + top element + ) -> nil + def self?.write_multipart_content: ( Enumerator::Yielder y, val: top, From f5b8022932e298a82aa303e923a6e5bf69279cdc Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 4 Mar 2026 08:08:23 +0000 Subject: [PATCH 3/4] chore(ci): add build step --- .github/workflows/ci.yml | 34 ++++++++++ scripts/utils/upload-artifact.sh | 113 +++++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+) create mode 100755 scripts/utils/upload-artifact.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f39b22b..aa22c63 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,6 +13,40 @@ on: - 'stl-preview-base/**' jobs: + build: + timeout-minutes: 10 + name: build + permissions: + contents: read + id-token: write + runs-on: ${{ github.repository == 'stainless-sdks/docker-ruby' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + if: |- + github.repository == 'stainless-sdks/docker-ruby' && + (github.event_name == 'push' || github.event.pull_request.head.repo.fork) + steps: + - uses: actions/checkout@v6 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + bundler-cache: false + - run: |- + bundle install + + - name: Get GitHub OIDC Token + if: github.repository == 'stainless-sdks/docker-ruby' + id: github-oidc + uses: actions/github-script@v8 + with: + script: core.setOutput('github_token', await core.getIDToken()); + + - name: Build and upload gem artifacts + if: github.repository == 'stainless-sdks/docker-ruby' + env: + URL: https://pkg.stainless.com/s + AUTH: ${{ steps.github-oidc.outputs.github_token }} + SHA: ${{ github.sha }} + PACKAGE_NAME: docker_engine_ruby + run: ./scripts/utils/upload-artifact.sh lint: timeout-minutes: 10 name: lint diff --git a/scripts/utils/upload-artifact.sh b/scripts/utils/upload-artifact.sh new file mode 100755 index 0000000..b495a41 --- /dev/null +++ b/scripts/utils/upload-artifact.sh @@ -0,0 +1,113 @@ +#!/usr/bin/env bash + +set -euo pipefail + +# ANSI Color Codes +GREEN='\033[32m' +RED='\033[31m' +NC='\033[0m' # No Color + +DIST_DIR="dist" + +log_error() { + local msg="$1" + local headers="$2" + local body="$3" + echo -e "${RED}${msg}${NC}" + [[ -f "$headers" ]] && echo -e "${RED}Headers:$(cat "$headers")${NC}" + echo -e "${RED}Body: ${body}${NC}" + exit 1 +} + +upload_file() { + local file_name="$1" + local tmp_headers + tmp_headers=$(mktemp) + + if [ -f "$file_name" ]; then + echo -e "${GREEN}Processing file: $file_name${NC}" + pkg_file_name="${file_name#"${DIST_DIR}/"}" + + # Get signed URL for uploading artifact file + signed_url_response=$(curl -X POST -G "$URL" \ + -sS --retry 5 \ + -D "$tmp_headers" \ + --data-urlencode "filename=$pkg_file_name" \ + -H "Authorization: Bearer $AUTH" \ + -H "Content-Type: application/json") + + # Validate JSON and extract URL + if ! signed_url=$(echo "$signed_url_response" | jq -e -r '.url' 2>/dev/null) || [[ "$signed_url" == "null" ]]; then + log_error "Failed to get valid signed URL" "$tmp_headers" "$signed_url_response" + fi + + # Set content-type based on file extension + local extension="${file_name##*.}" + local content_type + case "$extension" in + gem) content_type="application/octet-stream" ;; + gz) content_type="application/gzip" ;; + rz) content_type="application/octet-stream" ;; + html) content_type="text/html" ;; + *) content_type="application/octet-stream" ;; + esac + + # Upload file + upload_response=$(curl -v -X PUT \ + --retry 5 \ + --retry-all-errors \ + -D "$tmp_headers" \ + -H "Content-Type: $content_type" \ + --data-binary "@${file_name}" "$signed_url" 2>&1) + + if ! echo "$upload_response" | grep -q "HTTP/[0-9.]* 200"; then + log_error "Failed to upload artifact file" "$tmp_headers" "$upload_response" + fi + + # Insert small throttle to reduce rate limiting risk + sleep 0.1 + fi +} + +walk_tree() { + local current_dir="$1" + + for entry in "$current_dir"/*; do + # Check that entry is valid + [ -e "$entry" ] || [ -h "$entry" ] || continue + + if [ -d "$entry" ]; then + walk_tree "$entry" + else + upload_file "$entry" + fi + done +} + +cd "$(dirname "$0")/../.." + +echo "::group::Building gem" +VERSION_FILE="lib/${PACKAGE_NAME}/version.rb" +if [[ ! -f "$VERSION_FILE" ]]; then + echo -e "${RED}Version file not found: ${VERSION_FILE}${NC}" + exit 1 +fi +SHORT_SHA="${SHA:0:7}" +sed -i.bak -E "s/(VERSION = \"[^\"]+)\"/\1.beta.${SHORT_SHA}\"/" "$VERSION_FILE" +rm -f "${VERSION_FILE}.bak" + +gem build +mkdir -p "${DIST_DIR}/gems" +mv ./*.gem "${DIST_DIR}/gems/" +echo "::endgroup::" + +echo "::group::Generating gem index" +gem generate_index --directory "$DIST_DIR" +echo "::endgroup::" + +echo "::group::Uploading to pkg.stainless.com" +walk_tree "$DIST_DIR" +echo "::endgroup::" + +echo -e "${GREEN}Gem artifacts uploaded to Stainless storage.${NC}" +echo -e "\033[32mInstallation: bundle remove docker-engine-ruby && bundle add docker-engine-ruby --source 'https://pkg.stainless.com/s/docker-ruby/$SHA'\033[0m" From 74030dad1d5432b543ae012b1d773cbdf3034599 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 4 Mar 2026 08:08:43 +0000 Subject: [PATCH 4/4] release: 0.7.4 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 14 ++++++++++++++ Gemfile.lock | 2 +- README.md | 2 +- lib/docker_engine_ruby/version.rb | 2 +- 5 files changed, 18 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 88ca3ec..904b037 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.7.3" + ".": "0.7.4" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d406a1..1703ede 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 0.7.4 (2026-03-04) + +Full Changelog: [v0.7.3...v0.7.4](https://github.com/Hexlet/docker-ruby/compare/v0.7.3...v0.7.4) + +### Bug Fixes + +* properly mock time in ruby ci tests ([f291b96](https://github.com/Hexlet/docker-ruby/commit/f291b9605760add258fc3e596a664800663d9e7f)) + + +### Chores + +* **ci:** add build step ([f5b8022](https://github.com/Hexlet/docker-ruby/commit/f5b8022932e298a82aa303e923a6e5bf69279cdc)) +* **internal:** codegen related update ([ab55081](https://github.com/Hexlet/docker-ruby/commit/ab550815719723e748965fb23c2547aee2b1de91)) + ## 0.7.3 (2026-02-24) Full Changelog: [v0.7.2...v0.7.3](https://github.com/Hexlet/docker-ruby/compare/v0.7.2...v0.7.3) diff --git a/Gemfile.lock b/Gemfile.lock index 1f0ad75..dea228e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -11,7 +11,7 @@ GIT PATH remote: . specs: - docker-engine-ruby (0.7.3) + docker-engine-ruby (0.7.4) cgi connection_pool diff --git a/README.md b/README.md index 08fefc6..a44ba98 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ To use this gem, install via Bundler by adding the following to your application ```ruby -gem "docker-engine-ruby", "~> 0.7.3" +gem "docker-engine-ruby", "~> 0.7.4" ``` diff --git a/lib/docker_engine_ruby/version.rb b/lib/docker_engine_ruby/version.rb index 8a8dcb1..aa2b44b 100644 --- a/lib/docker_engine_ruby/version.rb +++ b/lib/docker_engine_ruby/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module DockerEngineRuby - VERSION = "0.7.3" + VERSION = "0.7.4" end