diff -pruN 2.2.0-2/.cane 2.4.0-1/.cane
--- 2.2.0-2/.cane	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/.cane	1970-01-01 00:00:00.000000000 +0000
@@ -1,2 +0,0 @@
---abc-max 30
---style-measure 120
diff -pruN 2.2.0-2/.github/workflows/unit_test.yml 2.4.0-1/.github/workflows/unit_test.yml
--- 2.2.0-2/.github/workflows/unit_test.yml	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/.github/workflows/unit_test.yml	1970-01-01 00:00:00.000000000 +0000
@@ -1,82 +0,0 @@
-name: Unit Tests
-on:
-  push:
-    branches:
-      # A test branch for seeing if your tests will pass in your personal fork
-      - test_me_github
-  pull_request:
-    branches:
-      - main
-      - master
-jobs:
-  docker-rspec:
-    runs-on:
-      - ubuntu-18.04
-    strategy:
-      matrix:
-        ruby:
-          - 2.7
-          - 2.6
-          - 2.5
-          - 2.4
-        docker_version:
-          - ':20.'
-          - ':19.'
-          - ':18.'
-      fail-fast: true
-    steps:
-      - uses: actions/checkout@v2
-      - uses: ruby/setup-ruby@v1
-        with:
-          ruby-version: ${{ matrix.ruby }}
-      - name: install bundler
-        run: |
-          gem install bundler -v '~> 1.17.3'
-          bundle update
-      - name: install docker
-        env:
-          DOCKER_VERSION: ${{ matrix.docker_version }}
-        run: |
-          set -x
-          sudo apt-get remove -y docker docker-engine docker.io containerd runc ||:
-          sudo apt-get update -y
-          sudo apt-get install -y apt-transport-https ca-certificates curl gnupg-agent software-properties-common
-          curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
-          sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
-          sudo apt-get update -y
-          sudo apt-cache gencaches
-          sudo apt-get install -y docker-ce=$( apt-cache madison docker-ce | grep -e $DOCKER_VERSION | cut -f 2 -d '|' | head -1 | sed 's/\s//g' )
-          if [ $? -ne 0 ]; then
-            echo "Error: Could not install ${DOCKER_VERSION}"
-            echo "Available docker versions:"
-            apt-cache madison docker-ce
-            exit 1
-          fi
-          sudo systemctl start docker
-      - name: spec tests
-        run: bundle exec rake
-
-  podman-rspec:
-    runs-on:
-      - ubuntu-latest
-    strategy:
-      matrix:
-        ruby:
-          - 2.7
-          - 2.6
-          - 2.5
-          - 2.4
-      fail-fast: true
-    steps:
-      - uses: actions/checkout@v2
-      - uses: ruby/setup-ruby@v1
-        with:
-          ruby-version: ${{ matrix.ruby }}
-      - name: install bundler
-        run: |
-          gem install bundler -v '~> 1.17.3'
-          bundle update
-      - name: install podman
-        run: sudo ./script/install_podman.sh
-      - name: spec tests
-        run: bundle exec rake
diff -pruN 2.2.0-2/.gitignore 2.4.0-1/.gitignore
--- 2.2.0-2/.gitignore	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/.gitignore	1970-01-01 00:00:00.000000000 +0000
@@ -1,5 +0,0 @@
-.DS_Store
-*.swp
-*.gem
-Gemfile.lock
-.ruby-*
diff -pruN 2.2.0-2/.rspec 2.4.0-1/.rspec
--- 2.2.0-2/.rspec	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/.rspec	1970-01-01 00:00:00.000000000 +0000
@@ -1 +0,0 @@
---order rand
diff -pruN 2.2.0-2/.simplecov 2.4.0-1/.simplecov
--- 2.2.0-2/.simplecov	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/.simplecov	1970-01-01 00:00:00.000000000 +0000
@@ -1,4 +0,0 @@
-SimpleCov.start do
-  add_group 'Library', 'lib'
-  add_group 'Specs', 'spec'
-end
diff -pruN 2.2.0-2/.travis.yml 2.4.0-1/.travis.yml
--- 2.2.0-2/.travis.yml	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/.travis.yml	1970-01-01 00:00:00.000000000 +0000
@@ -1,25 +0,0 @@
-os: linux
-dist: bionic
-language: ruby
-cache: bundler
-rvm:
-  - 2.7
-  - 2.6
-  - 2.5
-  - 2.4
-  - 2.3
-  - 2.2
-env:
-  - DOCKER_VERSION=5:19.03.8~3-0~ubuntu-bionic
-  - DOCKER_VERSION=5:18.09.9~3-0~ubuntu-bionic
-  - DOCKER_VERSION=18.06.3~ce~3-0~ubuntu
-jobs:
-  fast_finish: true
-before_install:
- - docker --version
- - gem install bundler -v '~> 1.17.3'
-before_script:
- - sudo ./script/install_docker.sh ${DOCKER_VERSION} ${DOCKER_CE}
- - uname -a
- - docker --version
- - docker info
diff -pruN 2.2.0-2/Dockerfile 2.4.0-1/Dockerfile
--- 2.2.0-2/Dockerfile	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/Dockerfile	1970-01-01 00:00:00.000000000 +0000
@@ -1,2 +0,0 @@
-FROM scratch
-ADD Dockerfile /Dockerfile
diff -pruN 2.2.0-2/Gemfile 2.4.0-1/Gemfile
--- 2.2.0-2/Gemfile	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/Gemfile	1970-01-01 00:00:00.000000000 +0000
@@ -1,3 +0,0 @@
-source 'http://rubygems.org'
-
-gemspec
diff -pruN 2.2.0-2/README.md 2.4.0-1/README.md
--- 2.2.0-2/README.md	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/README.md	2025-01-17 00:09:48.000000000 +0000
@@ -1,10 +1,10 @@
 docker-api
 ==========
-[![Gem Version](https://badge.fury.io/rb/docker-api.svg)](https://badge.fury.io/rb/docker-api) [![travis-ci](https://travis-ci.org/swipely/docker-api.svg?branch=master)](https://travis-ci.org/swipely/docker-api) [![Code Climate](https://codeclimate.com/github/swipely/docker-api.svg)](https://codeclimate.com/github/swipely/docker-api)
+[![Gem Version](https://badge.fury.io/rb/docker-api.svg)](https://badge.fury.io/rb/docker-api) [![Code Climate](https://codeclimate.com/github/upserve/docker-api.svg)](https://codeclimate.com/github/upserve/docker-api)
 
 This gem provides an object-oriented interface to the [Docker Engine API](https://docs.docker.com/develop/sdk/). Every method listed there is implemented. At the time of this writing, docker-api is meant to interface with Docker version 1.4.*
 
-If you're interested in using Docker to package your apps, we recommend the [dockly](https://github.com/swipely/dockly) gem. Dockly provides a simple DSL for describing Docker containers that install as Debian packages and are controlled by upstart scripts.
+If you're interested in using Docker to package your apps, we recommend the [dockly](https://github.com/upserve/dockly) gem. Dockly provides a simple DSL for describing Docker containers that install as Debian packages and are controlled by upstart scripts.
 
 Installation
 ------------
diff -pruN 2.2.0-2/Rakefile 2.4.0-1/Rakefile
--- 2.2.0-2/Rakefile	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/Rakefile	1970-01-01 00:00:00.000000000 +0000
@@ -1,54 +0,0 @@
-require 'bundler/setup'
-
-ENV['PATH'] = "/opt/docker/:#{ENV['PATH']}" if ENV['CI'] == 'true'
-
-require 'docker'
-require 'rspec/core/rake_task'
-require 'cane/rake_task'
-
-
-desc 'Run the full test suite from scratch'
-task :default => [:unpack, :rspec, :quality]
-
-RSpec::Core::RakeTask.new do |t|
-  t.pattern = 'spec/**/*_spec.rb'
-end
-
-Cane::RakeTask.new(:quality) do |cane|
-  cane.canefile = '.cane'
-end
-
-desc 'Download the necessary base images'
-task :unpack do
-  %w( swipely/base registry busybox tianon/true debian:stable ).each do |image|
-    system "docker pull #{image}"
-  end
-end
-
-desc 'Run spec tests with a registry'
-task :rspec do
-  begin
-    registry = Docker::Container.create(
-      'name' => 'registry',
-      'Image' => 'registry',
-      'Env' => ["GUNICORN_OPTS=[--preload]"],
-      'ExposedPorts' => {
-        '5000/tcp' => {}
-      },
-      'HostConfig' => {
-        'PortBindings' => { '5000/tcp' => [{ 'HostPort' => '5000' }] }
-      }
-    )
-    registry.start
-    Rake::Task["spec"].invoke
-  ensure
-    registry.kill!.remove unless registry.nil?
-  end
-end
-
-desc 'Pull an Ubuntu image'
-image 'ubuntu:13.10' do
-  puts "Pulling ubuntu:13.10"
-  image = Docker::Image.create('fromImage' => 'ubuntu', 'tag' => '13.10')
-  puts "Pulled ubuntu:13.10, image id: #{image.id}"
-end
diff -pruN 2.2.0-2/TESTING.md 2.4.0-1/TESTING.md
--- 2.2.0-2/TESTING.md	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/TESTING.md	1970-01-01 00:00:00.000000000 +0000
@@ -1,49 +0,0 @@
-# Prerequisites
-To develop on this gem, you must the following installed:
-* a sane Ruby 1.9+ environment with `bundler`
-```shell
-$ gem install bundler
-```
-* Docker v1.3.1 or greater
-
-
-
-# Getting Started
-1. Clone the git repository from Github:
-```shell
-$ git clone git@github.com:swipely/docker-api.git
-```
-2. Install the dependencies using Bundler
-```shell
-$ bundle install
-```
-3. Create a branch for your changes
-```shell
-$ git checkout -b my_bug_fix
-```
-4. Make any changes
-5. Write tests to support those changes.
-6. Run the tests:
-  * `bundle exec rake`
-7. Assuming the tests pass, open a Pull Request on Github.
-
-# Using Rakefile Commands
-This repository comes with five Rake commands to assist in your testing of the code.
-
-## `rake rspec`
-This command will run Rspec tests normally on your local system. You must have all the required base images pulled.
-
-## `rake quality`
-This command runs a code quality threshold checker to hinder bad code.
-
-## `rake unpack`
-Pulls down all the required base images for testing.
-
-### Setting Up Environment Variables
-Certain Rspec tests will require your credentials to the Docker Hub. If you do not have a Docker Hub account, you can sign up for one [here](https://hub.docker.com/account/signup/). To avoid hard-coding credentials into the code the test suite leverages three Environment Variables: `DOCKER_API_USER`, `DOCKER_API_PASS`, and `DOCKER_API_EMAIL`. You will need to configure your work environment (shell profile, IDE, etc) with these values in order to successfully run certain tests.
-
-```shell
-export DOCKER_API_USER='your_docker_hub_user'
-export DOCKER_API_PASS='your_docker_hub_password'
-export DOCKER_API_EMAIL='your_docker_hub_email_address'
-```
diff -pruN 2.2.0-2/debian/changelog 2.4.0-1/debian/changelog
--- 2.2.0-2/debian/changelog	2023-12-07 21:41:38.000000000 +0000
+++ 2.4.0-1/debian/changelog	2025-10-29 17:08:49.000000000 +0000
@@ -1,3 +1,12 @@
+ruby-docker-api (2.4.0-1) unstable; urgency=medium
+
+  * Team upload.
+  * New upstream release.
+  * Use ${ruby:Depends} for runtime dependencies.
+  * Update Standards-Version to 4.7.2, no changes needed.
+
+ -- Simon Quigley <tsimonq2@debian.org>  Wed, 29 Oct 2025 12:08:49 -0500
+
 ruby-docker-api (2.2.0-2) unstable; urgency=medium
 
   * Replace dependency on ruby-archive-tar-minitar with ruby-minitar.
diff -pruN 2.2.0-2/debian/control 2.4.0-1/debian/control
--- 2.2.0-2/debian/control	2023-12-07 21:41:38.000000000 +0000
+++ 2.4.0-1/debian/control	2025-10-29 17:07:32.000000000 +0000
@@ -10,7 +10,7 @@ Build-Depends: debhelper-compat (= 13),
                ruby-rspec,
                ruby-rspec-its,
                ruby-simplecov
-Standards-Version: 4.6.2
+Standards-Version: 4.7.2
 Vcs-Git: https://salsa.debian.org/ruby-team/ruby-docker-api.git
 Vcs-Browser: https://salsa.debian.org/ruby-team/ruby-docker-api
 Homepage: https://github.com/swipely/docker-api
@@ -19,9 +19,8 @@ Rules-Requires-Root: no
 Package: ruby-docker-api
 Architecture: all
 Multi-Arch: foreign
-Depends: ruby,
-         ruby-minitar,
-         ruby-excon,
+Depends: ruby-minitar,
+         ${ruby:Depends},
          ${misc:Depends},
          ${shlibs:Depends}
 Recommends: podman
diff -pruN 2.2.0-2/debian/gbp.conf 2.4.0-1/debian/gbp.conf
--- 2.2.0-2/debian/gbp.conf	2023-12-07 21:35:14.000000000 +0000
+++ 2.4.0-1/debian/gbp.conf	2025-10-29 17:05:28.000000000 +0000
@@ -1,3 +1,5 @@
 [DEFAULT]
+debian-branch = debian/latest
+upstream-branch = upstream/latest
 pristine-tar = True
 sign-tags = True
diff -pruN 2.2.0-2/debian/salsa-ci.yml 2.4.0-1/debian/salsa-ci.yml
--- 2.2.0-2/debian/salsa-ci.yml	2023-12-07 21:35:14.000000000 +0000
+++ 2.4.0-1/debian/salsa-ci.yml	2025-10-29 17:05:28.000000000 +0000
@@ -1,4 +1,3 @@
 ---
 include:
-  - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/salsa-ci.yml
-  - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/pipeline-jobs.yml
+  - https://salsa.debian.org/ruby-team/meta/raw/master/salsa-ci.yml
diff -pruN 2.2.0-2/docker-api.gemspec 2.4.0-1/docker-api.gemspec
--- 2.2.0-2/docker-api.gemspec	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/docker-api.gemspec	2025-01-17 00:09:48.000000000 +0000
@@ -1,23 +1,48 @@
+#########################################################
+# This file has been automatically generated by gem2tgz #
+#########################################################
 # -*- encoding: utf-8 -*-
-require File.expand_path('../lib/docker/version', __FILE__)
+# stub: docker-api 2.4.0 ruby lib
 
-Gem::Specification.new do |gem|
-  gem.authors       = ['Swipely, Inc.']
-  gem.email         = 'tomhulihan@swipely.com bright@swipely.com toddlunter@swipely.com'
-  gem.description   = gem.summary = 'A simple REST client for the Docker Remote API'
-  gem.homepage      = 'https://github.com/swipely/docker-api'
-  gem.license       = 'MIT'
-  gem.files         = `git ls-files lib README.md LICENSE`.split($\)
-  gem.name          = 'docker-api'
-  gem.version       = Docker::VERSION
-  gem.add_dependency 'excon', '>= 0.47.0'
-  gem.add_dependency 'multi_json'
-  gem.add_development_dependency 'rake'
-  gem.add_development_dependency 'rspec', '~> 3.0'
-  gem.add_development_dependency 'rspec-its'
-  gem.add_development_dependency 'cane'
-  gem.add_development_dependency 'pry'
-  gem.add_development_dependency 'single_cov'
-  gem.add_development_dependency 'webmock'
-  gem.add_development_dependency 'parallel'
+Gem::Specification.new do |s|
+  s.name = "docker-api".freeze
+  s.version = "2.4.0"
+
+  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
+  s.require_paths = ["lib".freeze]
+  s.authors = ["Swipely, Inc.".freeze]
+  s.date = "2024-10-30"
+  s.description = "A simple REST client for the Docker Remote API".freeze
+  s.email = "tomhulihan@swipely.com bright@swipely.com toddlunter@swipely.com".freeze
+  s.files = ["LICENSE".freeze, "README.md".freeze, "lib/docker-api.rb".freeze, "lib/docker.rb".freeze, "lib/docker/base.rb".freeze, "lib/docker/connection.rb".freeze, "lib/docker/container.rb".freeze, "lib/docker/error.rb".freeze, "lib/docker/event.rb".freeze, "lib/docker/exec.rb".freeze, "lib/docker/image.rb".freeze, "lib/docker/messages.rb".freeze, "lib/docker/messages_stack.rb".freeze, "lib/docker/network.rb".freeze, "lib/docker/rake_task.rb".freeze, "lib/docker/util.rb".freeze, "lib/docker/version.rb".freeze, "lib/docker/volume.rb".freeze, "lib/excon/middlewares/hijack.rb".freeze]
+  s.homepage = "https://github.com/upserve/docker-api".freeze
+  s.licenses = ["MIT".freeze]
+  s.rubygems_version = "3.3.15".freeze
+  s.summary = "A simple REST client for the Docker Remote API".freeze
+
+  if s.respond_to? :specification_version then
+    s.specification_version = 4
+  end
+
+  if s.respond_to? :add_runtime_dependency then
+    s.add_runtime_dependency(%q<excon>.freeze, [">= 0.64.0"])
+    s.add_runtime_dependency(%q<multi_json>.freeze, [">= 0"])
+    s.add_development_dependency(%q<parallel>.freeze, [">= 0"])
+    s.add_development_dependency(%q<pry>.freeze, [">= 0"])
+    s.add_development_dependency(%q<rake>.freeze, [">= 0"])
+    s.add_development_dependency(%q<rspec>.freeze, ["~> 3.0"])
+    s.add_development_dependency(%q<rspec-its>.freeze, [">= 0"])
+    s.add_development_dependency(%q<single_cov>.freeze, [">= 0"])
+    s.add_development_dependency(%q<webmock>.freeze, [">= 0"])
+  else
+    s.add_dependency(%q<excon>.freeze, [">= 0.64.0"])
+    s.add_dependency(%q<multi_json>.freeze, [">= 0"])
+    s.add_dependency(%q<parallel>.freeze, [">= 0"])
+    s.add_dependency(%q<pry>.freeze, [">= 0"])
+    s.add_dependency(%q<rake>.freeze, [">= 0"])
+    s.add_dependency(%q<rspec>.freeze, ["~> 3.0"])
+    s.add_dependency(%q<rspec-its>.freeze, [">= 0"])
+    s.add_dependency(%q<single_cov>.freeze, [">= 0"])
+    s.add_dependency(%q<webmock>.freeze, [">= 0"])
+  end
 end
diff -pruN 2.2.0-2/lib/docker/base.rb 2.4.0-1/lib/docker/base.rb
--- 2.2.0-2/lib/docker/base.rb	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/lib/docker/base.rb	2025-01-17 00:09:48.000000000 +0000
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 # This class is a base class for Docker Container and Image.
 # It is implementing accessor methods for the models attributes.
 module Docker::Base
diff -pruN 2.2.0-2/lib/docker/connection.rb 2.4.0-1/lib/docker/connection.rb
--- 2.2.0-2/lib/docker/connection.rb	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/lib/docker/connection.rb	2025-01-17 00:09:48.000000000 +0000
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 # This class represents a Connection to a Docker server. The Connection is
 # immutable in that once the url and options is set they cannot be changed.
 class Docker::Connection
diff -pruN 2.2.0-2/lib/docker/container.rb 2.4.0-1/lib/docker/container.rb
--- 2.2.0-2/lib/docker/container.rb	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/lib/docker/container.rb	2025-01-17 00:09:48.000000000 +0000
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 # This class represents a Docker Container. It's important to note that nothing
 # is cached so that the information is always up to date.
 class Docker::Container
diff -pruN 2.2.0-2/lib/docker/error.rb 2.4.0-1/lib/docker/error.rb
--- 2.2.0-2/lib/docker/error.rb	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/lib/docker/error.rb	2025-01-17 00:09:48.000000000 +0000
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 # This module holds the Errors for the gem.
 module Docker::Error
 
diff -pruN 2.2.0-2/lib/docker/event.rb 2.4.0-1/lib/docker/event.rb
--- 2.2.0-2/lib/docker/event.rb	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/lib/docker/event.rb	2025-01-17 00:09:48.000000000 +0000
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 # This class represents a Docker Event.
 class Docker::Event
   include Docker::Error
diff -pruN 2.2.0-2/lib/docker/exec.rb 2.4.0-1/lib/docker/exec.rb
--- 2.2.0-2/lib/docker/exec.rb	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/lib/docker/exec.rb	2025-01-17 00:09:48.000000000 +0000
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 # This class represents a Docker Exec Instance.
 class Docker::Exec
   include Docker::Base
diff -pruN 2.2.0-2/lib/docker/image.rb 2.4.0-1/lib/docker/image.rb
--- 2.2.0-2/lib/docker/image.rb	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/lib/docker/image.rb	2025-01-17 00:09:48.000000000 +0000
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 # This class represents a Docker Image.
 class Docker::Image
   include Docker::Base
@@ -28,7 +30,7 @@ class Docker::Image
     repo, tag = Docker::Util.parse_repo_tag(repo_tag)
     raise ArgumentError, "Image does not have a name to push." if repo.nil?
 
-    body = ""
+    body = +""
     credentials = creds || Docker.creds || {}
     headers = Docker::Util.build_auth_header(credentials)
     opts = {:tag => tag}.merge(options)
@@ -68,7 +70,7 @@ class Docker::Image
     name = opts.delete(:name)
 
     unless name
-      if ::Docker.podman?
+      if ::Docker.podman?(connection)
         name = self.id.split(':').last
       else
         name = self.id
@@ -119,7 +121,7 @@ class Docker::Image
     def create(opts = {}, creds = nil, conn = Docker.connection, &block)
       credentials = creds.nil? ? Docker.creds : MultiJson.dump(creds)
       headers = credentials && Docker::Util.build_auth_header(credentials) || {}
-      body = ''
+      body = +''
       conn.post(
         '/images/create',
         opts,
@@ -168,7 +170,7 @@ class Docker::Image
         end
         nil
       else
-        string = ''
+        string = +''
         save_stream(names, {}, conn, &response_block_for_save(string))
         string
       end
@@ -196,7 +198,7 @@ class Docker::Image
     def load(tar, opts = {}, conn = Docker.connection, creds = nil, &block)
        headers = build_headers(creds)
        io = tar.is_a?(String) ? File.open(tar, 'rb') : tar
-       body = ""
+       body = +""
        conn.post(
          '/images/load',
          opts,
@@ -267,7 +269,7 @@ class Docker::Image
 
     # Given a Dockerfile as a string, builds an Image.
     def build(commands, opts = {}, connection = Docker.connection, &block)
-      body = ""
+      body = +""
       connection.post(
         '/build', opts,
         :body => Docker::Util.create_tar('Dockerfile' => commands),
@@ -288,7 +290,7 @@ class Docker::Image
       headers = build_headers(creds)
 
       # The response_block passed to Excon will build up this body variable.
-      body = ""
+      body = +""
       connection.post(
         '/build', opts,
         :headers => headers,
@@ -340,7 +342,7 @@ class Docker::Image
   # Convience method to get the Dockerfile for a file hash and a path to
   # output to.
   def dockerfile_for(file_hash, output_path)
-    dockerfile = "from #{self.id}\n"
+    dockerfile = +"from #{self.id}\n"
 
     file_hash.keys.each do |basename|
       dockerfile << "add #{basename} #{output_path}\n"
diff -pruN 2.2.0-2/lib/docker/messages.rb 2.4.0-1/lib/docker/messages.rb
--- 2.2.0-2/lib/docker/messages.rb	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/lib/docker/messages.rb	2025-01-17 00:09:48.000000000 +0000
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 # This class represents all the messages either received by chunks from attach
 class Docker::Messages
 
diff -pruN 2.2.0-2/lib/docker/messages_stack.rb 2.4.0-1/lib/docker/messages_stack.rb
--- 2.2.0-2/lib/docker/messages_stack.rb	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/lib/docker/messages_stack.rb	2025-01-17 00:09:48.000000000 +0000
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 # This class represents a messages stack
 class Docker::MessagesStack
 
diff -pruN 2.2.0-2/lib/docker/network.rb 2.4.0-1/lib/docker/network.rb
--- 2.2.0-2/lib/docker/network.rb	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/lib/docker/network.rb	2025-01-17 00:09:48.000000000 +0000
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 # This class represents a Docker Network.
 class Docker::Network
   include Docker::Base
diff -pruN 2.2.0-2/lib/docker/rake_task.rb 2.4.0-1/lib/docker/rake_task.rb
--- 2.2.0-2/lib/docker/rake_task.rb	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/lib/docker/rake_task.rb	2025-01-17 00:09:48.000000000 +0000
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 # This class allows image-based tasks to be created.
 class Docker::ImageTask < Rake::Task
   def self.scope_name(_scope, task_name)
diff -pruN 2.2.0-2/lib/docker/util.rb 2.4.0-1/lib/docker/util.rb
--- 2.2.0-2/lib/docker/util.rb	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/lib/docker/util.rb	2025-01-17 00:09:48.000000000 +0000
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'set'
 
 # This module holds shared logic that doesn't really belong anywhere else in the
diff -pruN 2.2.0-2/lib/docker/version.rb 2.4.0-1/lib/docker/version.rb
--- 2.2.0-2/lib/docker/version.rb	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/lib/docker/version.rb	2025-01-17 00:09:48.000000000 +0000
@@ -1,4 +1,6 @@
+# frozen_string_literal: true
+
 module Docker
   # The version of the docker-api gem.
-  VERSION = '2.2.0'
+  VERSION = '2.4.0'
 end
diff -pruN 2.2.0-2/lib/docker/volume.rb 2.4.0-1/lib/docker/volume.rb
--- 2.2.0-2/lib/docker/volume.rb	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/lib/docker/volume.rb	2025-01-17 00:09:48.000000000 +0000
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 # class represents a Docker Volume
 class Docker::Volume
   include Docker::Base
diff -pruN 2.2.0-2/lib/docker-api.rb 2.4.0-1/lib/docker-api.rb
--- 2.2.0-2/lib/docker-api.rb	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/lib/docker-api.rb	2025-01-17 00:09:48.000000000 +0000
@@ -1 +1,3 @@
+# frozen_string_literal: true
+
 require 'docker'
diff -pruN 2.2.0-2/lib/docker.rb 2.4.0-1/lib/docker.rb
--- 2.2.0-2/lib/docker.rb	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/lib/docker.rb	2025-01-17 00:09:48.000000000 +0000
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'cgi'
 require 'multi_json'
 require 'excon'
diff -pruN 2.2.0-2/lib/excon/middlewares/hijack.rb 2.4.0-1/lib/excon/middlewares/hijack.rb
--- 2.2.0-2/lib/excon/middlewares/hijack.rb	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/lib/excon/middlewares/hijack.rb	2025-01-17 00:09:48.000000000 +0000
@@ -1,11 +1,15 @@
-module Excon
-  VALID_REQUEST_KEYS << :hijack_block
+# frozen_string_literal: true
 
+module Excon
   module Middleware
     # Hijack is an Excon middleware which parses response headers and then
     # yields the underlying TCP socket for raw TCP communication (used to
     # attach to STDIN of containers).
     class Hijack < Base
+      def self.valid_parameter_keys
+        [:hijack_block].freeze
+      end
+
       def build_response(status, socket)
         response = {
           :body          => '',
diff -pruN 2.2.0-2/script/docker 2.4.0-1/script/docker
--- 2.2.0-2/script/docker	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/script/docker	1970-01-01 00:00:00.000000000 +0000
@@ -1,149 +0,0 @@
-#!/bin/sh
-set -e
-
-### BEGIN INIT INFO
-# Provides:           docker
-# Required-Start:     $syslog $remote_fs
-# Required-Stop:      $syslog $remote_fs
-# Should-Start:       cgroupfs-mount cgroup-lite
-# Should-Stop:        cgroupfs-mount cgroup-lite
-# Default-Start:      2 3 4 5
-# Default-Stop:       0 1 6
-# Short-Description:  Create lightweight, portable, self-sufficient containers.
-# Description:
-#  Docker is an open-source project to easily create lightweight, portable,
-#  self-sufficient containers from any application. The same container that a
-#  developer builds and tests on a laptop can run at scale, in production, on
-#  VMs, bare metal, OpenStack clusters, public clouds and more.
-### END INIT INFO
-
-export PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
-
-BASE=$(basename $0)
-
-# modify these in /etc/default/$BASE (/etc/default/docker)
-DOCKER=/usr/bin/$BASE
-# This is the pid file managed by docker itself
-DOCKER_PIDFILE=/var/run/$BASE.pid
-# This is the pid file created/managed by start-stop-daemon
-DOCKER_SSD_PIDFILE=/var/run/$BASE-ssd.pid
-DOCKER_LOGFILE=/var/log/$BASE.log
-DOCKER_OPTS=
-DOCKER_DESC="Docker"
-
-# Get lsb functions
-. /lib/lsb/init-functions
-
-if [ -f /etc/default/$BASE ]; then
-        . /etc/default/$BASE
-fi
-
-# Check docker is present
-if [ ! -x $DOCKER ]; then
-        log_failure_msg "$DOCKER not present or not executable"
-        exit 1
-fi
-
-check_init() {
-         # see also init_is_upstart in /lib/lsb/init-functions (which isn't available in Ubuntu 12.04, or we'd use it directly)
-         if [ -x /sbin/initctl ] && /sbin/initctl version 2>/dev/null | grep -q upstart; then
-                log_failure_msg "$DOCKER_DESC is managed via upstart, try using service $BASE $1"
-                exit 1
-         fi
-}
-
-fail_unless_root() {
-        if [ "$(id -u)" != '0' ]; then
-                log_failure_msg "$DOCKER_DESC must be run as root"
-                exit 1
-        fi
-}
-
-cgroupfs_mount() {
-        # see also https://github.com/tianon/cgroupfs-mount/blob/master/cgroupfs-mount
-        if grep -v '^#' /etc/fstab | grep -q cgroup \
-                || [ ! -e /proc/cgroups ] \
-                || [ ! -d /sys/fs/cgroup ]; then
-                return
-        fi
-        if ! mountpoint -q /sys/fs/cgroup; then
-                mount -t tmpfs -o uid=0,gid=0,mode=0755 cgroup /sys/fs/cgroup
-        fi
-        (
-                cd /sys/fs/cgroup
-                for sys in $(awk '!/^#/ { if ($4 == 1) print $1 }' /proc/cgroups); do
-                        mkdir -p $sys
-                        if ! mountpoint -q $sys; then
-                                if ! mount -n -t cgroup -o $sys cgroup $sys; then
-                                        rmdir $sys || true
-                                fi
-                        fi
-                done
-        )
-}
-
-case "$1" in
-        start)
-                check_init
-
-                fail_unless_root
-
-                cgroupfs_mount
-
-                touch "$DOCKER_LOGFILE"
-                chgrp docker "$DOCKER_LOGFILE"
-
-                ulimit -n 1048576
-                if [ "$BASH" ]; then
-                        ulimit -u 1048576
-                else
-                        ulimit -p 1048576
-                fi
-
-                log_begin_msg "Starting $DOCKER_DESC: $BASE"
-                start-stop-daemon --start --background \
-                        --no-close \
-                        --exec "$DOCKER" \
-                        --pidfile "$DOCKER_SSD_PIDFILE" \
-                        --make-pidfile \
-                        -- \
-                                -d -p "$DOCKER_PIDFILE" \
-                                $DOCKER_OPTS \
-                                        >> "$DOCKER_LOGFILE" 2>&1
-                log_end_msg $?
-                ;;
-
-        stop)
-                check_init
-                fail_unless_root
-                log_begin_msg "Stopping $DOCKER_DESC: $BASE"
-                start-stop-daemon --stop --pidfile "$DOCKER_SSD_PIDFILE"
-                log_end_msg $?
-                ;;
-
-        restart)
-                check_init
-                fail_unless_root
-                docker_pid=`cat "$DOCKER_SSD_PIDFILE" 2>/dev/null`
-                [ -n "$docker_pid" ] \
-                        && ps -p $docker_pid > /dev/null 2>&1 \
-                        && $0 stop
-                $0 start
-                ;;
-
-        force-reload)
-                check_init
-                fail_unless_root
-                $0 restart
-                ;;
-
-        status)
-                check_init
-                status_of_proc -p "$DOCKER_SSD_PIDFILE" "$DOCKER" "$DOCKER_DESC"
-                ;;
-
-        *)
-                echo "Usage: service docker {start|stop|restart|status}"
-                exit 1
-                ;;
-esac
diff -pruN 2.2.0-2/script/docker.conf 2.4.0-1/script/docker.conf
--- 2.2.0-2/script/docker.conf	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/script/docker.conf	1970-01-01 00:00:00.000000000 +0000
@@ -1,61 +0,0 @@
-description "Docker daemon"
-
-start on (local-filesystems and net-device-up IFACE!=lo)
-stop on runlevel [!2345]
-limit nofile 524288 1048576
-limit nproc 524288 1048576
-
-respawn
-
-kill timeout 20
-
-pre-start script
-        # see also https://github.com/tianon/cgroupfs-mount/blob/master/cgroupfs-mount
-        if grep -v '^#' /etc/fstab | grep -q cgroup \
-                || [ ! -e /proc/cgroups ] \
-                || [ ! -d /sys/fs/cgroup ]; then
-                exit 0
-        fi
-        if ! mountpoint -q /sys/fs/cgroup; then
-                mount -t tmpfs -o uid=0,gid=0,mode=0755 cgroup /sys/fs/cgroup
-        fi
-        (
-                cd /sys/fs/cgroup
-                for sys in $(awk '!/^#/ { if ($4 == 1) print $1 }' /proc/cgroups); do
-                        mkdir -p $sys
-                        if ! mountpoint -q $sys; then
-                                if ! mount -n -t cgroup -o $sys cgroup $sys; then
-                                        rmdir $sys || true
-                                fi
-                        fi
-                done
-        )
-end script
-
-script
-        # modify these in /etc/default/$UPSTART_JOB (/etc/default/docker)
-        DOCKER=/usr/bin/$UPSTART_JOB
-        DOCKER_OPTS=
-        if [ -f /etc/default/$UPSTART_JOB ]; then
-                . /etc/default/$UPSTART_JOB
-        fi
-        exec "$DOCKER" -d $DOCKER_OPTS
-end script
-
-# Don't emit "started" event until docker.sock is ready.
-# See https://github.com/docker/docker/issues/6647
-post-start script
-        DOCKER_OPTS=
-        if [ -f /etc/default/$UPSTART_JOB ]; then
-                . /etc/default/$UPSTART_JOB
-        fi
-        if ! printf "%s" "$DOCKER_OPTS" | grep -qE -e '-H|--host'; then
-                while ! [ -e /var/run/docker.sock ]; do
-                        initctl status $UPSTART_JOB | grep -qE "(stop|respawn)/" && exit 1
-                        echo "Waiting for /var/run/docker.sock"
-                        sleep 0.1
-                done
-                echo "/var/run/docker.sock is up"
-        fi
-end script
-
diff -pruN 2.2.0-2/script/install_docker.sh 2.4.0-1/script/install_docker.sh
--- 2.2.0-2/script/install_docker.sh	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/script/install_docker.sh	1970-01-01 00:00:00.000000000 +0000
@@ -1,44 +0,0 @@
-#!/bin/bash
-set -ex
-
-declare -a SEMVER
-
-# argv[0]
-DOCKER_VERSION=$1
-# argv[1]
-DOCKER_CE=$2
-
-# disable travis default installation
-systemctl stop docker.service
-apt-get -y --purge remove docker docker-engine docker.io containerd runc
-
-# install gpg key for docker rpo
-curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
-apt-key fingerprint 0EBFCD88
-
-# enable docker repo
-add-apt-repository \
-  "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
-  $(lsb_release -cs) \
-  stable"
-apt-get update
-apt-cache gencaches
-
-set +e
-# install package
-apt-get install docker-ce=${DOCKER_VERSION}
-
-if [ $? -ne 0 ]; then
-  echo "Error: Could not install ${DOCKER_VERSION}"
-  echo "Available docker versions:"
-  apt-cache madison docker-ce
-  exit 1
-fi
-set -e
-
-systemctl stop docker.service
-
-echo 'DOCKER_OPTS="-H unix:///var/run/docker.sock --pidfile=/var/run/docker.pid"' > /etc/default/docker
-cat /etc/default/docker
-
-systemctl start docker.service
diff -pruN 2.2.0-2/script/install_podman.sh 2.4.0-1/script/install_podman.sh
--- 2.2.0-2/script/install_podman.sh	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/script/install_podman.sh	1970-01-01 00:00:00.000000000 +0000
@@ -1,12 +0,0 @@
-#!/bin/sh
-set -ex
-
-. /etc/os-release
-
-curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_${VERSION_ID}/Release.key | sudo apt-key add -
-
-echo "deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_18.04/ /" > /etc/apt/sources.list.d/podman.list
-
-apt-get update
-
-apt-get install -y podman
diff -pruN 2.2.0-2/spec/cov_spec.rb 2.4.0-1/spec/cov_spec.rb
--- 2.2.0-2/spec/cov_spec.rb	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/spec/cov_spec.rb	1970-01-01 00:00:00.000000000 +0000
@@ -1,21 +0,0 @@
-require 'spec_helper'
-
-SingleCov.not_covered!
-
-describe "Coverage" do
-  it "has coverage for all tests" do
-    SingleCov.assert_used
-  end
-
-  it "has tests for all files" do
-    SingleCov.assert_tested untested: %w[
-      lib/docker/base.rb
-      lib/docker/error.rb
-      lib/docker/messages_stack.rb
-      lib/docker/rake_task.rb
-      lib/docker/version.rb
-      lib/docker-api.rb
-      lib/excon/middlewares/hijack.rb
-    ]
-  end
-end
diff -pruN 2.2.0-2/spec/docker/connection_spec.rb 2.4.0-1/spec/docker/connection_spec.rb
--- 2.2.0-2/spec/docker/connection_spec.rb	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/spec/docker/connection_spec.rb	1970-01-01 00:00:00.000000000 +0000
@@ -1,125 +0,0 @@
-require 'spec_helper'
-
-SingleCov.covered! uncovered: 12
-
-describe Docker::Connection do
-  subject { described_class.new('http://localhost:4243', {}) }
-
-  describe '#initialize' do
-    let(:url) { 'http://localhost:4243' }
-    let(:options) { {} }
-    subject { described_class.new(url, options) }
-
-    context 'when the first argument is not a String' do
-      let(:url) { :lol_not_a_string }
-
-      it 'raises an error' do
-        expect { subject }.to raise_error(Docker::Error::ArgumentError)
-      end
-    end
-
-    context 'when the first argument is a String' do
-      context 'and the url is a unix socket' do
-        let(:url) { ::Docker.env_url || ::Docker.default_socket_url }
-
-        it 'sets the socket path in the options' do
-          expect(subject.url).to eq('unix:///')
-          expect(subject.options).to include(:socket => url.split('//').last)
-        end
-      end
-
-      context 'but the second argument is not a Hash' do
-        let(:options) { :lol_not_a_hash }
-
-        it 'raises an error' do
-          expect { subject }.to raise_error(Docker::Error::ArgumentError)
-        end
-      end
-
-      context 'and the second argument is a Hash' do
-        it 'sets the url and options' do
-          expect(subject.url).to eq url
-          expect(subject.options).to eq options
-        end
-      end
-    end
-
-    context 'url conversion to uri' do
-      context 'when the url does not contain a scheme' do
-        let(:url) { 'localhost:4243' }
-
-        it 'adds the scheme to the url' do
-          expect(subject.url).to eq "http://#{url}"
-        end
-      end
-
-      context 'when the url is a complete uri' do
-        let(:url) { 'http://localhost:4243' }
-
-        it 'leaves the url intact' do
-          expect(subject.url).to eq url
-        end
-      end
-    end
-  end
-
-  describe '#resource' do
-    its(:resource) { should be_a Excon::Connection }
-  end
-
-  describe '#request' do
-    let(:method) { :get }
-    let(:path) { '/test' }
-    let(:query) { { :all => true } }
-    let(:options) { { :expects => 201, :lol => true } }
-    let(:body) { rand(10000000) }
-    let(:resource) { double(:resource) }
-    let(:response) { double(:response, :body => body) }
-    let(:expected_hash) {
-      {
-        :method  => method,
-        :path    => path,
-        :query   => query,
-        :headers => { 'Content-Type' => 'text/plain',
-                      'User-Agent'   => "Swipely/Docker-API #{Docker::VERSION}",
-                    },
-        :expects => 201,
-        :idempotent => true,
-        :lol => true
-      }
-    }
-
-    before do
-      allow(subject).to receive(:resource).and_return(resource)
-      expect(resource).to receive(:request).
-        with(expected_hash).
-        and_return(response)
-    end
-
-    it 'sends #request to #resource with the compiled params' do
-      expect(subject.request(method, path, query, options)).to eq body
-    end
-  end
-
-  [:get, :put, :post, :delete].each do |method|
-    describe "##{method}" do
-      it 'is delegated to #request' do
-        expect(subject).to receive(:request).with(method)
-        subject.public_send(method)
-      end
-    end
-  end
-
-  describe '#to_s' do
-    let(:url) { 'http://google.com:4000' }
-    let(:options) { {} }
-    let(:expected_string) {
-      "Docker::Connection { :url => #{url}, :options => #{options} }"
-    }
-    subject { described_class.new(url, options) }
-
-    it 'returns a pretty version with the url and port' do
-      expect(subject.to_s).to eq expected_string
-    end
-  end
-end
diff -pruN 2.2.0-2/spec/docker/container_spec.rb 2.4.0-1/spec/docker/container_spec.rb
--- 2.2.0-2/spec/docker/container_spec.rb	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/spec/docker/container_spec.rb	1970-01-01 00:00:00.000000000 +0000
@@ -1,937 +0,0 @@
-require 'spec_helper'
-
-SingleCov.covered! uncovered: 39
-
-describe Docker::Container do
-  describe '#to_s' do
-    subject {
-      described_class.send(:new, Docker.connection, 'id' => rand(10000).to_s)
-    }
-
-    let(:id) { 'bf119e2' }
-    let(:connection) { Docker.connection }
-    let(:expected_string) {
-      "Docker::Container { :id => #{id}, :connection => #{connection} }"
-    }
-    before do
-      {
-        :@id => id,
-        :@connection => connection
-      }.each { |k, v| subject.instance_variable_set(k, v) }
-    end
-
-    its(:to_s) { should == expected_string }
-  end
-
-  describe '#json' do
-    subject {
-      described_class.create('Cmd' => %w[true], 'Image' => 'debian:stable')
-    }
-    let(:description) { subject.json }
-    after(:each) { subject.remove }
-
-    it 'returns the description as a Hash' do
-      expect(description).to be_a Hash
-      expect(description['Id']).to start_with(subject.id)
-    end
-  end
-
-  describe '#streaming_logs' do
-    let(:options) { {} }
-    subject do
-      described_class.create(
-        {'Cmd' => ['/bin/bash', '-lc', 'echo hello'], 'Image' => 'debian:stable'}.merge(options)
-      )
-    end
-
-    before(:each) { subject.tap(&:start).wait }
-    after(:each) { subject.remove }
-
-    context 'when not selecting any stream' do
-      let(:non_destination) { subject.streaming_logs }
-      it 'raises a client error' do
-        expect { non_destination }.to raise_error(Docker::Error::ClientError)
-      end
-    end
-
-    context 'when selecting stdout' do
-      let(:stdout) { subject.streaming_logs(stdout: 1) }
-      it 'returns blank logs' do
-        expect(stdout).to be_a String
-        expect(stdout).to match("hello")
-      end
-    end
-
-    context 'when using a tty' do
-      let(:options) { { 'Tty' => true } }
-
-      let(:output) { subject.streaming_logs(stdout: 1, tty: 1) }
-      it 'returns `hello`' do
-        expect(output).to be_a(String)
-        expect(output).to match("hello")
-      end
-    end
-
-    context 'when passing a block' do
-      let(:lines) { [] }
-      let(:output) { subject.streaming_logs(stdout: 1, follow: 1) { |s,c| lines << c } }
-      it 'returns `hello`' do
-        expect(output).to be_a(String)
-        expect(output).to match("hello")
-        expect(lines.join).to match("hello")
-      end
-    end
-  end
-
-  describe '#stats', :docker_1_9 do
-    after(:each) do
-      subject.wait
-      subject.remove
-    end
-
-    context "when requesting container stats" do
-      subject {
-        described_class.create('Cmd' => ['echo', 'hello'], 'Image' => 'debian:stable')
-      }
-
-      let(:output) { subject.stats }
-      it "returns a Hash" do
-        skip('Not supported on podman') if ::Docker.podman?
-        expect(output).to be_a Hash
-      end
-    end
-
-    context "when streaming container stats" do
-      subject {
-        described_class.create(
-          'Cmd' => ['sleep', '3'],
-          'Image' => 'debian:stable'
-        )
-      }
-
-      it "yields a Hash" do
-        skip('Not supported on podman') if ::Docker.podman?
-        subject.start! # If the container isn't started, no stats will be streamed
-        called_count = 0
-        subject.stats do |output|
-          expect(output).to be_a Hash
-          called_count += 1
-          break if called_count == 2
-        end
-        expect(called_count).to eq 2
-      end
-    end
-  end
-
-  describe '#logs' do
-    subject {
-      described_class.create('Cmd' => ['echo',  'hello'], 'Image' => 'debian:stable')
-    }
-    after(:each) { subject.remove }
-
-    context "when not selecting any stream" do
-      let(:non_destination) { subject.logs }
-      it 'raises a client error' do
-        expect { non_destination }.to raise_error(Docker::Error::ClientError)
-      end
-    end
-
-    context "when selecting stdout" do
-      let(:stdout) { subject.logs(stdout: 1) }
-      it 'returns blank logs' do
-        expect(stdout).to be_a String
-        expect(stdout).to eq ""
-      end
-    end
-  end
-
-  describe '#create' do
-    subject {
-      described_class.create({
-        'Cmd' => %w[true],
-        'Image' => 'debian:stable'
-      }.merge(opts))
-    }
-
-    context 'when creating a container named bob' do
-      let(:opts) { {"name" => "bob"} }
-      after(:each) { subject.remove }
-
-      it 'should have name set to bob' do
-        expect(subject.json["Name"]).to eq("/bob")
-      end
-    end
-  end
-
-  describe '#rename' do
-    subject {
-      described_class.create({
-        'name' => 'foo',
-        'Cmd' => %w[true],
-        'Image' => 'debian:stable'
-      })
-    }
-
-    before { subject.start }
-    after(:each) { subject.tap(&:wait).remove }
-
-    it 'renames the container' do
-      skip('Not supported on podman') if ::Docker.podman?
-      subject.rename('bar')
-      expect(subject.json["Name"]).to match(%r{bar})
-    end
-  end
-
-  describe "#update", :docker_1_10 do
-    subject {
-      described_class.create({
-        "name" => "foo",
-        'Cmd' => %w[true],
-        "Image" => "debian:stable",
-        "HostConfig" => {
-          "CpuShares" => 60000
-        }
-      })
-    }
-
-    before { subject.tap(&:start).tap(&:wait) }
-    after(:each) { subject.tap(&:wait).remove }
-
-    it "updates the container" do
-      skip('Podman containers are immutable once created') if ::Docker.podman?
-      subject.refresh!
-      expect(subject.info.fetch("HostConfig").fetch("CpuShares")).to eq 60000
-      subject.update("CpuShares" => 50000)
-      subject.refresh!
-      expect(subject.info.fetch("HostConfig").fetch("CpuShares")).to eq 50000
-    end
-  end
-
-  describe '#changes' do
-    subject {
-      described_class.create(
-        'Cmd' => %w[rm -rf /root],
-        'Image' => 'debian:stable'
-      )
-    }
-    let(:changes) { subject.changes }
-
-    before { subject.tap(&:start).tap(&:wait) }
-    after(:each) { subject.tap(&:wait).remove }
-
-    it 'returns the changes as an array' do
-      expect(changes).to be_a(Array)
-      expect(changes).to include(
-        {
-          "Path" => "/root",
-          "Kind" => 2
-        },
-      )
-    end
-  end
-
-  describe '#top' do
-    let(:dir) {
-      File.join(File.dirname(__FILE__), '..', 'fixtures', 'top')
-    }
-    let(:image) { Docker::Image.build_from_dir(dir) }
-    let(:top_empty) { sleep 1; container.top }
-    let(:top_ary) { sleep 1; container.top }
-    let(:top_hash) { sleep 1; container.top(format: :hash) }
-    let!(:container) { image.run('/while') }
-    after do
-      container.kill!.remove
-      image.remove
-    end
-
-    it 'returns the top commands as an Array' do
-      expect(top_ary).to be_a Array
-      expect(top_ary).to_not be_empty
-      expect(top_ary.first.keys).to include(/PID/)
-    end
-
-    it 'returns the top commands as an Hash' do
-      expect(top_hash).to be_a Hash
-      expect(top_hash).to_not be_empty
-      expect(top_hash.keys).to eq ['Processes', 'Titles']
-    end
-
-    it 'returns nothing when Processes were not returned due to an error' do
-      expect(Docker::Util).to receive(:parse_json).and_return({}).at_least(:once)
-      expect(top_empty).to eq []
-    end
-  end
-
-  describe '#archive_in', :docker_1_8 do
-    let(:license_path) { File.absolute_path(File.join(__FILE__, '..', '..', '..', 'LICENSE')) }
-    subject { Docker::Container.create('Image' => 'debian:stable', 'Cmd' => ['/bin/sh']) }
-    let(:committed_image) { subject.commit }
-    let(:ls_container) { committed_image.run('ls /').tap(&:wait) }
-    let(:output) { ls_container.streaming_logs(stdout: true, stderr: true) }
-
-    after do
-      subject.remove
-    end
-
-    context 'when the input is a tar' do
-      after do
-        ls_container.remove
-        committed_image.remove
-      end
-
-      it 'file exists in the container' do
-        skip('Not supported on podman') if ::Docker.podman?
-        subject.archive_in(license_path, '/', overwrite: false)
-        expect(output).to include('LICENSE')
-      end
-    end
-  end
-
-  describe '#archive_in_stream', :docker_1_8 do
-    let(:tar) { StringIO.new(Docker::Util.create_tar('/lol' => 'TEST')) }
-    subject { Docker::Container.create('Image' => 'debian:stable', 'Cmd' => ['/bin/sh']) }
-    let(:committed_image) { subject.commit }
-    let(:ls_container) { committed_image.run('ls /').tap(&:wait) }
-    let(:output) { ls_container.streaming_logs(stdout: true, stderr: true) }
-
-    after do
-      subject.remove
-    end
-
-    context 'when the input is a tar' do
-      after do
-        ls_container.remove
-        committed_image.remove
-      end
-
-      it 'file exists in the container' do
-        skip('Not supported on podman') if ::Docker.podman?
-        subject.archive_in_stream('/', overwrite: false) { tar.read }
-        expect(output).to include('lol')
-      end
-    end
-
-    context 'when the input would overwrite a directory with a file' do
-      let(:tar) { StringIO.new(Docker::Util.create_tar('/etc' => 'TEST')) }
-
-      it 'raises an error' do
-        skip('Not supported on podman') if ::Docker.podman?
-        # Docs say this should return a client error: clearly wrong
-        # https://docs.docker.com/engine/reference/api/docker_remote_api_v1.21/
-        # #extract-an-archive-of-files-or-folders-to-a-directory-in-a-container
-        expect {
-          subject.archive_in_stream('/', overwrite: false) { tar.read }
-        }.to raise_error(Docker::Error::ServerError)
-      end
-    end
-  end
-
-  describe '#archive_out', :docker_1_8 do
-    subject { Docker::Container.create('Image' => 'debian:stable', 'Cmd' => ['touch','/test']) }
-
-    after { subject.remove }
-
-    context 'when the file does not exist' do
-      it 'raises an error' do
-        skip('Not supported on podman') if ::Docker.podman?
-        subject.start
-        subject.wait
-
-        expect { subject.archive_out('/lol') { |chunk| puts chunk } }
-          .to raise_error(Docker::Error::NotFoundError)
-      end
-    end
-
-    context 'when the input is a file' do
-      it 'yields each chunk of the tarred file' do
-        skip('Not supported on podman') if ::Docker.podman?
-        subject.start; subject.wait
-
-        chunks = []
-        subject.archive_out('/test') { |chunk| chunks << chunk }
-        chunks = chunks.join("\n")
-        expect(chunks).to be_include('test')
-      end
-    end
-
-    context 'when the input is a directory' do
-      it 'yields each chunk of the tarred directory' do
-        skip('Not supported on podman') if ::Docker.podman?
-        subject.start; subject.wait
-
-        chunks = []
-        subject.archive_out('/etc/logrotate.d') { |chunk| chunks << chunk }
-        chunks = chunks.join("\n")
-        expect(%w[apt dpkg]).to be_all { |file| chunks.include?(file) }
-      end
-    end
-  end
-
-  describe "#read_file", :docker_1_8 do
-    subject {
-      Docker::Container.create(
-        "Image" => "debian:stable",
-        "Cmd" => ["/bin/bash", "-c", "echo \"Hello world\" > /test"]
-      )
-    }
-
-    after { subject.remove }
-
-    before do
-      subject.start
-      subject.wait
-    end
-
-    it "reads contents from files" do
-      skip('Not supported on podman') if ::Docker.podman?
-      expect(subject.read_file("/test")).to eq "Hello world\n"
-    end
-  end
-
-  describe "#store_file", :docker_1_8 do
-    subject { Docker::Container.create('Image' => 'debian:stable', 'Cmd' => ["ls"]) }
-
-    after { subject.remove }
-
-    it "stores content in files" do
-      skip('Not supported on podman') if ::Docker.podman?
-      subject.store_file("/test", "Hello\nWorld")
-      expect(subject.read_file("/test")).to eq "Hello\nWorld"
-    end
-  end
-
-  describe '#export' do
-    subject { described_class.create('Cmd' => %w[/true],
-                                     'Image' => 'tianon/true') }
-    before { subject.start }
-    after { subject.tap(&:wait).remove }
-
-    it 'yields each chunk' do
-      first = nil
-      subject.export do |chunk|
-        first ||= chunk
-      end
-      expect(first[257..261]).to eq "ustar" # Make sure the export is a tar.
-    end
-  end
-
-  describe '#attach' do
-    subject {
-      described_class.create(
-        'Cmd' => ['bash','-c','sleep 2; echo hello'],
-        'Image' => 'debian:stable'
-      )
-    }
-
-    before { subject.start }
-    after(:each) { subject.stop.remove }
-
-    context 'with normal sized chunks' do
-      it 'yields each chunk' do
-        chunk = nil
-        subject.attach do |stream, c|
-          chunk ||= c
-        end
-        expect(chunk).to eq("hello\n")
-      end
-    end
-
-    context 'with very small chunks' do
-      before do
-        Docker.options = { :chunk_size => 1 }
-      end
-
-      after do
-        Docker.options = {}
-      end
-
-      it 'yields each chunk' do
-        chunk = nil
-        subject.attach do |stream, c|
-          chunk ||= c
-        end
-        expect(chunk).to eq("hello\n")
-      end
-    end
-  end
-
-  describe '#attach with stdin' do
-    it 'yields the output' do
-      skip('Currently broken in podman') if ::Docker.podman?
-      container = described_class.create(
-        'Cmd'       => %w[cat],
-        'Image'     => 'debian:stable',
-        'OpenStdin' => true,
-        'StdinOnce' => true
-      )
-      chunk = nil
-      container
-        .tap(&:start)
-        .attach(stdin: StringIO.new("foo\nbar\n")) do |stream, c|
-          chunk ||= c
-        end
-      container.tap(&:wait).remove
-
-      expect(chunk).to eq("foo\nbar\n")
-    end
-  end
-
-  describe '#start' do
-    subject {
-      described_class.create(
-        'Cmd' => %w[test -d /foo],
-        'Image' => 'debian:stable',
-        'Volumes' => {'/foo' => {}},
-        'HostConfig' => { 'Binds' => ["/tmp:/foo"] }
-      )
-    }
-    let(:all) { Docker::Container.all(all: true) }
-
-    before { subject.start }
-    after(:each) { subject.remove }
-
-    it 'starts the container' do
-      expect(all.map(&:id)).to be_any { |id| id.start_with?(subject.id) }
-      expect(subject.wait(10)['StatusCode']).to be_zero
-    end
-  end
-
-  describe '#stop' do
-    subject {
-      described_class.create('Cmd' => %w[true], 'Image' => 'debian:stable')
-    }
-
-    before { subject.tap(&:start).stop('timeout' => '10') }
-    after { subject.remove }
-
-    it 'stops the container' do
-      expect(described_class.all(:all => true).map(&:id)).to be_any { |id|
-        id.start_with?(subject.id)
-      }
-      expect(described_class.all.map(&:id)).to be_none { |id|
-        id.start_with?(subject.id)
-      }
-    end
-
-    context 'with a timeout' do
-      let(:custom_timeout) { 60 }
-
-      before do
-        subject.tap(&:start)
-      end
-
-      it 'extends the Excon timeout ensuring the request does not timeout before Docker' do
-        expect(subject.connection).to receive(:request).with(
-          :post,
-          anything,
-          anything,
-          hash_including(read_timeout: custom_timeout + 5, write_timeout: custom_timeout + 5)
-        ).once
-        allow(subject.connection).to receive(:request).with(:delete, anything, anything)
-        subject.stop('timeout' => custom_timeout)
-      end
-    end
-
-    context 'without a timeout' do
-      before do
-        subject.tap(&:start)
-      end
-
-      it 'does not adjust the default Excon HTTP timeout' do
-        expect(subject.connection).to receive(:request).with(
-          :post,
-          anything,
-          anything,
-          hash_including(body: '{}')
-        ).once
-        allow(subject.connection).to receive(:request).with(:delete, anything, anything)
-        subject.stop
-      end
-    end
-  end
-
-  describe '#exec' do
-    subject {
-      described_class.create(
-        'Cmd' => %w[sleep 20],
-        'Image' => 'debian:stable'
-      ).start
-    }
-    after { subject.kill!.remove }
-
-    context 'when passed only a command' do
-      let(:output) { subject.exec(['bash','-c','sleep 2; echo hello']) }
-
-      it 'returns the stdout/stderr messages and exit code' do
-        expect(output).to eq([["hello\n"], [], 0])
-      end
-    end
-
-    context 'when detach is true' do
-      let(:output) { subject.exec(['date'], detach: true) }
-
-      it 'returns the Docker::Exec object' do
-        expect(output).to be_a Docker::Exec
-        expect(output.id).to_not be_nil
-      end
-    end
-
-    context 'when passed a block' do
-      it 'streams the stdout/stderr messages' do
-        chunk = nil
-        subject.exec(['bash','-c','sleep 2; echo hello']) do |stream, c|
-          chunk ||= c
-        end
-        expect(chunk).to eq("hello\n")
-      end
-    end
-
-    context 'when stdin object is passed' do
-      let(:output) { subject.exec(['cat'], stdin: StringIO.new("hello")) }
-
-      it 'returns the stdout/stderr messages' do
-        skip('Not supported on podman') if ::Docker.podman?
-        expect(output).to eq([["hello"],[],0])
-      end
-    end
-
-    context 'when tty is true' do
-      let(:command) { [
-        "bash", "-c",
-        "if [ -t 1 ]; then echo -n \"I'm a TTY!\"; fi"
-      ] }
-      let(:output) { subject.exec(command, tty: true) }
-
-      it 'returns the raw stdout/stderr output' do
-        expect(output).to eq([["I'm a TTY!"], [], 0])
-      end
-    end
-  end
-
-  describe '#kill' do
-    let(:command) { ['/bin/bash', '-c', 'while [ 1 ]; do echo hello; done'] }
-    subject {
-      described_class.create('Cmd' => command, 'Image' => 'debian:stable')
-    }
-
-    before { subject.start }
-    after(:each) {subject.remove }
-
-    it 'kills the container' do
-      subject.kill
-      expect(described_class.all.map(&:id)).to be_none { |id|
-        id.start_with?(subject.id)
-      }
-      expect(described_class.all(:all => true).map(&:id)).to be_any { |id|
-        id.start_with?(subject.id)
-      }
-    end
-
-    context 'with a kill signal' do
-      let(:command) {
-        [
-          '/bin/bash',
-          '-c',
-          'trap echo SIGTERM; while [ 1 ]; do echo hello; done'
-        ]
-      }
-      it 'kills the container' do
-        subject.kill(:signal => "SIGTERM")
-        expect(described_class.all.map(&:id)).to be_any { |id|
-          id.start_with?(subject.id)
-        }
-        expect(described_class.all(:all => true).map(&:id)).to be_any { |id|
-          id.start_with?(subject.id)
-        }
-
-        subject.kill(:signal => "SIGKILL")
-        expect(described_class.all.map(&:id)).to be_none { |id|
-          id.start_with?(subject.id)
-        }
-        expect(described_class.all(:all => true).map(&:id)).to be_any { |id|
-          id.start_with?(subject.id)
-        }
-      end
-    end
-  end
-
-  describe '#delete' do
-    subject {
-      described_class.create('Cmd' => ['ls'], 'Image' => 'debian:stable')
-    }
-
-    it 'deletes the container' do
-      subject.delete(:force => true)
-      expect(described_class.all.map(&:id)).to be_none { |id|
-        id.start_with?(subject.id)
-      }
-    end
-  end
-
-  describe '#restart' do
-    subject {
-      described_class.create('Cmd' => %w[sleep 10], 'Image' => 'debian:stable')
-    }
-
-    before { subject.start }
-    after { subject.kill!.remove }
-
-    it 'restarts the container' do
-      expect(described_class.all.map(&:id)).to be_any { |id|
-        id.start_with?(subject.id)
-      }
-      subject.stop
-      expect(described_class.all.map(&:id)).to be_none { |id|
-        id.start_with?(subject.id)
-      }
-      subject.restart('timeout' => '10')
-      expect(described_class.all.map(&:id)).to be_any { |id|
-        id.start_with?(subject.id)
-      }
-    end
-  end
-
-  describe '#pause' do
-    subject {
-      described_class.create(
-        'Cmd' => %w[sleep 50],
-        'Image' => 'debian:stable'
-      ).start
-    }
-    after { subject.unpause.kill!.remove }
-
-    it 'pauses the container' do
-      skip('Not supported on rootless podman') if (::Docker.podman? && ::Docker.rootless?)
-      subject.pause
-      expect(described_class.get(subject.id).info['State']['Paused']).to be true
-    end
-  end
-
-  describe '#unpause' do
-    subject {
-      described_class.create(
-        'Cmd' => %w[sleep 50],
-        'Image' => 'debian:stable'
-      ).start
-    }
-    before { subject.pause }
-    after { subject.kill!.remove }
-
-    it 'unpauses the container' do
-      subject.unpause
-      expect(
-        described_class.get(subject.id).info['State']['Paused']
-      ).to be false
-    end
-  end
-
-  describe '#wait' do
-    subject {
-      described_class.create(
-        'Cmd' => %w[tar nonsense],
-        'Image' => 'debian:stable'
-      )
-    }
-
-    before { subject.start }
-    after(:each) { subject.remove }
-
-    it 'waits for the command to finish' do
-      expect(subject.wait['StatusCode']).to_not be_zero
-    end
-
-    context 'when an argument is given' do
-      subject { described_class.create('Cmd' => %w[sleep 5],
-                                       'Image' => 'debian:stable') }
-
-      it 'sets the :read_timeout to that amount of time' do
-        expect(subject.wait(6)['StatusCode']).to be_zero
-      end
-
-      context 'and a command runs for too long' do
-        it 'raises a ServerError' do
-          expect{subject.wait(4)}.to raise_error(Docker::Error::TimeoutError)
-          subject.tap(&:wait)
-        end
-      end
-    end
-  end
-
-  describe '#run' do
-    let(:run_command) { subject.run('ls') }
-
-    context 'when the Container\'s command does not return status code of 0' do
-      subject { described_class.create('Cmd' => %w[false],
-                                       'Image' => 'debian:stable') }
-
-      after do
-        subject.remove
-      end
-
-      it 'raises an error' do
-        expect { run_command }
-            .to raise_error(Docker::Error::UnexpectedResponseError)
-      end
-    end
-
-    context 'when the Container\'s command returns a status code of 0' do
-      subject { described_class.create('Cmd' => %w[pwd],
-                                       'Image' => 'debian:stable') }
-      after do
-        subject.remove
-        image = run_command.json['Image']
-        run_command.remove
-        Docker::Image.get(image).history.each do |layer|
-          next unless layer['CreatedBy'] == 'pwd'
-          Docker::Image.get(layer['Id']).remove(:noprune => true)
-        end
-      end
-
-      it 'creates a new container to run the specified command' do
-        expect(run_command.wait['StatusCode']).to be_zero
-      end
-    end
-  end
-
-  describe '#commit' do
-    subject {
-      described_class.create('Cmd' => %w[true], 'Image' => 'debian:stable')
-    }
-    let(:image) { subject.commit }
-
-    after(:each) do
-      subject.remove
-      image.remove
-    end
-
-    it 'creates a new Image from the  Container\'s changes' do
-      subject.tap(&:start).wait
-
-      expect(image).to be_a Docker::Image
-      expect(image.id).to_not be_nil
-    end
-
-    context 'if run is passed, it saves the command in the image' do
-      let(:image) { subject.commit }
-      let(:container) { image.run('pwd') }
-
-      it 'saves the command' do
-        skip('Not supported on podman') if ::Docker.podman?
-        container.wait
-        expect(container.attach(logs: true, stream: false)).to eql [["/\n"],[]]
-        container.remove
-      end
-    end
-  end
-
-  describe '.create' do
-    subject { described_class }
-
-    context 'when the Container does not yet exist' do
-      context 'when the HTTP request does not return a 200' do
-        before do
-          Docker.options = { :mock => true }
-          Excon.stub({ :method => :post }, { :status => 400 })
-        end
-        after do
-          Excon.stubs.shift
-          Docker.options = {}
-        end
-
-        it 'raises an error' do
-          expect { subject.create }.to raise_error(Docker::Error::ClientError)
-        end
-      end
-
-      context 'when the HTTP request returns a 200' do
-        let(:options) do
-          {
-            "Cmd"          => ["date"],
-            "Image"        => "debian:stable",
-          }
-        end
-        let(:container) { subject.create(options) }
-        after { container.remove }
-
-        it 'sets the id' do
-          expect(container).to be_a Docker::Container
-          expect(container.id).to_not be_nil
-          expect(container.connection).to_not be_nil
-        end
-      end
-    end
-  end
-
-  describe '.get' do
-    subject { described_class }
-
-    context 'when the HTTP response is not a 200' do
-      before do
-        Docker.options = { :mock => true }
-        Excon.stub({ :method => :get }, { :status => 500 })
-      end
-      after do
-        Excon.stubs.shift
-        Docker.options = {}
-      end
-
-      it 'raises an error' do
-        expect { subject.get('randomID') }
-            .to raise_error(Docker::Error::ServerError)
-      end
-    end
-
-    context 'when the HTTP response is a 200' do
-      let(:container) {
-        subject.create('Cmd' => ['ls'], 'Image' => 'debian:stable')
-      }
-      after { container.remove }
-
-      it 'materializes the Container into a Docker::Container' do
-        expect(subject.get(container.id)).to be_a Docker::Container
-      end
-    end
-
-  end
-
-  describe '.all' do
-    subject { described_class }
-
-    context 'when the HTTP response is not a 200' do
-      before do
-        Docker.options = { :mock => true }
-        Excon.stub({ :method => :get }, { :status => 500 })
-      end
-      after do
-        Excon.stubs.shift
-        Docker.options = {}
-      end
-
-      it 'raises an error' do
-        expect { subject.all }
-            .to raise_error(Docker::Error::ServerError)
-      end
-    end
-
-    context 'when the HTTP response is a 200' do
-      let(:container) {
-        subject.create('Cmd' => ['ls'], 'Image' => 'debian:stable')
-      }
-      before { container }
-      after { container.remove }
-
-      it 'materializes each Container into a Docker::Container' do
-        expect(subject.all(:all => true)).to be_all { |container|
-          container.is_a?(Docker::Container)
-        }
-        expect(subject.all(:all => true).length).to_not be_zero
-      end
-    end
-  end
-
-  describe '.prune', :docker_17_03 => true do
-    it 'prune containers' do
-      expect { Docker::Container.prune }.not_to raise_error
-    end
-  end
-end
diff -pruN 2.2.0-2/spec/docker/event_spec.rb 2.4.0-1/spec/docker/event_spec.rb
--- 2.2.0-2/spec/docker/event_spec.rb	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/spec/docker/event_spec.rb	1970-01-01 00:00:00.000000000 +0000
@@ -1,161 +0,0 @@
-require 'spec_helper'
-
-SingleCov.covered! uncovered: 5
-
-describe Docker::Event do
-  let(:api_response) do
-    {
-      'Action' => 'start',
-      'Actor' => {
-        'Attributes' => {
-          'image' => 'tianon/true',
-          'name' => 'true-dat'
-        },
-        'ID' => 'bb2c783a32330b726f18d1eb44d80c899ef45771b4f939326e0fefcfc7e05db8'
-      },
-      'Type' => 'container',
-      'from' => 'tianon/true',
-      'id' => 'bb2c783a32330b726f18d1eb44d80c899ef45771b4f939326e0fefcfc7e05db8',
-      'status' => 'start',
-      'time' => 1461083270,
-      'timeNano' => 1461083270652069004
-    }
-  end
-
-  describe "#to_s" do
-    context 'with an old event' do
-      let(:event) do
-        described_class.new(
-          status: status,
-          id: id,
-          from: from,
-          time: time
-        )
-      end
-
-      let(:status) { "start" }
-      let(:id) { "398c9f77b5d2" }
-      let(:from) { "debian:stable" }
-      let(:time) { 1381956164 }
-
-      let(:expected_string) {
-        "Docker::Event { #{time} #{status} #{id} (from=#{from}) }"
-      }
-
-      it "equals the expected string" do
-        expect(event.to_s).to eq(expected_string)
-      end
-    end
-
-    context 'with a new event' do
-      let(:event) { described_class.new(api_response) }
-
-      let(:expected_string) do
-        'Docker::Event { 1461083270652069004 container start '\
-        'bb2c783a32330b726f18d1eb44d80c899ef45771b4f939326e0fefcfc7e05db8 '\
-        '(image=tianon/true, name=true-dat) }'
-      end
-
-      it 'equals the expected string' do
-        expect(event.to_s).to eq(expected_string)
-      end
-    end
-  end
-
-  describe ".stream" do
-    it 'receives at least 4 events' do
-      events = 0
-
-      stream_thread = Thread.new do
-        Docker::Event.stream do |event|
-          puts "#{event}"
-          events += 1
-
-          break if events >= 4
-        end
-      end
-
-      container = Docker::Image.create('fromImage' => 'debian:stable')
-        .run('bash')
-        .tap(&:wait)
-
-      stream_thread.join(10) || stream_thread.kill
-
-      expect(events).to be >= 4
-
-      container.remove
-    end
-  end
-
-  describe ".since" do
-    let(:time) { Time.now.to_i + 1 }
-
-    it 'receives at least 4 events' do
-      skip('Not supported on podman') if ::Docker.podman?
-      events = 0
-
-      stream_thread = Thread.new do
-        Docker::Event.since(time) do |event|
-          puts "#{event}"
-          events += 1
-
-          break if events >= 4
-        end
-      end
-
-      container = Docker::Image.create('fromImage' => 'debian:stable')
-        .run('bash')
-        .tap(&:wait)
-
-      stream_thread.join(10) || stream_thread.kill
-
-      expect(events).to be >= 4
-
-      container.remove
-    end
-  end
-
-  describe ".new_event" do
-    context 'with an old api response' do
-      let(:event) { Docker::Event.new_event(response_body, nil, nil) }
-      let(:status) { "start" }
-      let(:id) { "398c9f77b5d2" }
-      let(:from) { "debian:stable" }
-      let(:time) { 1381956164 }
-      let(:response_body) {
-        "{\"status\":\"#{status}\",\"id\":\"#{id}\""\
-        ",\"from\":\"#{from}\",\"time\":#{time}}"
-      }
-
-      it "returns a Docker::Event" do
-        expect(event).to be_kind_of(Docker::Event)
-        expect(event.status).to eq(status)
-        expect(event.id).to eq(id)
-        expect(event.from).to eq(from)
-        expect(event.time).to eq(time)
-      end
-    end
-
-    context 'with a new api response' do
-      let(:event) do
-        Docker::Event.new_event(
-          MultiJson.dump(api_response),
-          nil,
-          nil
-        )
-      end
-
-      it 'returns a Docker::Event' do
-        expect(event).to be_kind_of(Docker::Event)
-        expect(event.type).to eq('container')
-        expect(event.action).to eq('start')
-        expect(
-          event.actor.id
-        ).to eq('bb2c783a32330b726f18d1eb44d80c899ef45771b4f939326e0fefcfc7e05db8')
-        expect(event.actor.attributes).to eq('image' => 'tianon/true', 'name' => 'true-dat')
-        expect(event.time).to eq 1461083270
-        expect(event.time_nano).to eq 1461083270652069004
-      end
-    end
-  end
-end
diff -pruN 2.2.0-2/spec/docker/exec_spec.rb 2.4.0-1/spec/docker/exec_spec.rb
--- 2.2.0-2/spec/docker/exec_spec.rb	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/spec/docker/exec_spec.rb	1970-01-01 00:00:00.000000000 +0000
@@ -1,184 +0,0 @@
-require 'spec_helper'
-
-SingleCov.covered! uncovered: 5
-
-describe Docker::Exec do
-  let(:container) {
-    Docker::Container.create(
-      'Cmd' => %w(sleep 300),
-      'Image' => 'debian:stable'
-    ).start!
-  }
-
-  describe '#to_s' do
-    subject {
-      described_class.send(:new, Docker.connection, 'id' => rand(10000).to_s)
-    }
-
-    let(:id) { 'bf119e2' }
-    let(:connection) { Docker.connection }
-    let(:expected_string) {
-      "Docker::Exec { :id => #{id}, :connection => #{connection} }"
-    }
-    before do
-      {
-        :@id => id,
-        :@connection => connection
-      }.each { |k, v| subject.instance_variable_set(k, v) }
-    end
-
-    its(:to_s) { should == expected_string }
-  end
-
-  describe '.create' do
-    subject { described_class }
-
-    context 'when the HTTP request returns a 201' do
-      let(:options) do
-        {
-          'AttachStdin' => false,
-          'AttachStdout' => false,
-          'AttachStderr' => false,
-          'Tty' => false,
-          'Cmd' => [
-            'date'
-          ],
-          'Container' => container.id
-        }
-      end
-      let(:process) { subject.create(options) }
-      after { container.kill!.remove }
-
-      it 'sets the id' do
-        expect(process).to be_a Docker::Exec
-        expect(process.id).to_not be_nil
-        expect(process.connection).to_not be_nil
-      end
-    end
-
-    context 'when the parent container does not exist' do
-      before do
-        Docker.options = { :mock => true }
-        Excon.stub({ :method => :get}, { :status => 404 }) # For Podman
-        Excon.stub({ :method => :post}, { :status => 404 })
-      end
-      after do
-        Excon.stubs.shift
-        Docker.options = {}
-      end
-
-      it 'raises an error' do
-        expect { subject.create }.to raise_error(Docker::Error::NotFoundError)
-      end
-    end
-  end
-
-  describe '#json' do
-    subject {
-      described_class.create(
-        'Container' => container.id,
-        'Detach' => true,
-        'Cmd' => %w[true]
-      )
-    }
-
-    let(:description) { subject.json }
-    before { subject.start! }
-    after { container.kill!.remove }
-
-    it 'returns the description as a Hash' do
-      expect(description).to be_a Hash
-      expect(description['ID']).to start_with(subject.id)
-    end
-  end
-
-  describe '#start!' do
-    context 'when the exec instance does not exist' do
-      subject do
-        described_class.send(:new, Docker.connection, 'id' => rand(10000).to_s)
-      end
-
-      it 'raises an error' do
-        expect { subject.start! }.to raise_error(Docker::Error::NotFoundError)
-      end
-    end
-
-    context 'when :detach is set to false' do
-      subject {
-        described_class.create(
-          'Container' => container.id,
-          'AttachStdout' => true,
-          'Cmd' => ['bash','-c','sleep 2; echo hello']
-        )
-      }
-      after { container.kill!.remove }
-
-      it 'returns the stdout and stderr messages' do
-        expect(subject.start!).to eq([["hello\n"],[],0])
-      end
-
-      context 'block is passed' do
-        it 'attaches to the stream' do
-          chunk = nil
-          result = subject.start! do |stream, c|
-            chunk ||= c
-          end
-          expect(chunk).to eq("hello\n")
-          expect(result).to eq([["hello\n"], [], 0])
-        end
-      end
-    end
-
-    context 'when :detach is set to true' do
-      subject {
-        described_class.create('Container' => container.id, 'Cmd' => %w[date])
-      }
-      after { container.kill!.remove }
-
-      it 'returns empty stdout/stderr messages with exitcode' do
-        expect(subject.start!(:detach => true).length).to eq(3)
-      end
-    end
-
-    context 'when :wait set long time value' do
-      subject {
-        described_class.create(
-          'Container' => container.id,
-          'AttachStdout' => true,
-          'Cmd' => %w[true]
-        )
-      }
-      after { container.kill!.remove }
-
-      it 'returns empty stdout and stderr messages with exitcode' do
-        expect(subject.start!(:wait => 100)).to eq([[], [], 0])
-      end
-    end
-
-    context 'when :wait set short time value' do
-      subject {
-        described_class.create(
-            'Container'    => container.id,
-            'AttachStdout' => true,
-            'Cmd'          => ['bash', '-c', 'sleep 2; echo hello']
-        )
-      }
-      after { container.kill!.remove }
-
-      it 'raises an error' do
-        expect { subject.start!(:wait => 1) }.to raise_error(Docker::Error::TimeoutError)
-      end
-    end
-
-    context 'when the HTTP request returns a 201' do
-      subject {
-        described_class.create('Container' => container.id, 'Cmd' => ['date'])
-      }
-      after { container.kill!.remove }
-
-      it 'starts the exec instance' do
-        expect { subject.start! }.not_to raise_error
-      end
-    end
-  end
-end
diff -pruN 2.2.0-2/spec/docker/image_spec.rb 2.4.0-1/spec/docker/image_spec.rb
--- 2.2.0-2/spec/docker/image_spec.rb	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/spec/docker/image_spec.rb	1970-01-01 00:00:00.000000000 +0000
@@ -1,828 +0,0 @@
-require 'spec_helper'
-
-SingleCov.covered! uncovered: 16
-
-describe Docker::Image do
-  describe '#to_s' do
-    subject { described_class.new(Docker.connection, info) }
-
-    let(:id) { 'bf119e2' }
-    let(:connection) { Docker.connection }
-
-    let(:info) do
-      {"id" => "bf119e2", "Repository" => "debian", "Tag" => "stable",
-        "Created" => 1364102658, "Size" => 24653, "VirtualSize" => 180116135}
-    end
-
-    let(:expected_string) do
-      "Docker::Image { :id => #{id}, :info => #{info.inspect}, "\
-        ":connection => #{connection} }"
-    end
-
-    its(:to_s) { should == expected_string }
-  end
-
-  describe '#remove' do
-    context 'when no name is given' do
-      let(:id) { subject.id }
-      subject { described_class.create('fromImage' => 'busybox:latest') }
-      after { described_class.create('fromImage' => 'busybox:latest') }
-
-      it 'removes the Image' do
-        subject.remove(:force => true)
-        expect(Docker::Image.all.map(&:id)).to_not include(id)
-      end
-    end
-
-    context 'when using the class' do
-      let(:id) { subject.id }
-      subject { described_class.create('fromImage' => 'busybox:latest') }
-      after { described_class.create('fromImage' => 'busybox:latest') }
-
-      it 'removes the Image' do
-        Docker::Image.remove(id, force: true)
-        expect(Docker::Image.all.map(&:id)).to_not include(id)
-      end
-    end
-
-    context 'when a valid tag is given' do
-      it 'untags the Image'
-    end
-
-    context 'when an invalid tag is given' do
-      it 'raises an error'
-    end
-  end
-
-  describe '#insert_local' do
-    include_context "local paths"
-
-    subject { described_class.create('fromImage' => 'debian:stable') }
-
-    let(:rm) { false }
-    let(:new_image) {
-      opts = {'localPath' => file, 'outputPath' => '/'}
-      opts[:rm] = true if rm
-      subject.insert_local(opts)
-    }
-
-    context 'when the local file does not exist' do
-      let(:file) { '/lol/not/a/file' }
-
-      it 'raises an error' do
-        expect { new_image }.to raise_error(Docker::Error::ArgumentError)
-      end
-    end
-
-    context 'when the local file does exist' do
-      let(:file) { File.join(project_dir, 'Gemfile') }
-      let(:gemfile) { File.read('Gemfile') }
-      let(:container) { new_image.run('cat /Gemfile').tap(&:wait) }
-      after do
-        container.remove
-        new_image.remove
-      end
-
-      it 'creates a new Image that has that file' do
-        begin
-          output = container.streaming_logs(stdout: true)
-          expect(output).to eq(gemfile)
-        rescue Docker::Error::UnexpectedResponseError => ex
-          skip("Could not communicate with DockerHub: #{ex}")
-        end
-      end
-    end
-
-    context 'when a directory is passed' do
-      let(:new_image) {
-        subject.insert_local(
-          'localPath' => File.join(project_dir, 'lib'),
-          'outputPath' => '/lib'
-        )
-      }
-      let(:container) { new_image.run('ls -a /lib/docker') }
-      let(:response) { container.tap(&:wait).streaming_logs(stdout: true) }
-      after do
-        container.tap(&:wait).remove
-        new_image.remove
-      end
-
-      it 'inserts the directory' do
-        begin
-          expect(response.split("\n").sort).to eq(Dir.entries('lib/docker').sort)
-        rescue Docker::Error::UnexpectedResponseError => ex
-          skip("Could not communicate with DockerHub: #{ex}")
-        end
-      end
-    end
-
-    context 'when there are multiple files passed' do
-      let(:file) {
-        [File.join(project_dir, 'Gemfile'), File.join(project_dir, 'LICENSE')]
-      }
-      let(:gemfile) { File.read('Gemfile') }
-      let(:license) { File.read('LICENSE') }
-      let(:container) { new_image.run('cat /Gemfile /LICENSE') }
-      let(:response) {
-        container.tap(&:wait).streaming_logs(stdout: true)
-      }
-      after do
-        container.remove
-        new_image.remove
-      end
-
-      it 'creates a new Image that has each file' do
-        begin
-          expect(response).to eq("#{gemfile}#{license}")
-        rescue Docker::Error::UnexpectedResponseError => ex
-          skip("Could not communicate with DockerHub: #{ex}")
-        end
-      end
-    end
-
-    context 'when removing intermediate containers' do
-      let(:rm) { true }
-      let(:file) { File.join(project_dir, 'Gemfile') }
-      after(:each) { new_image.remove }
-
-      it 'leave no intermediate containers' do
-        begin
-          expect { new_image }.to change {
-            Docker::Container.all(:all => true).count
-          }.by 0
-        rescue Docker::Error::UnexpectedResponseError => ex
-          skip("Could not communicate with DockerHub: #{ex}")
-        end
-      end
-
-      it 'creates a new image' do
-        begin
-          expect{new_image}.to change{Docker::Image.all.count}.by 1
-        rescue Docker::Error::UnexpectedResponseError => ex
-          skip("Could not communicate with DockerHub: #{ex}")
-        end
-      end
-    end
-  end
-
-  describe '#push' do
-    let(:credentials) {
-      {
-        'username' => ENV['DOCKER_API_USER'],
-        'password' => ENV['DOCKER_API_PASS'],
-        'serveraddress' => 'https://index.docker.io/v1',
-        'email'    => ENV['DOCKER_API_EMAIL']
-      }
-    }
-    let(:repo_tag) { "#{ENV['DOCKER_API_USER']}/true" }
-    let(:image) {
-      described_class.build("FROM tianon/true\n", "t" => repo_tag).refresh!
-    }
-    after { image.remove(:name => repo_tag, :noprune => true) }
-
-    it 'pushes the Image' do
-      skip_without_auth
-      image.push(credentials)
-    end
-
-    it 'streams output from push' do
-      skip_without_auth
-      expect { |b| image.push(credentials, &b) }
-        .to yield_control.at_least(1)
-    end
-
-    context 'when a tag is specified' do
-      it 'pushes that specific tag'
-    end
-
-    context 'when the image was retrived by get' do
-      let(:image) {
-        described_class.build("FROM tianon/true\n", "t" => repo_tag).refresh!
-        described_class.get(repo_tag)
-      }
-
-      context 'when no tag is specified' do
-        it 'looks up the first repo tag' do
-          skip_without_auth
-          expect { image.push }.to_not raise_error
-        end
-      end
-    end
-
-    context 'when there are no credentials' do
-      let(:credentials) { nil }
-      let(:repo_tag) { "localhost:5000/true" }
-
-      it 'still pushes' do
-        begin
-          image.push
-        rescue => ex
-          if ex.message =~ /connection refused/
-            skip("Registry at #{repo_tag} is not available")
-          else
-            expect { raise(ex) }.to_not raise_error
-          end
-        end
-      end
-    end
-  end
-
-  describe '#tag' do
-    subject { described_class.create('fromImage' => 'debian:stable') }
-    after { subject.remove(:name => 'teh:latest', :noprune => true) }
-
-    it 'tags the image with the repo name' do
-      subject.tag(:repo => :teh, :force => true)
-      expect(subject.info['RepoTags']).to include 'teh:latest'
-    end
-  end
-
-  describe '#json' do
-    before { skip_without_auth }
-
-    subject { described_class.create('fromImage' => 'debian:stable') }
-    let(:json) { subject.json }
-
-    it 'returns additional information about image image' do
-      expect(json).to be_a Hash
-      expect(json.length).to_not be_zero
-    end
-  end
-
-  describe '#history' do
-    subject { described_class.create('fromImage' => 'debian:stable') }
-    let(:history) { subject.history }
-
-    it 'returns the history of the Image' do
-      expect(history).to be_a Array
-      expect(history.length).to_not be_zero
-      expect(history).to be_all { |elem| elem.is_a? Hash }
-    end
-  end
-
-  describe '#run' do
-    let(:cmd) { nil }
-    let(:options) { {} }
-
-    subject do
-      described_class.create(
-        {'fromImage' => 'debian:stable'})
-    end
-
-    let(:container) { subject.run(cmd, options).tap(&:wait) }
-    let(:output) { container.streaming_logs(stdout: true) }
-
-    context 'when cmd is a String' do
-      let(:cmd) { 'ls /lib64/' }
-      after { container.remove }
-
-      it 'splits the String by spaces and creates a new Container' do
-        expect(output).to eq("ld-linux-x86-64.so.2\n")
-      end
-    end
-
-    context 'when cmd is an Array' do
-      let(:cmd) { %w[which pwd] }
-      after { container.remove }
-
-      it 'creates a new Container' do
-        expect(output).to eq("/bin/pwd\n")
-      end
-    end
-
-    context 'when cmd is nil', docker_1_12: true do
-      let(:cmd) { nil }
-
-      context 'no command configured in image' do
-        subject { described_class.create('fromImage' => 'swipely/base') }
-        it 'should raise an error if no command is specified' do
-          begin
-            container
-          rescue => ex
-            expect([Docker::Error::ServerError, Docker::Error::ClientError]).to include(ex.class)
-            expect(ex.message).to match(/No\ command\ specified/)
-          end
-        end
-      end
-    end
-
-    context "command configured in image" do
-      let(:cmd) { 'pwd' }
-      after { container.remove }
-
-      it 'should normally show result if image has Cmd configured' do
-        expect(output).to eql "/\n"
-      end
-    end
-
-    context 'when using cpu shares' do
-      let(:options) { { 'CpuShares' => 50 } }
-      after { container.remove }
-
-      it 'returns 50' do
-        skip('Not supported on podman') if ::Docker.podman?
-        expect(container.json["HostConfig"]["CpuShares"]).to eq 50
-      end
-    end
-  end
-
-  describe '#save' do
-    let(:image) { Docker::Image.get('busybox') }
-
-    it 'calls the class method' do
-      expect(Docker::Image).to receive(:save)
-        .with(image.id, 'busybox.tar', anything)
-      image.save('busybox.tar')
-    end
-  end
-
-  describe '#save_stream' do
-    let(:image) { Docker::Image.get('busybox') }
-    let(:block) { proc { |chunk| puts chunk } }
-
-    it 'calls the class method' do
-      expect(Docker::Image).to receive(:save_stream)
-        .with(image.id, instance_of(Hash), instance_of(Docker::Connection))
-      image.save_stream(:chunk_size => 1024 * 1024, &block)
-    end
-  end
-
-  describe '#refresh!' do
-    let(:image) { Docker::Image.create('fromImage' => 'debian:stable') }
-
-    it 'updates the @info hash' do
-      size = image.info.size
-      image.refresh!
-      expect(image.info.size).to be > size
-    end
-
-    context 'with an explicit connection' do
-      let(:connection) { Docker::Connection.new(Docker.url, Docker.options) }
-      let(:image) {
-        Docker::Image.create({'fromImage' => 'debian:stable'}, nil, connection)
-      }
-
-      it 'updates using the provided connection' do
-        image.refresh!
-      end
-    end
-  end
-
-  describe '.load' do
-    include_context "local paths"
-    let(:file) { File.join(project_dir, 'spec', 'fixtures', 'load.tar') }
-
-    context 'when the argument is a String' do
-      it 'loads tianon/true image from the file system' do
-        result = Docker::Image.load(file)
-        expect(result).to eq("")
-      end
-    end
-
-    context 'when the argument is an IO' do
-      let(:io) { File.open(file) }
-
-      after { io.close }
-
-      it 'loads tinan/true image from the IO' do
-        result = Docker::Image.load(io)
-        expect(result).to eq("")
-      end
-    end
-  end
-
-  describe '.create' do
-    subject { described_class }
-
-    context 'when the Image does not yet exist and the body is a Hash' do
-      let(:image) { subject.create('fromImage' => 'swipely/base') }
-      let(:creds) {
-        {
-          :username => ENV['DOCKER_API_USER'],
-          :password => ENV['DOCKER_API_PASS'],
-          :email => ENV['DOCKER_API_EMAIL']
-        }
-      }
-
-      before do
-        skip_without_auth
-        Docker::Image.create('fromImage' => 'swipely/base').remove
-      end
-      after { Docker::Image.create('fromImage' => 'swipely/base') }
-
-      it 'sets the id and sends Docker.creds' do
-        allow(Docker).to receive(:creds).and_return(creds)
-        expect(image).to be_a Docker::Image
-        expect(image.id).to match(/\A(sha256:)?[a-fA-F0-9]+\Z/)
-        expect(image.id).to_not include('base')
-        expect(image.id).to_not be_nil
-        expect(image.id).to_not be_empty
-      end
-    end
-
-    context 'image with tag' do
-      it 'pulls the image (string arguments)' do
-        image = subject.create('fromImage' => 'busybox', 'tag' => 'uclibc')
-        image.refresh!
-        expect(image.info['RepoTags']).to include(/busybox:uclibc$/)
-      end
-
-      it 'pulls the image (symbol arguments)' do
-        image = subject.create(fromImage: 'busybox', tag: 'uclibc')
-        image.refresh!
-        expect(image.info['RepoTags']).to include(/busybox:uclibc$/)
-      end
-
-      it 'supports identical fromImage and tag', docker_1_10: true do
-        # This is here for backwards compatibility. docker-api used to
-        # complete ignore the "tag" argument, which Docker itself prioritizes
-        # over a tag found in fromImage, which meant that we had 3 scenarios:
-        #
-        # 1 fromImage does not include a tag, and the tag argument is provided
-        #   and isn't the default (i.e. "latest"): docker-api crashes looking
-        #   for fromImage when the image that was pulled is fromImage:tag (or
-        #   returns the wrong image if fromImage:latest exists)
-        # 2 fromImage does not a include a tag, and the tag argument is absent
-        #   or default (i.e. "latest"): docker-api finds the right image.
-        # 3 fromImage includes a tag, and the tag argument is absent: docker-api
-        #   also finds the right image.
-        # 4 fromImage includes a tag, and the tag argument is present: works if
-        #   the tag is the same in both.
-        #
-        # Adding support for the tag argument to fix 1 above means we'd break 4
-        # if we didn't explicitly handle the case where both tags are identical.
-        # This is what this test checks.
-        #
-        # Note that providing the tag inline in fromImage is only supported in
-        # Docker 1.10 and up.
-        skip('Not supported on podman') if ::Docker.podman?
-        image = subject.create(fromImage: 'busybox:uclibc', tag: 'uclibc')
-        image.refresh!
-        expect(image.info['RepoTags']).to include('busybox:uclibc')
-      end
-    end
-
-    context 'with a block capturing create output' do
-      let(:create_output) { "" }
-      let(:block) { Proc.new { |chunk| create_output << chunk } }
-
-      before do
-        Docker.creds = nil
-        subject.create('fromImage' => 'busybox').remove(force: true)
-      end
-
-      it 'calls the block and passes build output' do
-        subject.create('fromImage' => 'busybox', &block)
-        expect(create_output).to match(/ulling.*busybox/)
-      end
-    end
-  end
-
-  describe '.get' do
-    subject { described_class }
-    let(:image) { subject.get(image_name) }
-
-    context 'when the image does exist' do
-      let(:image_name) { 'debian:stable' }
-
-      it 'returns the new image' do
-        expect(image).to be_a Docker::Image
-      end
-    end
-
-    context 'when the image does not exist' do
-      let(:image_name) { 'abcdefghijkl' }
-
-      before do
-        Docker.options = { :mock => true }
-        Excon.stub({ :method => :get }, { :status => 404 })
-      end
-
-      after do
-        Docker.options = {}
-        Excon.stubs.shift
-      end
-
-      it 'raises a not found error' do
-        expect { image }.to raise_error(Docker::Error::NotFoundError)
-      end
-    end
-  end
-
-  describe '.save' do
-    include_context "local paths"
-
-    context 'when a filename is specified' do
-      let(:file) { "#{project_dir}/scratch.tar" }
-      after { FileUtils.remove(file) }
-
-      it 'exports tarball of image to specified file' do
-        Docker::Image.save('swipely/base', file)
-        expect(File.exist?(file)).to eq true
-        expect(File.read(file)).to_not be_nil
-      end
-    end
-
-    context 'when no filename is specified' do
-      it 'returns raw binary data as string' do
-        raw = Docker::Image.save('swipely/base')
-        expect(raw).to_not be_nil
-      end
-    end
-  end
-
-  describe '.save_stream' do
-    let(:image) { 'busybox:latest' }
-    let(:non_streamed) do
-      Docker.connection.get('/images/get', 'names' => image)
-    end
-    let(:streamed) { '' }
-    let(:tar_files) do
-      proc do |string|
-        Gem::Package::TarReader
-          .new(StringIO.new(string, 'rb'))
-          .map(&:full_name)
-          .sort
-      end
-    end
-
-    it 'yields each chunk of the image' do
-      Docker::Image.save_stream(image) { |chunk| streamed << chunk }
-      expect(tar_files.call(streamed)).to eq(tar_files.call(non_streamed))
-    end
-  end
-
-  describe '.exist?' do
-    subject { described_class }
-    let(:exists) { subject.exist?(image_name) }
-
-    context 'when the image does exist' do
-      let(:image_name) { 'debian:stable' }
-
-      it 'returns true' do
-        expect(exists).to eq(true)
-      end
-    end
-
-    context 'when the image does not exist' do
-      let(:image_name) { 'abcdefghijkl' }
-
-      before do
-        Docker.options = { :mock => true }
-        Excon.stub({ :method => :get }, { :status => 404 })
-      end
-
-      after do
-        Docker.options = {}
-        Excon.stubs.shift
-      end
-
-      it 'return false' do
-        expect(exists).to eq(false)
-      end
-    end
-  end
-
-  describe '.import' do
-    include_context "local paths"
-
-    subject { described_class }
-
-    context 'when the file does not exist' do
-      let(:file) { '/lol/not/a/file' }
-
-      it 'raises an error' do
-        expect { subject.import(file) }
-          .to raise_error(Docker::Error::IOError)
-      end
-    end
-
-    context 'when the file does exist' do
-      let(:file) { File.join(project_dir, 'spec', 'fixtures', 'export.tar') }
-      let(:import) { subject.import(file) }
-      after { import.remove(:noprune => true) }
-
-      it 'creates the Image' do
-        expect(import).to be_a Docker::Image
-        expect(import.id).to_not be_nil
-      end
-    end
-
-    context 'when the argument is a URI' do
-      context 'when the URI is invalid' do
-        it 'raises an error' do
-          expect { subject.import('http://google.com') }
-            .to raise_error(Docker::Error::IOError)
-        end
-      end
-
-      context 'when the URI is valid' do
-        let(:uri) { 'http://swipely-pub.s3.amazonaws.com/tianon_true.tar' }
-        let(:import) { subject.import(uri) }
-        after { import.remove(:noprune => true) }
-
-        it 'returns an Image' do
-          expect(import).to be_a Docker::Image
-          expect(import.id).to_not be_nil
-        end
-      end
-    end
-  end
-
-  describe '.all' do
-    subject { described_class }
-
-    let(:images) { subject.all(:all => true) }
-    before { subject.create('fromImage' => 'debian:stable') }
-
-    it 'materializes each Image into a Docker::Image' do
-      images.each do |image|
-        expect(image).to_not be_nil
-
-        expect(image).to be_a(described_class)
-
-        expect(image.id).to_not be_nil
-
-        expected = [
-          'Created',
-          'Size'
-        ]
-
-        expected << 'VirtualSize' unless ::Docker.podman?
-
-        expected.each do |key|
-          expect(image.info).to have_key(key)
-        end
-      end
-
-      expect(images.length).to_not be_zero
-    end
-  end
-
-  describe '.prune', :docker_17_03 => true do
-    it 'prune images' do
-      expect { Docker::Image.prune }.not_to raise_error
-    end
-  end
-
-  unless ::Docker.podman?
-    describe '.search' do
-      subject { described_class }
-
-      it 'materializes each Image into a Docker::Image' do
-        expect(subject.search('term' => 'sshd')).to be_all { |image|
-          !image.id.nil? && image.is_a?(described_class)
-        }
-      end
-    end
-  end
-
-  describe '.build' do
-    subject { described_class }
-    context 'with an invalid Dockerfile' do
-      if ::Docker.podman?
-        it 'throws a UnexpectedResponseError' do
-          expect { subject.build('lololol') }
-              .to raise_error(Docker::Error::UnexpectedResponseError)
-        end
-      else
-        it 'throws a UnexpectedResponseError', docker_17_09: false do
-          expect { subject.build('lololol') }
-              .to raise_error(Docker::Error::ClientError)
-        end
-
-        it 'throws a ClientError', docker_17_09: true do
-            expect { subject.build('lololol') }
-                .to raise_error(Docker::Error::ClientError)
-        end
-      end
-    end
-
-    context 'with a valid Dockerfile' do
-      context 'without query parameters' do
-        let(:image) { subject.build("FROM debian:stable\n") }
-
-        it 'builds an image' do
-          expect(image).to be_a Docker::Image
-          expect(image.id).to_not be_nil
-          expect(image.connection).to be_a Docker::Connection
-        end
-      end
-
-      context 'with specifying a repo in the query parameters' do
-        let(:image) {
-          subject.build(
-            "FROM debian:stable\nRUN true\n",
-            "t" => "#{ENV['DOCKER_API_USER']}/debian:true"
-          )
-        }
-        after { image.remove(:noprune => true) }
-
-        it 'builds an image and tags it' do
-          expect(image).to be_a Docker::Image
-          expect(image.id).to_not be_nil
-          expect(image.connection).to be_a Docker::Connection
-          image.refresh!
-          expect(image.info["RepoTags"].size).to eq(1)
-          expect(image.info["RepoTags"].first).to match(%r{#{ENV['DOCKER_API_USER']}/debian:true})
-        end
-      end
-
-      context 'with a block capturing build output' do
-        let(:build_output) { "" }
-        let(:block) { Proc.new { |chunk| build_output << chunk } }
-        let!(:image) { subject.build("FROM debian:stable\n", &block) }
-
-        it 'calls the block and passes build output' do
-          expect(build_output).to match(/(Step|STEP) \d(\/\d)?\s?: FROM debian:stable/)
-        end
-      end
-    end
-  end
-
-  describe '.build_from_dir' do
-    subject { described_class }
-
-    context 'with a valid Dockerfile' do
-      let(:dir) {
-        File.join(File.dirname(__FILE__), '..', 'fixtures', 'build_from_dir')
-      }
-      let(:docker_file) { File.new("#{dir}/Dockerfile") }
-      let(:image) { subject.build_from_dir(dir, opts, &block) }
-      let(:opts) { {} }
-      let(:block) { Proc.new {} }
-      let(:container) do
-        Docker::Container.create(
-          'Image' => image.id,
-          'Cmd' => %w[cat /Dockerfile]
-        ).tap(&:start).tap(&:wait)
-      end
-      let(:output) { container.streaming_logs(stdout: true) }
-
-      after(:each) do
-        container.remove
-        image.remove(:noprune => true)
-      end
-
-      context 'with no query parameters' do
-        it 'builds the image' do
-          expect(output).to eq(docker_file.read)
-        end
-      end
-
-      context 'with specifying a repo in the query parameters' do
-        let(:opts) { { "t" => "#{ENV['DOCKER_API_USER']}/debian:from_dir" } }
-        it 'builds the image and tags it' do
-          expect(output).to eq(docker_file.read)
-          image.refresh!
-          expect(image.info["RepoTags"].size).to eq(1)
-          expect(image.info["RepoTags"].first).to match(%r{#{ENV['DOCKER_API_USER']}/debian:from_dir})
-        end
-      end
-
-      context 'with a block capturing build output' do
-        let(:build_output) { "" }
-        let(:block) { Proc.new { |chunk| build_output << chunk } }
-
-        it 'calls the block and passes build output' do
-          image # Create the image variable, which is lazy-loaded by Rspec
-          expect(build_output).to match(/(Step|STEP) \d(\/\d)?\s?: FROM debian:stable/)
-        end
-
-        context 'uses a cached version the second time' do
-          let(:build_output_two) { "" }
-          let(:block_two) { Proc.new { |chunk| build_output_two << chunk } }
-          let(:image_two) { subject.build_from_dir(dir, opts, &block_two) }
-
-          it 'calls the block and passes build output' do
-            skip('Not supported on podman') if ::Docker.podman?
-            image # Create the image variable, which is lazy-loaded by Rspec
-            expect(build_output).to match(/(Step|STEP) \d(\/\d)?\s?: FROM debian:stable/)
-            expect(build_output).to_not match(/Using cache/)
-
-            image_two # Create the image_two variable, which is lazy-loaded by Rspec
-            expect(build_output_two).to match(/Using cache/)
-          end
-        end
-      end
-
-      context 'with credentials passed' do
-        let(:creds) {
-          {
-            :username => ENV['DOCKER_API_USER'],
-            :password => ENV['DOCKER_API_PASS'],
-            :email => ENV['DOCKER_API_EMAIL'],
-            :serveraddress => 'https://index.docker.io/v1'
-          }
-        }
-
-        before { Docker.creds = creds }
-        after { Docker.creds = nil }
-
-        it 'sends X-Registry-Config header' do
-          expect(image.info[:headers].keys).to include('X-Registry-Config')
-        end
-      end
-    end
-  end
-end
diff -pruN 2.2.0-2/spec/docker/messages_spec.rb 2.4.0-1/spec/docker/messages_spec.rb
--- 2.2.0-2/spec/docker/messages_spec.rb	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/spec/docker/messages_spec.rb	1970-01-01 00:00:00.000000000 +0000
@@ -1,99 +0,0 @@
-require 'spec_helper'
-
-SingleCov.covered! uncovered: 4
-
-describe Docker::Messages do
-  shared_examples_for "two equal messages" do
-    it "has the same messages as we expect" do
-      expect(messages.all_messages).to eq(expected.all_messages)
-      expect(messages.stdout_messages).to eq(expected.stdout_messages)
-      expect(messages.stderr_messages).to eq(expected.stderr_messages)
-      expect(messages.buffer).to eq(expected.buffer)
-    end
-  end
-
-  describe '.decipher_messages' do
-    shared_examples_for "decipher_messages of raw_test" do
-      let(:messages) {
-        subject.decipher_messages(raw_text)
-      }
-
-      it_behaves_like "two equal messages"
-    end
-
-    context 'given both standard out and standard error' do
-      let(:raw_text) {
-        "\x01\x00\x00\x00\x00\x00\x00\x01a\x02\x00\x00\x00\x00\x00\x00\x01b"
-      }
-      let(:expected) {
-        Docker::Messages.new(["a"], ["b"], ["a","b"], "")
-      }
-
-      it_behaves_like "decipher_messages of raw_test"
-    end
-
-    context 'given a single header' do
-      let(:raw_text) { "\x01\x00\x00\x00\x00\x00\x00\x01a" }
-      let(:expected) {
-        Docker::Messages.new(["a"], [], ["a"], "")
-      }
-
-      it_behaves_like "decipher_messages of raw_test"
-    end
-
-    context 'given two headers' do
-      let(:raw_text) {
-        "\x01\x00\x00\x00\x00\x00\x00\x01a\x01\x00\x00\x00\x00\x00\x00\x01b"
-      }
-
-      let(:expected) {
-        Docker::Messages.new(["a", "b"], [], ["a","b"], "")
-      }
-
-      it_behaves_like "decipher_messages of raw_test"
-    end
-
-    context 'given a header for text longer then 255 characters' do
-      let(:raw_text) {
-        "\x01\x00\x00\x00\x00\x00\x01\x01" + ("a" * 257)
-      }
-      let(:expected) {
-        Docker::Messages.new([("a" * 257)], [], [("a" * 257)], "")
-      }
-
-      it_behaves_like "decipher_messages of raw_test"
-    end
-  end
-
-  describe "#append" do
-    context "appending one set of messages on another" do
-      let(:messages) {
-        Docker::Messages.new([], [], [], "")
-      }
-
-      before do
-        messages.append(new_messages)
-      end
-
-      context "with a buffer" do
-        let(:new_messages) {
-          Docker::Messages.new(["a"], [], ["a"], "b")
-        }
-        let(:expected) {
-          Docker::Messages.new(["a"], [], ["a"], "")
-        }
-        it_behaves_like "two equal messages"
-      end
-
-      context "without a buffer" do
-        let(:new_messages) {
-          Docker::Messages.new(["a"], [], ["a"], "")
-        }
-        let(:expected) {
-          Docker::Messages.new(["a"], [], ["a"], "")
-        }
-        it_behaves_like "two equal messages"
-      end
-    end
-  end
-end
diff -pruN 2.2.0-2/spec/docker/messages_stack.rb 2.4.0-1/spec/docker/messages_stack.rb
--- 2.2.0-2/spec/docker/messages_stack.rb	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/spec/docker/messages_stack.rb	1970-01-01 00:00:00.000000000 +0000
@@ -1,27 +0,0 @@
-require 'spec_helper'
-
-SingleCov.covered!
-
-describe Docker::MessagesStack do
-  describe '#append' do
-    context 'without limits' do |variable|
-      it 'does not limit stack size by default' do
-        data = ['foo', 'bar']
-        msg = Docker::Messages.new(data, [], data)
-        expect(subject.messages).not_to receive(:shift)
-        1000.times { subject.append(msg) }
-      end
-    end
-
-    context 'with size limit' do
-      let(:subject) { described_class.new(100) }
-
-      it 'limits stack to given size' do
-        data = ['foo', 'bar']
-        msg = Docker::Messages.new(data, [], data)
-        expect(subject.messages).to receive(:shift).exactly(1900).times
-        1000.times { subject.append(msg) }
-      end
-    end
-  end
-end
diff -pruN 2.2.0-2/spec/docker/network_spec.rb 2.4.0-1/spec/docker/network_spec.rb
--- 2.2.0-2/spec/docker/network_spec.rb	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/spec/docker/network_spec.rb	1970-01-01 00:00:00.000000000 +0000
@@ -1,160 +0,0 @@
-require 'spec_helper'
-
-unless ::Docker.podman?
-  SingleCov.covered! uncovered: 2
-
-  describe Docker::Network, docker_1_9: true do
-    let(:name) do |example|
-      example.description.downcase.gsub(/\s/, '-')
-    end
-
-    describe '#to_s' do
-      subject { described_class.new(Docker.connection, info) }
-      let(:connection) { Docker.connection }
-
-      let(:id) do
-        'a6c5ffd25e07a6c906accf804174b5eb6a9d2f9e07bccb8f5aa4f4de5be6d01d'
-      end
-
-      let(:info) do
-        {
-          'Name' => 'bridge',
-          'Scope' => 'local',
-          'Driver' => 'bridge',
-          'IPAM' => {
-            'Driver' => 'default',
-            'Config' => [{ 'Subnet' => '172.17.0.0/16' }]
-          },
-          'Containers' => {},
-          'Options' => {
-            'com.docker.network.bridge.default_bridge' => 'true',
-            'com.docker.network.bridge.enable_icc' => 'true',
-            'com.docker.network.bridge.enable_ip_masquerade' => 'true',
-            'com.docker.network.bridge.host_binding_ipv4' => '0.0.0.0',
-            'com.docker.network.bridge.name' => 'docker0',
-            'com.docker.network.driver.mtu' => '1500'
-          },
-          'id' => id
-        }
-      end
-
-      let(:expected_string) do
-        "Docker::Network { :id => #{id}, :info => #{info.inspect}, "\
-          ":connection => #{connection} }"
-      end
-
-      its(:to_s) { should == expected_string }
-    end
-
-    describe '.create' do
-      let!(:id) { subject.id }
-      subject { described_class.create(name) }
-      after { described_class.remove(id) }
-
-      it 'creates a Network' do
-        expect(Docker::Network.all.map(&:id)).to include(id)
-      end
-    end
-
-    describe '.remove' do
-      let(:id) { subject.id }
-      subject { described_class.create(name) }
-
-      it 'removes the Network' do
-        described_class.remove(id)
-        expect(Docker::Network.all.map(&:id)).to_not include(id)
-      end
-    end
-
-    describe '.get' do
-      after do
-        described_class.remove(name)
-      end
-
-      let!(:network) { described_class.create(name) }
-
-      it 'returns a network' do
-        expect(Docker::Network.get(name).id).to eq(network.id)
-      end
-    end
-
-    describe '.all' do
-      let!(:networks) do
-        5.times.map { |i| described_class.create("#{name}-#{i}") }
-      end
-
-      after do
-        networks.each(&:remove)
-      end
-
-      it 'should return all networks' do
-        expect(Docker::Network.all.map(&:id)).to include(*networks.map(&:id))
-      end
-    end
-
-    describe '.prune', :docker_17_03 => true do
-      it 'prune networks' do
-        expect { Docker::Network.prune }.not_to raise_error
-      end
-    end
-
-    describe '#connect' do
-      let!(:container) do
-        Docker::Container.create(
-          'Cmd' => %w(sleep 10),
-          'Image' => 'debian:stable'
-        )
-      end
-      subject { described_class.create(name) }
-
-      before(:each) { container.start }
-      after(:each) do
-        container.kill!.remove
-        subject.remove
-      end
-
-      it 'connects a container to a network' do
-        subject.connect(container.id)
-        expect(subject.info['Containers']).to include(container.id)
-      end
-    end
-
-    describe '#disconnect' do
-      let!(:container) do
-        Docker::Container.create(
-          'Cmd' => %w(sleep 10),
-          'Image' => 'debian:stable'
-        )
-      end
-
-      subject { described_class.create(name) }
-
-      before(:each) do
-        container.start
-        sleep 1
-        subject.connect(container.id)
-      end
-
-      after(:each) do
-        container.kill!.remove
-        subject.remove
-      end
-
-      it 'connects a container to a network' do
-        subject.disconnect(container.id)
-        expect(subject.info['Containers']).not_to include(container.id)
-      end
-    end
-
-    describe '#remove' do
-      let(:id) { subject.id }
-      let(:name) { 'test-network-remove' }
-      subject { described_class.create(name) }
-
-      it 'removes the Network' do
-        subject.remove
-        expect(Docker::Network.all.map(&:id)).to_not include(id)
-      end
-    end
-  end
-end
diff -pruN 2.2.0-2/spec/docker/util_spec.rb 2.4.0-1/spec/docker/util_spec.rb
--- 2.2.0-2/spec/docker/util_spec.rb	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/spec/docker/util_spec.rb	1970-01-01 00:00:00.000000000 +0000
@@ -1,331 +0,0 @@
-require 'spec_helper'
-require 'tempfile'
-require 'fileutils'
-
-SingleCov.covered! uncovered: 71
-
-describe Docker::Util do
-  subject { described_class }
-
-  describe '.parse_json' do
-    subject { described_class.parse_json(arg) }
-
-    context 'when the argument is nil' do
-      let(:arg) { nil }
-
-      it { should be_nil }
-    end
-
-    context 'when the argument is empty' do
-      let(:arg) { '' }
-
-      it { should be_nil }
-    end
-
-    context 'when the argument is \'null\'' do
-      let(:arg) { 'null' }
-
-      it { should be_nil }
-    end
-
-    context 'when the argument is not valid JSON' do
-      let(:arg) { '~~lol not valid json~~' }
-
-      it 'raises an error' do
-        expect { subject }.to raise_error Docker::Error::UnexpectedResponseError
-      end
-    end
-
-    context 'when the argument is valid JSON' do
-      let(:arg) { '{"yolo":"swag"}' }
-
-      it 'parses the JSON into a Hash' do
-        expect(subject).to eq 'yolo' => 'swag'
-      end
-    end
-  end
-
-  describe '.fix_json' do
-    let(:response) { '{"this":"is"}{"not":"json"}' }
-    subject { Docker::Util.fix_json(response) }
-
-    it 'fixes the "JSON" response that Docker returns' do
-      expect(subject).to eq [
-        {
-          'this' => 'is'
-        },
-        {
-          'not' => 'json'
-        }
-      ]
-    end
-  end
-
-  describe '.create_dir_tar' do
-    attr_accessor :tmpdir
-
-    def files_in_tar(tar)
-      Gem::Package::TarReader.new(tar) { |content| return content.map(&:full_name).sort }
-    end
-
-    # @param base_dir [String] the path to the directory where the structure should be written
-    # @param dockerignore_entries [Array<String>] the lines of the desired .dockerignore file
-    def structure_context_dir(dockerignore_entries = nil)
-      FileUtils.mkdir_p("#{tmpdir}/a_dir/a_subdir")
-      [
-        '#edge',
-        'a_file',
-        'a_file2',
-        'a_dir/a_file',
-        'a_dir/a_subdir/a_file',
-      ].each { |f| File.write("#{tmpdir}/#{f}", 'x') }
-
-      File.write("#{tmpdir}/.dockerignore", dockerignore_entries.join("\n")) unless dockerignore_entries.nil?
-    end
-
-    def expect_tar_entries(*entries)
-      expect(files_in_tar(tar)).to contain_exactly(*entries)
-    end
-
-    let(:tar) { subject.create_dir_tar tmpdir }
-
-    around do |example|
-      Dir.mktmpdir do |tmpdir|
-        self.tmpdir = tmpdir
-        example.call
-        FileUtils.rm tar
-      end
-    end
-
-    it 'creates a tarball' do
-      tar = subject.create_dir_tar tmpdir
-      expect(files_in_tar(tar)).to eq []
-    end
-
-    it 'packs regular files' do
-      File.write("#{tmpdir}/foo", 'bar')
-      expect(files_in_tar(tar)).to eq ['foo']
-    end
-
-    it 'packs nested files, but not directory entries' do
-      FileUtils.mkdir("#{tmpdir}/foo")
-      File.write("#{tmpdir}/foo/bar", 'bar')
-      expect(files_in_tar(tar)).to eq ['foo/bar']
-    end
-
-    describe '.dockerignore' do
-      it 'passes all files when there is no .dockerignore' do
-        structure_context_dir
-        expect_tar_entries('#edge', 'a_dir/a_file', 'a_dir/a_subdir/a_file', 'a_file', 'a_file2')
-      end
-
-      it 'passes all files when there is an empty .dockerignore' do
-        structure_context_dir([''])
-        expect_tar_entries('#edge', '.dockerignore', 'a_dir/a_file', 'a_dir/a_subdir/a_file', 'a_file', 'a_file2')
-      end
-
-      it 'does not interpret comments' do
-        structure_context_dir(['#edge'])
-        expect_tar_entries('#edge', '.dockerignore', 'a_dir/a_file', 'a_dir/a_subdir/a_file', 'a_file', 'a_file2')
-      end
-
-      it 'ignores files' do
-        structure_context_dir(['a_file'])
-        expect_tar_entries('#edge', '.dockerignore', 'a_dir/a_file', 'a_dir/a_subdir/a_file', 'a_file2')
-      end
-
-      it 'ignores files with wildcard' do
-        structure_context_dir(['a_file'])
-        expect_tar_entries('#edge', '.dockerignore', 'a_dir/a_file', 'a_dir/a_subdir/a_file', 'a_file2')
-      end
-
-      it 'ignores files with dir wildcard' do
-        structure_context_dir(['**/a_file'])
-        expect_tar_entries('#edge', '.dockerignore', 'a_file2')
-      end
-
-      it 'ignores files with dir wildcard but handles exceptions' do
-        structure_context_dir(['**/a_file', '!a_dir/a_file'])
-        expect_tar_entries('#edge', '.dockerignore', 'a_dir/a_file', 'a_file2')
-      end
-
-      it 'ignores directories' do
-        structure_context_dir(['a_dir'])
-        expect_tar_entries('#edge', '.dockerignore', 'a_file', 'a_file2')
-      end
-
-      it 'ignores directories with dir wildcard' do
-        structure_context_dir(['*/a_subdir'])
-        expect_tar_entries('#edge', '.dockerignore', 'a_dir/a_file', 'a_file', 'a_file2')
-      end
-
-      it 'ignores directories with dir double wildcard' do
-        structure_context_dir(['**/a_subdir'])
-        expect_tar_entries('#edge', '.dockerignore', 'a_dir/a_file', 'a_file', 'a_file2')
-      end
-
-      it 'ignores directories with dir wildcard' do
-        structure_context_dir(['a_dir', '!a_dir/a_subdir'])
-        expect_tar_entries('#edge', '.dockerignore', 'a_dir/a_subdir/a_file', 'a_file', 'a_file2')
-      end
-
-      it 'ignores files' do
-        File.write("#{tmpdir}/foo", 'bar')
-        File.write("#{tmpdir}/baz", 'bar')
-
-        File.write("#{tmpdir}/.dockerignore", "foo")
-
-        expect(files_in_tar(tar)).to eq ['.dockerignore', 'baz']
-      end
-
-      it 'ignores folders' do
-        FileUtils.mkdir("#{tmpdir}/foo")
-        File.write("#{tmpdir}/foo/bar", 'bar')
-
-        File.write("#{tmpdir}/.dockerignore", "foo")
-
-        expect(files_in_tar(tar)).to eq ['.dockerignore']
-      end
-
-      it 'ignores based on wildcards' do
-        File.write("#{tmpdir}/bar", 'bar')
-        File.write("#{tmpdir}/baz", 'bar')
-
-        File.write("#{tmpdir}/.dockerignore", "*z")
-
-        expect(files_in_tar(tar)).to eq ['.dockerignore', 'bar']
-      end
-
-      it 'ignores comments' do
-        File.write("#{tmpdir}/foo", 'bar')
-        File.write("#{tmpdir}/baz", 'bar')
-
-        File.write("#{tmpdir}/.dockerignore", "# nothing here\nfoo")
-
-        expect(files_in_tar(tar)).to eq ['.dockerignore', 'baz']
-      end
-
-      it 'ignores whitespace' do
-        File.write("#{tmpdir}/foo", 'bar')
-        File.write("#{tmpdir}/baz", 'bar')
-
-        File.write("#{tmpdir}/.dockerignore", "foo   \n   \n\n")
-
-        expect(files_in_tar(tar)).to eq ['.dockerignore', 'baz']
-      end
-
-      it 'ignores multiple patterns' do
-        File.write("#{tmpdir}/foo", 'bar')
-        File.write("#{tmpdir}/baz", 'bar')
-        File.write("#{tmpdir}/zig", 'bar')
-
-        File.write("#{tmpdir}/.dockerignore", "fo*\nba*")
-
-        expect(files_in_tar(tar)).to eq ['.dockerignore', 'zig']
-      end
-    end
-  end
-
-  describe '.build_auth_header' do
-    subject { described_class }
-
-    let(:credentials) {
-      {
-        :username      => 'test',
-        :password      => 'password',
-        :email         => 'test@example.com',
-        :serveraddress => 'https://registry.com/'
-      }
-    }
-    let(:credential_string) { MultiJson.dump(credentials) }
-    let(:encoded_creds) { Base64.urlsafe_encode64(credential_string) }
-    let(:expected_header) {
-      {
-        'X-Registry-Auth' => encoded_creds
-      }
-    }
-
-    context 'given credentials as a Hash' do
-      it 'returns an X-Registry-Auth header encoded' do
-        expect(subject.build_auth_header(credentials)).to eq(expected_header)
-      end
-    end
-
-    context 'given credentials as a String' do
-      it 'returns an X-Registry-Auth header encoded' do
-        expect(
-          subject.build_auth_header(credential_string)
-        ).to eq(expected_header)
-      end
-    end
-
-    it 'does not contain newlines' do
-      h = subject.build_auth_header(credentials).fetch('X-Registry-Auth')
-      expect(h).not_to include("\n")
-    end
-  end
-
-  describe '.build_config_header' do
-    subject { described_class }
-
-    let(:credentials) {
-      {
-        :username      => 'test',
-        :password      => 'password',
-        :email         => 'test@example.com',
-        :serveraddress => 'https://registry.com/'
-      }
-    }
-
-    let(:credentials_object) do
-      MultiJson.dump(
-        :'https://registry.com/' => {
-          username: 'test',
-          password: 'password',
-          email: 'test@example.com'
-        }
-      )
-    end
-
-    let(:encoded_creds) { Base64.urlsafe_encode64(credentials_object) }
-    let(:expected_header) {
-      {
-        'X-Registry-Config' => encoded_creds
-      }
-    }
-
-    context 'given credentials as a Hash' do
-      it 'returns an X-Registry-Config header encoded' do
-        expect(subject.build_config_header(credentials)).to eq(expected_header)
-      end
-    end
-
-    context 'given credentials as a String' do
-      it 'returns an X-Registry-Config header encoded' do
-        expect(
-          subject.build_config_header(MultiJson.dump(credentials))
-        ).to eq(expected_header)
-      end
-    end
-
-    it 'does not contain newlines' do
-      h = subject.build_config_header(credentials).fetch('X-Registry-Config')
-      expect(h).not_to include("\n")
-    end
-  end
-
-  describe '.filesystem_permissions' do
-    it 'returns the permissions on a file' do
-      file = Tempfile.new('test_file')
-      file.close
-      expected_permissions = 0600
-      File.chmod(expected_permissions, file.path)
-
-      actual_permissions = subject.filesystem_permissions(file.path)
-
-      file.unlink
-      expect(actual_permissions).to eql(expected_permissions)
-    end
-  end
-
-end
diff -pruN 2.2.0-2/spec/docker/volume_spec.rb 2.4.0-1/spec/docker/volume_spec.rb
--- 2.2.0-2/spec/docker/volume_spec.rb	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/spec/docker/volume_spec.rb	1970-01-01 00:00:00.000000000 +0000
@@ -1,53 +0,0 @@
-require 'spec_helper'
-
-SingleCov.covered! uncovered: 1
-
-# Volume requests are actually slow enough to occasionally not work
-# Use sleep statements to manage that
-describe Docker::Volume, :docker_1_9 do
-  let(:name) { "ArbitraryNameForTheRakeTestVolume" }
-
-  describe '.create' do
-    let(:volume) { Docker::Volume.create(name) }
-
-    after { volume.remove }
-
-    it 'creates a volume' do
-      expect(volume.id).to eq(name)
-    end
-  end
-
-  describe '.get' do
-    let(:volume) { Docker::Volume.get(name) }
-
-    before { Docker::Volume.create(name); sleep 1 }
-    after { volume.remove }
-
-    it 'gets volume details' do
-      expect(volume.id).to eq(name)
-      expect(volume.info).to_not be_empty
-    end
-  end
-
-  describe '.all' do
-    after { Docker::Volume.get(name).remove }
-
-    it 'gets a list of volumes' do
-      expect { Docker::Volume.create(name); sleep 1 }.to change { Docker::Volume.all.length }.by(1)
-    end
-  end
-
-  describe '.prune', :docker_17_03 => true do
-    it 'prune volumes' do
-      expect { Docker::Volume.prune }.not_to raise_error
-    end
-  end
-
-  describe '#remove' do
-    it 'removes a volume' do
-      volume = Docker::Volume.create(name)
-      sleep 1
-      expect { volume.remove }.to change { Docker::Volume.all.length }.by(-1)
-    end
-  end
-end
diff -pruN 2.2.0-2/spec/docker_spec.rb 2.4.0-1/spec/docker_spec.rb
--- 2.2.0-2/spec/docker_spec.rb	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/spec/docker_spec.rb	1970-01-01 00:00:00.000000000 +0000
@@ -1,242 +0,0 @@
-require 'spec_helper'
-
-SingleCov.covered! uncovered: 8
-
-describe Docker do
-  subject { Docker }
-
-  it { should be_a Module }
-
-  context 'default url and connection' do
-    context "when the DOCKER_* ENV variables aren't set" do
-      before do
-        allow(ENV).to receive(:[]).with('DOCKER_URL').and_return(nil)
-        allow(ENV).to receive(:[]).with('DOCKER_HOST').and_return(nil)
-        allow(ENV).to receive(:[]).with('DOCKER_CERT_PATH').and_return(nil)
-        Docker.reset!
-      end
-      after { Docker.reset! }
-
-      its(:options) { should == {} }
-      its(:url) { should == 'unix:///var/run/docker.sock' }
-      its(:connection) { should be_a Docker::Connection }
-    end
-
-    context "when the DOCKER_* ENV variables are set" do
-      before do
-        allow(ENV).to receive(:[]).with('DOCKER_URL')
-          .and_return('unixs:///var/run/not-docker.sock')
-        allow(ENV).to receive(:[]).with('DOCKER_HOST').and_return(nil)
-        allow(ENV).to receive(:[]).with('DOCKER_CERT_PATH').and_return(nil)
-        Docker.reset!
-      end
-      after { Docker.reset! }
-
-      its(:options) { should == {} }
-      its(:url) { should == 'unixs:///var/run/not-docker.sock' }
-      its(:connection) { should be_a Docker::Connection }
-    end
-
-    context "when the DOCKER_HOST is set and uses default tcp://" do
-      before do
-        allow(ENV).to receive(:[]).with('DOCKER_URL').and_return(nil)
-        allow(ENV).to receive(:[]).with('DOCKER_HOST').and_return('tcp://')
-        allow(ENV).to receive(:[]).with('DOCKER_CERT_PATH').and_return(nil)
-        Docker.reset!
-      end
-      after { Docker.reset! }
-
-      its(:options) { should == {} }
-      its(:url) { should == 'tcp://localhost:2375' }
-      its(:connection) { should be_a Docker::Connection }
-    end
-
-    context "when the DOCKER_HOST ENV variable is set" do
-      before do
-        allow(ENV).to receive(:[]).with('DOCKER_URL').and_return(nil)
-        allow(ENV).to receive(:[]).with('DOCKER_HOST')
-          .and_return('tcp://someserver:8103')
-        allow(ENV).to receive(:[]).with('DOCKER_CERT_PATH').and_return(nil)
-        Docker.reset!
-      end
-      after { Docker.reset! }
-
-      its(:options) { should == {} }
-      its(:url) { should == 'tcp://someserver:8103' }
-      its(:connection) { should be_a Docker::Connection }
-    end
-
-    context "DOCKER_URL should take precedence over DOCKER_HOST" do
-      before do
-        allow(ENV).to receive(:[]).with('DOCKER_URL')
-          .and_return('tcp://someotherserver:8103')
-        allow(ENV).to receive(:[]).with('DOCKER_HOST')
-          .and_return('tcp://someserver:8103')
-        allow(ENV).to receive(:[]).with('DOCKER_CERT_PATH').and_return(nil)
-        Docker.reset!
-      end
-      after { Docker.reset! }
-
-      its(:options) { should == {} }
-      its(:url) { should == 'tcp://someotherserver:8103' }
-      its(:connection) { should be_a Docker::Connection }
-    end
-
-    context "when the DOCKER_CERT_PATH and DOCKER_HOST ENV variables are set" do
-      before do
-        allow(ENV).to receive(:[]).with('DOCKER_URL').and_return(nil)
-        allow(ENV).to receive(:[]).with('DOCKER_HOST')
-          .and_return('tcp://someserver:8103')
-        allow(ENV).to receive(:[]).with('DOCKER_CERT_PATH')
-          .and_return('/boot2dockert/cert/path')
-        allow(ENV).to receive(:[]).with('DOCKER_SSL_VERIFY').and_return(nil)
-        Docker.reset!
-      end
-      after { Docker.reset! }
-
-      its(:options) {
-        should == {
-          client_cert: '/boot2dockert/cert/path/cert.pem',
-          client_key: '/boot2dockert/cert/path/key.pem',
-          ssl_ca_file: '/boot2dockert/cert/path/ca.pem',
-          scheme: 'https'
-        }
-      }
-      its(:url) { should == 'tcp://someserver:8103' }
-      its(:connection) { should be_a Docker::Connection }
-    end
-
-    context "when the DOCKER_CERT_PATH and DOCKER_SSL_VERIFY ENV variables are set" do
-      before do
-        allow(ENV).to receive(:[]).with('DOCKER_URL').and_return(nil)
-        allow(ENV).to receive(:[]).with('DOCKER_HOST')
-          .and_return('tcp://someserver:8103')
-        allow(ENV).to receive(:[]).with('DOCKER_CERT_PATH')
-          .and_return('/boot2dockert/cert/path')
-        allow(ENV).to receive(:[]).with('DOCKER_SSL_VERIFY')
-          .and_return('false')
-        Docker.reset!
-      end
-      after { Docker.reset! }
-
-      its(:options) {
-        should == {
-          client_cert: '/boot2dockert/cert/path/cert.pem',
-          client_key: '/boot2dockert/cert/path/key.pem',
-          ssl_ca_file: '/boot2dockert/cert/path/ca.pem',
-          scheme: 'https',
-          ssl_verify_peer: false
-        }
-      }
-      its(:url) { should == 'tcp://someserver:8103' }
-      its(:connection) { should be_a Docker::Connection }
-    end
-
-  end
-
-  describe '#reset_connection!' do
-    before { subject.connection }
-    it 'sets the @connection to nil' do
-      expect { subject.reset_connection! }
-          .to change { subject.instance_variable_get(:@connection) }
-          .to nil
-    end
-  end
-
-  [:options=, :url=].each do |method|
-    describe "##{method}" do
-      before { Docker.reset! }
-
-      it 'calls #reset_connection!' do
-        expect(subject).to receive(:reset_connection!)
-        subject.public_send(method, nil)
-      end
-    end
-  end
-
-  describe '#version' do
-    before { Docker.reset! }
-
-    let(:expected) {
-      %w[ApiVersion Arch GitCommit GoVersion KernelVersion Os Version]
-    }
-
-    let(:version) { subject.version }
-    it 'returns the version as a Hash' do
-      expect(version).to be_a Hash
-      expect(version.keys.sort).to include(*expected)
-    end
-  end
-
-  describe '#info' do
-    before { Docker.reset! }
-
-    let(:info) { subject.info }
-    let(:keys) do
-      %w(Containers Debug DockerRootDir Driver DriverStatus ID IPv4Forwarding
-         Images IndexServerAddress KernelVersion Labels MemTotal MemoryLimit
-         NCPU NEventsListener NFd NGoroutines Name OperatingSystem SwapLimit)
-    end
-
-    it 'returns the info as a Hash' do
-      expect(info).to be_a Hash
-      expect(info.keys.sort).to include(*keys)
-    end
-  end
-
-  describe '#ping' do
-    before { Docker.reset! }
-
-    let(:ping) { subject.ping}
-
-    it 'returns the status as a String' do
-      expect(ping).to eq('OK')
-    end
-  end
-
-  describe '#authenticate!' do
-    subject { described_class }
-
-    let(:authentication) {
-      subject.authenticate!(credentials)
-    }
-
-    after { Docker.creds = nil }
-
-    context 'with valid credentials' do
-      let(:credentials) {
-        {
-          :username      => ENV['DOCKER_API_USER'],
-          :password      => ENV['DOCKER_API_PASS'],
-          :email         => ENV['DOCKER_API_EMAIL'],
-          :serveraddress => 'https://index.docker.io/v1/'
-        }
-      }
-
-      it 'logs in and sets the creds' do
-        skip_without_auth
-        expect(authentication).to be true
-        expect(Docker.creds).to eq(MultiJson.dump(credentials))
-      end
-    end
-
-    context 'with invalid credentials' do
-      let(:credentials) {
-        {
-          :username      => 'test',
-          :password      => 'password',
-          :email         => 'test@example.com',
-          :serveraddress => 'https://index.docker.io/v1/'
-        }
-      }
-
-      it "raises an error and doesn't set the creds" do
-        skip('Not supported on podman') if ::Docker.podman?
-        expect {
-          authentication
-        }.to raise_error(Docker::Error::AuthenticationError)
-        expect(Docker.creds).to be_nil
-      end
-    end
-  end
-end
diff -pruN 2.2.0-2/spec/fixtures/build_from_dir/Dockerfile 2.4.0-1/spec/fixtures/build_from_dir/Dockerfile
--- 2.2.0-2/spec/fixtures/build_from_dir/Dockerfile	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/spec/fixtures/build_from_dir/Dockerfile	1970-01-01 00:00:00.000000000 +0000
@@ -1,2 +0,0 @@
-FROM debian:stable
-ADD . /
Binary files 2.2.0-2/spec/fixtures/export.tar and 2.4.0-1/spec/fixtures/export.tar differ
Binary files 2.2.0-2/spec/fixtures/load.tar and 2.4.0-1/spec/fixtures/load.tar differ
diff -pruN 2.2.0-2/spec/fixtures/top/Dockerfile 2.4.0-1/spec/fixtures/top/Dockerfile
--- 2.2.0-2/spec/fixtures/top/Dockerfile	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/spec/fixtures/top/Dockerfile	1970-01-01 00:00:00.000000000 +0000
@@ -1,4 +0,0 @@
-FROM debian:stable
-RUN apt-get update
-RUN apt-get install -y procps
-RUN printf '#! /bin/sh\nwhile true\ndo\ntrue\ndone\n' > /while && chmod +x /while
diff -pruN 2.2.0-2/spec/spec_helper.rb 2.4.0-1/spec/spec_helper.rb
--- 2.2.0-2/spec/spec_helper.rb	2021-07-07 20:46:50.000000000 +0000
+++ 2.4.0-1/spec/spec_helper.rb	1970-01-01 00:00:00.000000000 +0000
@@ -1,38 +0,0 @@
-require 'bundler/setup'
-
-$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
-
-require 'rspec/its'
-
-require 'single_cov'
-
-# avoid coverage failure from lower docker versions not running all tests
-SingleCov.setup :rspec
-
-require 'docker'
-
-ENV['DOCKER_API_USER']  ||= 'debbie_docker'
-ENV['DOCKER_API_PASS']  ||= '*************'
-ENV['DOCKER_API_EMAIL'] ||= 'debbie_docker@example.com'
-
-RSpec.shared_context "local paths" do
-  def project_dir
-    File.expand_path(File.join(File.dirname(__FILE__), '..'))
-  end
-end
-
-module SpecHelpers
-  def skip_without_auth
-    skip "Disabled because of missing auth" if ENV['DOCKER_API_USER'] == 'debbie_docker'
-  end
-end
-
-Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
-
-RSpec.configure do |config|
-  config.mock_with :rspec
-  config.color = true
-  config.formatter = :documentation
-  config.tty = true
-  config.include SpecHelpers
-end
