Compare commits

..

25 Commits

Author SHA1 Message Date
ecc694264d Merge pull request #6621 from thaJeztah/28.x_plugin_hide
Some checks failed
build / prepare (push) Has been cancelled
build / build (push) Has been cancelled
build / bin-image (push) Has been cancelled
build / prepare-plugins (push) Has been cancelled
build / plugins (push) Has been cancelled
codeql / codeql (push) Has been cancelled
e2e / tests (alpine, 25, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 25, local) (push) Has been cancelled
e2e / tests (alpine, 27, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 27, local) (push) Has been cancelled
e2e / tests (alpine, 28, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 28, local) (push) Has been cancelled
e2e / tests (debian, 25, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 25, local) (push) Has been cancelled
e2e / tests (debian, 27, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 27, local) (push) Has been cancelled
e2e / tests (debian, 28, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 28, local) (push) Has been cancelled
test / ctn (push) Has been cancelled
test / host (macos-14) (push) Has been cancelled
test / host (macos-15) (push) Has been cancelled
test / host (macos-15-intel) (push) Has been cancelled
validate / validate (lint) (push) Has been cancelled
validate / validate (shellcheck) (push) Has been cancelled
validate / validate (update-authors) (push) Has been cancelled
validate / validate (validate-vendor) (push) Has been cancelled
validate / validate-md (push) Has been cancelled
validate / validate-make (manpages) (push) Has been cancelled
validate / validate-make (yamldocs) (push) Has been cancelled
[28.x backport] Plugin may set itself as hidden
2025-11-05 11:32:31 +01:00
c475c696cf Plugin may set itself as hidden
Signed-off-by: Alano Terblanche <18033717+Benehiko@users.noreply.github.com>
(cherry picked from commit 700875b666)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-05 07:33:23 +01:00
7494d2cee4 cli: allManagementSubCommands: improve handling of plugin stubs
The allManagementSubCommands function is used to present plugin-commands
in the docker --help output; these commands are included in the "management
commands" section, but for plugins we don't know if they have sub-commands.

However, plugin stubs may be hidden (for placeholders that are not yet loaded),
or not be runnable, which was previously ignored.

This patch treats plugin-stubs the same as other commands, with the exception
of checking if they have subcommands (which is not yet known for plugin-stubs).

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 259df25a96)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-05 07:33:08 +01:00
5306df36fa Merge pull request #6610 from thaJeztah/28.x_backport_deprecate_builder_utils
[28.x backport] cli/command/image/build: deprecate `DefaultDockerfileName`, `DetectArchiveReader`, `WriteTempDockerfile`, `ResolveAndValidateContextPath`
2025-11-04 07:23:51 -06:00
5dbaa52325 cli/command/image/build: deprecate ResolveAndValidateContextPath util
This utility was used internally and will be removed in the next release.
Use `DetectContextType` to detect the context-type, and use `GetContextFromLocalDir`,
`GetContextFromLocalDir`, `GetContextFromGitURL`, or `GetContextFromURL`
instead.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 0f2f9e9c41)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-04 07:14:44 -06:00
dd832b6d97 cli/command/image/build: deprecate WriteTempDockerfile util
It was only used internal in the package.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 6e1ff0bec1)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-04 07:14:44 -06:00
a99e91cc89 cli/command/image/build: deprecate DetectArchiveReader util
It was only used internal in the package.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit c52fa073cd)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-04 07:14:44 -06:00
579b72aa06 cli/command/image/build: deprecate DefaultDockerfileName const
It was only used internal in the package.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit f24bb4bc76)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-04 07:14:40 -06:00
6fc5891770 Merge pull request #6560 from thaJeztah/28.x_backport_deprecations
[28.x backport] opts, image/build: deprecate IsArchive, ValidateMACAddress,  ListOpts.Delete()
2025-11-04 07:12:50 -06:00
9af6cbc489 Merge pull request #6613 from thaJeztah/28.x_bump_go
[28.x] update to go1.24.9
2025-11-04 06:45:14 -06:00
ba53322412 update to go1.24.9
go1.24.9 (released 2025-10-13) includes fixes to the crypto/x509 package.
See the Go 1.24.9 milestone on our issue tracker for details:

- https://github.com/golang/go/issues?q=milestone%3AGo1.24.9+label%3ACherryPickApproved
- full diff: https://github.com/golang/go/compare/go1.24.8...go1.24.9

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-04 11:10:08 +01:00
61d2e8aaad Merge pull request #6609 from thaJeztah/28.x_backport_bump_golangci_lint
[28.x backport] Dockerfile: update golangci-lint to v2.5.0
2025-11-03 11:39:06 -06:00
6abcd4a2a1 Merge pull request #6608 from thaJeztah/28.x_backport_update_gotestsum
[28.x backport] Dockerfile: bump gotest.tools/gotestsum v1.13.0
2025-11-03 15:41:42 +01:00
00c129b974 Merge pull request #6607 from thaJeztah/28.x_backport_update_actions
[28.x backport] update actions
2025-11-03 15:33:29 +01:00
fbf5c8a86d Dockerfile: update golangci-lint to v2.5.0
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 5ad9fbdef7)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-03 12:45:13 +01:00
4c7d52534f Dockerfile: bump gotest.tools/gotestsum v1.13.0
full diff: https://github.com/gotestyourself/gotestsum/compare/v1.12.3...v1.13.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit f8b1b8d165)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-03 12:37:13 +01:00
70123c523c build(deps): bump actions/upload-artifact from 4 to 5
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
(cherry picked from commit aef2ef8c77)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-03 12:32:03 +01:00
4942b2747f build(deps): bump github/codeql-action from 3 to 4
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
(cherry picked from commit 5483b10e94)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-03 12:31:44 +01:00
d8bab71747 opts: deprecate ListOpts.Delete()
This method was added as part of a refactor in [moby@1ba1138], at which
time it was used to delete original values for "--host" and "--volume"
after normalizing. This beccame redundant in [moby@6200002], which added
specialized options that used a validate function, which both validated
and normalized inputs.

It's no longer used, so let's mark it deprecated so that we can remove it.

[moby@1ba1138]: 1ba11384bf
[moby@6200002]: 6200002669

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 193db8ec41)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-10-13 14:33:08 +02:00
36d9523e31 opts: deprecate ValidateMACAddress
It was a wrapper around net.ParseMAC from stdlib, so users should
use that directly.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 17d6a92954)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-10-13 14:30:34 +02:00
7063a0ef07 cli/command/image: runBuild: inline vars and minor cleanups
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 9e646f6d92)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-10-13 14:26:53 +02:00
22487ad6f6 cli/command/image/build: deprecate IsArchive utility
It was only used internally.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 64be664e85)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-10-13 14:24:44 +02:00
4737ed4906 cli/command/image/build: fix linting, add sub-tests
- fix minor linting issues (unhandled errors)
- rename vars to prevent shadowing
- use sub-tests for tests that already prepared for it

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 2c539a6530)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-10-13 14:24:30 +02:00
8cff0087da Merge pull request #6544 from thaJeztah/28.x_bump_engine
[28.x] vendor: github.com/docker/docker v28.5.1
2025-10-09 10:11:06 +02:00
e8b22ce03c vendor: github.com/docker/docker v28.5.1
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-10-08 16:17:13 +02:00
28 changed files with 210 additions and 120 deletions

View File

@ -88,7 +88,7 @@ jobs:
fi
-
name: Upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: ${{ env.ARTIFACT_NAME }}
path: /tmp/out/*

View File

@ -63,17 +63,17 @@ jobs:
name: Update Go
uses: actions/setup-go@v6
with:
go-version: "1.24.8"
go-version: "1.24.9"
-
name: Initialize CodeQL
uses: github/codeql-action/init@v3
uses: github/codeql-action/init@v4
with:
languages: go
-
name: Autobuild
uses: github/codeql-action/autobuild@v3
uses: github/codeql-action/autobuild@v4
-
name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
uses: github/codeql-action/analyze@v4
with:
category: "/language:go"

View File

@ -67,7 +67,7 @@ jobs:
name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.24.8"
go-version: "1.24.9"
-
name: Test
run: |

View File

@ -5,7 +5,7 @@ run:
# which causes it to fallback to go1.17 semantics.
#
# TODO(thaJeztah): update "usetesting" settings to enable go1.24 features once our minimum version is go1.24
go: "1.24.8"
go: "1.24.9"
timeout: 5m

View File

@ -8,13 +8,13 @@ ARG BASE_VARIANT=alpine
ARG ALPINE_VERSION=3.21
ARG BASE_DEBIAN_DISTRO=bookworm
ARG GO_VERSION=1.24.8
ARG GO_VERSION=1.24.9
ARG XX_VERSION=1.6.1
ARG GOVERSIONINFO_VERSION=v1.4.1
# GOTESTSUM_VERSION sets the version of gotestsum to install in the dev container.
# It must be a valid tag in the https://github.com/gotestyourself/gotestsum repository.
ARG GOTESTSUM_VERSION=v1.12.3
ARG GOTESTSUM_VERSION=v1.13.0
# BUILDX_VERSION sets the version of buildx to use for the e2e tests.
# It must be a tag in the docker.io/docker/buildx-bin image repository

View File

@ -38,6 +38,7 @@ func AddPluginCommandStubs(dockerCLI config.Provider, rootCmd *cobra.Command) (e
rootCmd.AddCommand(&cobra.Command{
Use: p.Name,
Short: p.ShortDescription,
Hidden: p.Hidden,
Run: func(_ *cobra.Command, _ []string) {},
Annotations: annotations,
DisableFlagParsing: true,

View File

@ -33,4 +33,6 @@ type Metadata struct {
ShortDescription string `json:",omitempty"`
// URL is a pointer to the plugin's homepage.
URL string `json:",omitempty"`
// Hidden hides the plugin in completion and help message output.
Hidden bool `json:",omitempty"`
}

View File

@ -376,13 +376,10 @@ func orchestratorSubCommands(cmd *cobra.Command) []*cobra.Command {
func allManagementSubCommands(cmd *cobra.Command) []*cobra.Command {
cmds := []*cobra.Command{}
for _, sub := range cmd.Commands() {
if isPlugin(sub) {
if invalidPluginReason(sub) == "" {
cmds = append(cmds, sub)
}
if invalidPluginReason(sub) != "" {
continue
}
if sub.IsAvailableCommand() && sub.HasSubCommands() {
if sub.IsAvailableCommand() && (isPlugin(sub) || sub.HasSubCommands()) {
cmds = append(cmds, sub)
}
}

View File

@ -56,6 +56,33 @@ func TestInvalidPlugin(t *testing.T) {
assert.DeepEqual(t, invalidPlugins(root), []*cobra.Command{sub1}, cmpopts.IgnoreUnexported(cobra.Command{}))
}
func TestHiddenPlugin(t *testing.T) {
root := &cobra.Command{Use: "root"}
sub1 := &cobra.Command{
Use: "sub1",
Hidden: true,
Annotations: map[string]string{
metadata.CommandAnnotationPlugin: "true",
},
Run: func(cmd *cobra.Command, args []string) {},
}
sub1sub1 := &cobra.Command{Use: "sub1sub1"}
sub1sub2 := &cobra.Command{Use: "sub1sub2"}
sub2 := &cobra.Command{
Use: "sub2",
Annotations: map[string]string{
metadata.CommandAnnotationPlugin: "true",
},
Run: func(cmd *cobra.Command, args []string) {},
}
root.AddCommand(sub1, sub2)
sub1.AddCommand(sub1sub1, sub1sub2)
assert.DeepEqual(t, allManagementSubCommands(root), []*cobra.Command{sub2}, cmpopts.IgnoreFields(cobra.Command{}, "Run"), cmpopts.IgnoreUnexported(cobra.Command{}))
}
func TestCommandAliases(t *testing.T) {
root := &cobra.Command{Use: "root"}
sub := &cobra.Command{Use: "subcommand", Aliases: []string{"alias1", "alias2"}}

View File

@ -4,6 +4,7 @@ import (
"bytes"
"encoding/json"
"fmt"
"net"
"os"
"path"
"path/filepath"
@ -350,7 +351,7 @@ func parse(flags *pflag.FlagSet, copts *containerOptions, serverOS string) (*con
// Validate the input mac address
if copts.macAddress != "" {
if _, err := opts.ValidateMACAddress(copts.macAddress); err != nil {
if _, err := net.ParseMAC(strings.TrimSpace(copts.macAddress)); err != nil {
return nil, errors.Errorf("%s is not a valid mac address", copts.macAddress)
}
}
@ -883,7 +884,7 @@ func parseNetworkAttachmentOpt(ep opts.NetworkAttachmentOpts) (*networktypes.End
}
}
if ep.MacAddress != "" {
if _, err := opts.ValidateMACAddress(ep.MacAddress); err != nil {
if _, err := net.ParseMAC(strings.TrimSpace(ep.MacAddress)); err != nil {
return nil, errors.Errorf("%s is not a valid mac address", ep.MacAddress)
}
epConfig.MacAddress = ep.MacAddress

View File

@ -12,7 +12,7 @@
// based on https://github.com/golang/go/blob/master/src/text/tabwriter/tabwriter.go Last modified 690ac40 on 31 Jan
//nolint:gocyclo,nakedret,unused // ignore linting errors, so that we can stick close to upstream
//nolint:gocyclo,gofumpt,nakedret,unused // ignore linting errors, so that we can stick close to upstream
package tabwriter
import (

View File

@ -184,7 +184,6 @@ func (out *lastProgressOutput) WriteProgress(prog progress.Progress) error {
//nolint:gocyclo
func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions) error {
var (
err error
buildCtx io.ReadCloser
dockerfileCtx io.ReadCloser
contextDir string
@ -266,7 +265,7 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions)
}
if err := build.ValidateContextDirectory(contextDir, excludes); err != nil {
return errors.Wrap(err, "error checking context")
return errors.Wrap(err, "checking context")
}
// And canonicalize dockerfile name to a platform-independent one
@ -353,7 +352,6 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions)
}
}
buildOpts := imageBuildOptions(dockerCli, options)
buildOpts.Version = buildtypes.BuilderV1
buildOpts.Dockerfile = relDockerfile
buildOpts.AuthConfigs = authConfigs
buildOpts.RemoteContext = remote
@ -363,7 +361,6 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions)
if options.quiet {
_, _ = fmt.Fprintf(dockerCli.Err(), "%s", progBuff)
}
cancel()
return err
}
defer response.Body.Close()
@ -380,7 +377,8 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions)
err = jsonstream.Display(ctx, response.Body, streams.NewOut(buildBuff), jsonstream.WithAuxCallback(aux))
if err != nil {
if jerr, ok := err.(*jsonstream.JSONError); ok {
var jerr *jsonstream.JSONError
if errors.As(err, &jerr) {
// If no error code is set, default to 1
if jerr.Code == 0 {
jerr.Code = 1
@ -544,6 +542,7 @@ func replaceDockerfileForContentTrust(ctx context.Context, inputTarStream io.Rea
func imageBuildOptions(dockerCli command.Cli, options buildOptions) buildtypes.ImageBuildOptions {
configFile := dockerCli.ConfigFile()
return buildtypes.ImageBuildOptions{
Version: buildtypes.BuilderV1,
Memory: options.memory.Value(),
MemorySwap: options.memorySwap.Value(),
Tags: options.tags.GetSlice(),

View File

@ -25,9 +25,14 @@ import (
"github.com/pkg/errors"
)
// DefaultDockerfileName is the Default filename with Docker commands, read by docker build
//
// Deprecated: this const is no longer used and will be removed in the next release.
const DefaultDockerfileName string = "Dockerfile"
const (
// DefaultDockerfileName is the Default filename with Docker commands, read by docker build
DefaultDockerfileName string = "Dockerfile"
// defaultDockerfileName is the Default filename with Docker commands, read by docker build
defaultDockerfileName string = "Dockerfile"
// archiveHeaderSize is the number of bytes in an archive header
archiveHeaderSize = 512
)
@ -80,7 +85,7 @@ func ValidateContextDirectory(srcPath string, excludes []string) error {
if err != nil && os.IsPermission(err) {
return errors.Errorf("no permission to read from '%s'", filePath)
}
currentFile.Close()
_ = currentFile.Close()
}
return nil
})
@ -97,10 +102,21 @@ func filepathMatches(matcher *patternmatcher.PatternMatcher, file string) (bool,
// DetectArchiveReader detects whether the input stream is an archive or a
// Dockerfile and returns a buffered version of input, safe to consume in lieu
// of input. If an archive is detected, isArchive is set to true, and to false
// of input. If an archive is detected, ok is set to true, and to false
// otherwise, in which case it is safe to assume input represents the contents
// of a Dockerfile.
func DetectArchiveReader(input io.ReadCloser) (rc io.ReadCloser, isArchive bool, err error) {
//
// Deprecated: this utility was only used internally, and will be removed in the next release.
func DetectArchiveReader(input io.ReadCloser) (rc io.ReadCloser, ok bool, err error) {
return detectArchiveReader(input)
}
// detectArchiveReader detects whether the input stream is an archive or a
// Dockerfile and returns a buffered version of input, safe to consume in lieu
// of input. If an archive is detected, ok is set to true, and to false
// otherwise, in which case it is safe to assume input represents the contents
// of a Dockerfile.
func detectArchiveReader(input io.ReadCloser) (rc io.ReadCloser, ok bool, err error) {
buf := bufio.NewReader(input)
magic, err := buf.Peek(archiveHeaderSize * 2)
@ -108,13 +124,22 @@ func DetectArchiveReader(input io.ReadCloser) (rc io.ReadCloser, isArchive bool,
return nil, false, errors.Errorf("failed to peek context header from STDIN: %v", err)
}
return newReadCloserWrapper(buf, func() error { return input.Close() }), IsArchive(magic), nil
return newReadCloserWrapper(buf, func() error { return input.Close() }), isArchive(magic), nil
}
// WriteTempDockerfile writes a Dockerfile stream to a temporary file with a
// name specified by DefaultDockerfileName and returns the path to the
// name specified by defaultDockerfileName and returns the path to the
// temporary directory containing the Dockerfile.
//
// Deprecated: this utility was only used internally, and will be removed in the next release.
func WriteTempDockerfile(rc io.ReadCloser) (dockerfileDir string, err error) {
return writeTempDockerfile(rc)
}
// writeTempDockerfile writes a Dockerfile stream to a temporary file with a
// name specified by defaultDockerfileName and returns the path to the
// temporary directory containing the Dockerfile.
func writeTempDockerfile(rc io.ReadCloser) (dockerfileDir string, err error) {
// err is a named return value, due to the defer call below.
dockerfileDir, err = os.MkdirTemp("", "docker-build-tempdockerfile-")
if err != nil {
@ -126,7 +151,7 @@ func WriteTempDockerfile(rc io.ReadCloser) (dockerfileDir string, err error) {
}
}()
f, err := os.Create(filepath.Join(dockerfileDir, DefaultDockerfileName))
f, err := os.Create(filepath.Join(dockerfileDir, defaultDockerfileName))
if err != nil {
return "", err
}
@ -141,12 +166,12 @@ func WriteTempDockerfile(rc io.ReadCloser) (dockerfileDir string, err error) {
// Dockerfile or tar archive. Returns a tar archive used as a context and a
// path to the Dockerfile inside the tar.
func GetContextFromReader(rc io.ReadCloser, dockerfileName string) (out io.ReadCloser, relDockerfile string, err error) {
rc, isArchive, err := DetectArchiveReader(rc)
rc, ok, err := detectArchiveReader(rc)
if err != nil {
return nil, "", err
}
if isArchive {
if ok {
return rc, dockerfileName, nil
}
@ -159,7 +184,7 @@ func GetContextFromReader(rc io.ReadCloser, dockerfileName string) (out io.ReadC
return nil, "", errors.New("ambiguous Dockerfile source: both stdin and flag correspond to Dockerfiles")
}
dockerfileDir, err := WriteTempDockerfile(rc)
dockerfileDir, err := writeTempDockerfile(rc)
if err != nil {
return nil, "", err
}
@ -171,14 +196,22 @@ func GetContextFromReader(rc io.ReadCloser, dockerfileName string) (out io.ReadC
return newReadCloserWrapper(tarArchive, func() error {
err := tarArchive.Close()
os.RemoveAll(dockerfileDir)
_ = os.RemoveAll(dockerfileDir)
return err
}), DefaultDockerfileName, nil
}), defaultDockerfileName, nil
}
// IsArchive checks for the magic bytes of a tar or any supported compression
// algorithm.
//
// Deprecated: this utility was used internally and will be removed in the next release.
func IsArchive(header []byte) bool {
return isArchive(header)
}
// isArchive checks for the magic bytes of a tar or any supported compression
// algorithm.
func isArchive(header []byte) bool {
if compression.Detect(header) != compression.None {
return true
}
@ -201,7 +234,7 @@ func GetContextFromGitURL(gitURL, dockerfileName string) (string, string, error)
return "", "", errors.Wrapf(err, "unable to 'git clone' to temporary context directory")
}
absContextDir, err = ResolveAndValidateContextPath(absContextDir)
absContextDir, err = resolveAndValidateContextPath(absContextDir)
if err != nil {
return "", "", err
}
@ -242,7 +275,7 @@ func getWithStatusError(url string) (resp *http.Response, err error) {
}
msg := fmt.Sprintf("failed to GET %s with status %s", url, resp.Status)
body, err := io.ReadAll(resp.Body)
resp.Body.Close()
_ = resp.Body.Close()
if err != nil {
return nil, errors.Wrapf(err, "%s: error reading body", msg)
}
@ -254,7 +287,7 @@ func getWithStatusError(url string) (resp *http.Response, err error) {
// the relative path of the dockerfile in that context directory, and a non-nil
// error on success.
func GetContextFromLocalDir(localDir, dockerfileName string) (string, string, error) {
localDir, err := ResolveAndValidateContextPath(localDir)
localDir, err := resolveAndValidateContextPath(localDir)
if err != nil {
return "", "", err
}
@ -274,7 +307,18 @@ func GetContextFromLocalDir(localDir, dockerfileName string) (string, string, er
// ResolveAndValidateContextPath uses the given context directory for a `docker build`
// and returns the absolute path to the context directory.
//
// Deprecated: this utility was used internally and will be removed in the next
// release. Use [DetectContextType] to detect the context-type, and use
// [GetContextFromLocalDir], [GetContextFromLocalDir], [GetContextFromGitURL],
// or [GetContextFromURL] instead.
func ResolveAndValidateContextPath(givenContextDir string) (string, error) {
return resolveAndValidateContextPath(givenContextDir)
}
// resolveAndValidateContextPath uses the given context directory for a `docker build`
// and returns the absolute path to the context directory.
func resolveAndValidateContextPath(givenContextDir string) (string, error) {
absContextDir, err := filepath.Abs(givenContextDir)
if err != nil {
return "", errors.Errorf("unable to get absolute context directory of given context directory %q: %v", givenContextDir, err)
@ -318,12 +362,12 @@ func getDockerfileRelPath(absContextDir, givenDockerfile string) (string, error)
if absDockerfile == "" {
// No -f/--file was specified so use the default relative to the
// context directory.
absDockerfile = filepath.Join(absContextDir, DefaultDockerfileName)
absDockerfile = filepath.Join(absContextDir, defaultDockerfileName)
// Just to be nice ;-) look for 'dockerfile' too but only
// use it if we found it, otherwise ignore this check
if _, err = os.Lstat(absDockerfile); os.IsNotExist(err) {
altPath := filepath.Join(absContextDir, strings.ToLower(DefaultDockerfileName))
altPath := filepath.Join(absContextDir, strings.ToLower(defaultDockerfileName))
if _, err = os.Lstat(altPath); err == nil {
absDockerfile = altPath
}
@ -374,7 +418,7 @@ func isUNC(path string) bool {
// the relative path to the dockerfile in the context.
func AddDockerfileToBuildContext(dockerfileCtx io.ReadCloser, buildCtx io.ReadCloser) (io.ReadCloser, string, error) {
file, err := io.ReadAll(dockerfileCtx)
dockerfileCtx.Close()
_ = dockerfileCtx.Close()
if err != nil {
return nil, "", err
}
@ -438,17 +482,19 @@ func Compress(buildCtx io.ReadCloser) (io.ReadCloser, error) {
go func() {
compressWriter, err := compression.CompressStream(pipeWriter, archive.Gzip)
if err != nil {
pipeWriter.CloseWithError(err)
_ = pipeWriter.CloseWithError(err)
}
defer buildCtx.Close()
defer func() {
_ = buildCtx.Close()
}()
if _, err := io.Copy(compressWriter, buildCtx); err != nil {
pipeWriter.CloseWithError(errors.Wrap(err, "failed to compress context"))
compressWriter.Close()
_ = pipeWriter.CloseWithError(errors.Wrap(err, "failed to compress context"))
_ = compressWriter.Close()
return
}
compressWriter.Close()
pipeWriter.Close()
_ = compressWriter.Close()
_ = pipeWriter.Close()
}()
return pipeReader, nil

View File

@ -31,7 +31,7 @@ func prepareNoFiles(t *testing.T) string {
func prepareOneFile(t *testing.T) string {
t.Helper()
contextDir := createTestTempDir(t)
createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents)
createTestTempFile(t, contextDir, defaultDockerfileName, dockerfileContents)
return contextDir
}
@ -66,7 +66,7 @@ func TestGetContextFromLocalDirNotExistingDockerfile(t *testing.T) {
func TestGetContextFromLocalDirWithNoDirectory(t *testing.T) {
contextDir := createTestTempDir(t)
createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents)
createTestTempFile(t, contextDir, defaultDockerfileName, dockerfileContents)
chdir(t, contextDir)
@ -74,23 +74,23 @@ func TestGetContextFromLocalDirWithNoDirectory(t *testing.T) {
assert.NilError(t, err)
assert.Check(t, is.Equal(contextDir, absContextDir))
assert.Check(t, is.Equal(DefaultDockerfileName, relDockerfile))
assert.Check(t, is.Equal(defaultDockerfileName, relDockerfile))
}
func TestGetContextFromLocalDirWithDockerfile(t *testing.T) {
contextDir := createTestTempDir(t)
createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents)
createTestTempFile(t, contextDir, defaultDockerfileName, dockerfileContents)
absContextDir, relDockerfile, err := GetContextFromLocalDir(contextDir, "")
assert.NilError(t, err)
assert.Check(t, is.Equal(contextDir, absContextDir))
assert.Check(t, is.Equal(DefaultDockerfileName, relDockerfile))
assert.Check(t, is.Equal(defaultDockerfileName, relDockerfile))
}
func TestGetContextFromLocalDirLocalFile(t *testing.T) {
contextDir := createTestTempDir(t)
createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents)
createTestTempFile(t, contextDir, defaultDockerfileName, dockerfileContents)
testFilename := createTestTempFile(t, contextDir, "tmpTest", "test")
absContextDir, relDockerfile, err := GetContextFromLocalDir(testFilename, "")
@ -112,13 +112,13 @@ func TestGetContextFromLocalDirWithCustomDockerfile(t *testing.T) {
contextDir := createTestTempDir(t)
chdir(t, contextDir)
createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents)
createTestTempFile(t, contextDir, defaultDockerfileName, dockerfileContents)
absContextDir, relDockerfile, err := GetContextFromLocalDir(contextDir, DefaultDockerfileName)
absContextDir, relDockerfile, err := GetContextFromLocalDir(contextDir, defaultDockerfileName)
assert.NilError(t, err)
assert.Check(t, is.Equal(contextDir, absContextDir))
assert.Check(t, is.Equal(DefaultDockerfileName, relDockerfile))
assert.Check(t, is.Equal(defaultDockerfileName, relDockerfile))
}
func TestGetContextFromReaderString(t *testing.T) {
@ -135,7 +135,7 @@ func TestGetContextFromReaderString(t *testing.T) {
}
buff := new(bytes.Buffer)
buff.ReadFrom(tarReader)
_, _ = buff.ReadFrom(tarReader)
contents := buff.String()
_, err = tarReader.Next()
@ -150,8 +150,8 @@ func TestGetContextFromReaderString(t *testing.T) {
t.Fatalf("Uncompressed tar archive does not equal: %s, got: %s", dockerfileContents, contents)
}
if relDockerfile != DefaultDockerfileName {
t.Fatalf("Relative path not equals %s, got: %s", DefaultDockerfileName, relDockerfile)
if relDockerfile != defaultDockerfileName {
t.Fatalf("Relative path not equals %s, got: %s", defaultDockerfileName, relDockerfile)
}
}
@ -164,12 +164,12 @@ func TestGetContextFromReaderStringConflict(t *testing.T) {
func TestGetContextFromReaderTar(t *testing.T) {
contextDir := createTestTempDir(t)
createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents)
createTestTempFile(t, contextDir, defaultDockerfileName, dockerfileContents)
tarStream, err := archive.Tar(contextDir, compression.None)
assert.NilError(t, err)
tarArchive, relDockerfile, err := GetContextFromReader(tarStream, DefaultDockerfileName)
tarArchive, relDockerfile, err := GetContextFromReader(tarStream, defaultDockerfileName)
assert.NilError(t, err)
tarReader := tar.NewReader(tarArchive)
@ -177,12 +177,12 @@ func TestGetContextFromReaderTar(t *testing.T) {
header, err := tarReader.Next()
assert.NilError(t, err)
if header.Name != DefaultDockerfileName {
t.Fatalf("Dockerfile name should be: %s, got: %s", DefaultDockerfileName, header.Name)
if header.Name != defaultDockerfileName {
t.Fatalf("Dockerfile name should be: %s, got: %s", defaultDockerfileName, header.Name)
}
buff := new(bytes.Buffer)
buff.ReadFrom(tarReader)
_, _ = buff.ReadFrom(tarReader)
contents := buff.String()
_, err = tarReader.Next()
@ -197,8 +197,8 @@ func TestGetContextFromReaderTar(t *testing.T) {
t.Fatalf("Uncompressed tar archive does not equal: %s, got: %s", dockerfileContents, contents)
}
if relDockerfile != DefaultDockerfileName {
t.Fatalf("Relative path not equals %s, got: %s", DefaultDockerfileName, relDockerfile)
if relDockerfile != defaultDockerfileName {
t.Fatalf("Relative path not equals %s, got: %s", defaultDockerfileName, relDockerfile)
}
}
@ -223,7 +223,7 @@ func TestValidateContextDirectoryWithOneFile(t *testing.T) {
}
func TestValidateContextDirectoryWithOneFileExcludes(t *testing.T) {
testValidateContextDirectory(t, prepareOneFile, []string{DefaultDockerfileName})
testValidateContextDirectory(t, prepareOneFile, []string{defaultDockerfileName})
}
// createTestTempDir creates a temporary directory for testing. It returns the
@ -263,7 +263,7 @@ func chdir(t *testing.T, dir string) {
}
func TestIsArchive(t *testing.T) {
testcases := []struct {
tests := []struct {
doc string
header []byte
expected bool
@ -289,13 +289,15 @@ func TestIsArchive(t *testing.T) {
expected: false,
},
}
for _, testcase := range testcases {
assert.Check(t, is.Equal(testcase.expected, IsArchive(testcase.header)), testcase.doc)
for _, tc := range tests {
t.Run(tc.doc, func(t *testing.T) {
assert.Check(t, is.Equal(tc.expected, isArchive(tc.header)), tc.doc)
})
}
}
func TestDetectArchiveReader(t *testing.T) {
testcases := []struct {
tests := []struct {
file string
desc string
expected bool
@ -316,14 +318,18 @@ func TestDetectArchiveReader(t *testing.T) {
expected: false,
},
}
for _, testcase := range testcases {
content, err := os.Open(testcase.file)
assert.NilError(t, err)
defer content.Close()
for _, tc := range tests {
t.Run(tc.desc, func(t *testing.T) {
content, err := os.Open(tc.file)
assert.NilError(t, err)
defer func() {
_ = content.Close()
}()
_, isArchive, err := DetectArchiveReader(content)
assert.NilError(t, err)
assert.Check(t, is.Equal(testcase.expected, isArchive), testcase.file)
_, isArchive, err := detectArchiveReader(content)
assert.NilError(t, err)
assert.Check(t, is.Equal(tc.expected, isArchive), tc.file)
})
}
}

View File

@ -21,7 +21,9 @@ func ReadDockerignore(contextDir string) ([]string, error) {
case err != nil:
return nil, err
}
defer f.Close()
defer func() {
_ = f.Close()
}()
patterns, err := ignorefile.ReadAll(f)
if err != nil {

View File

@ -1,5 +1,5 @@
variable "GO_VERSION" {
default = "1.24.8"
default = "1.24.9"
}
variable "VERSION" {
default = ""

View File

@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1
ARG GO_VERSION=1.24.8
ARG GO_VERSION=1.24.9
# ALPINE_VERSION sets the version of the alpine base image to use, including for the golang image.
# It must be a supported tag in the docker.io/library/alpine image repository
@ -28,7 +28,7 @@ RUN --mount=type=cache,target=/root/.cache/go-build \
FROM golang AS gotestsum
# GOTESTSUM_VERSION sets the version of gotestsum to install in the dev container.
# It must be a valid tag in the https://github.com/gotestyourself/gotestsum repository.
ARG GOTESTSUM_VERSION=v1.12.3
ARG GOTESTSUM_VERSION=v1.13.0
RUN --mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg/mod \
--mount=type=tmpfs,target=/go/src/ \

View File

@ -1,12 +1,13 @@
# syntax=docker/dockerfile:1
ARG GO_VERSION=1.24.8
ARG GO_VERSION=1.24.9
# ALPINE_VERSION sets the version of the alpine base image to use, including for the golang image.
# It must be a supported tag in the docker.io/library/alpine image repository
# that's also available as alpine image variant for the Golang version used.
ARG ALPINE_VERSION=3.21
ARG GOLANGCI_LINT_VERSION=v2.1.5
# GOLANGCI_LINT_VERSION sets the version of the golangci/golangci-lint image to use.
ARG GOLANGCI_LINT_VERSION=v2.5.0
FROM golangci/golangci-lint:${GOLANGCI_LINT_VERSION}-alpine AS golangci-lint

View File

@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1
ARG GO_VERSION=1.24.8
ARG GO_VERSION=1.24.9
# ALPINE_VERSION sets the version of the alpine base image to use, including for the golang image.
# It must be a supported tag in the docker.io/library/alpine image repository

View File

@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1
ARG GO_VERSION=1.24.8
ARG GO_VERSION=1.24.9
FROM golang:${GO_VERSION}-alpine AS generated
ENV GOTOOLCHAIN=local

View File

@ -60,6 +60,8 @@ func (opts *ListOpts) Set(value string) error {
}
// Delete removes the specified element from the slice.
//
// Deprecated: this method is no longer used and will be removed in the next release.
func (opts *ListOpts) Delete(key string) {
for i, k := range *opts.values {
if k == key {
@ -264,6 +266,8 @@ func ValidateIPAddress(val string) (string, error) {
}
// ValidateMACAddress validates a MAC address.
//
// Deprecated: use [net.ParseMAC]. This function will be removed in the next release.
func ValidateMACAddress(val string) (string, error) {
_, err := net.ParseMAC(strings.TrimSpace(val))
if err != nil {

View File

@ -142,18 +142,14 @@ func TestListOptsWithoutValidator(t *testing.T) {
if o.Get("baz") {
t.Error(`o.Get("baz") == true`)
}
o.Delete("foo")
if o.String() != "[bar bar]" {
t.Errorf("%s != [bar bar]", o.String())
if listOpts := o.GetSlice(); len(listOpts) != 3 || listOpts[0] != "foo" || listOpts[1] != "bar" || listOpts[2] != "bar" {
t.Errorf("Expected [[foo bar bar]], got [%v]", listOpts)
}
if listOpts := o.GetAll(); len(listOpts) != 2 || listOpts[0] != "bar" || listOpts[1] != "bar" {
t.Errorf("Expected [[bar bar]], got [%v]", listOpts)
if listOpts := o.GetAll(); len(listOpts) != 3 || listOpts[0] != "foo" || listOpts[1] != "bar" || listOpts[2] != "bar" {
t.Errorf("Expected [[foo bar bar]], got [%v]", listOpts)
}
if listOpts := o.GetSlice(); len(listOpts) != 2 || listOpts[0] != "bar" || listOpts[1] != "bar" {
t.Errorf("Expected [[bar bar]], got [%v]", listOpts)
}
if mapListOpts := o.GetMap(); len(mapListOpts) != 1 {
t.Errorf("Expected [map[bar:{}]], got [%v]", mapListOpts)
if mapListOpts := o.GetMap(); len(mapListOpts) != 2 {
t.Errorf("Expected [map[bar:{} foo:{}]], got [%v]", mapListOpts)
}
}
@ -186,9 +182,8 @@ func TestListOptsWithValidator(t *testing.T) {
if o.Get("baz") {
t.Error(`o.Get("baz") == true`)
}
o.Delete("valid-option2=2")
if o.String() != "" {
t.Errorf(`%s != ""`, o.String())
if expected := "[valid-option2=2]"; o.String() != expected {
t.Errorf(`%s != %q`, o.String(), expected)
}
}
@ -396,20 +391,6 @@ func TestNamedMapOpts(t *testing.T) {
}
}
func TestValidateMACAddress(t *testing.T) {
if _, err := ValidateMACAddress(`92:d0:c6:0a:29:33`); err != nil {
t.Fatalf("ValidateMACAddress(`92:d0:c6:0a:29:33`) got %s", err)
}
if _, err := ValidateMACAddress(`92:d0:c6:0a:33`); err == nil {
t.Fatalf("ValidateMACAddress(`92:d0:c6:0a:33`) succeeded; expected failure on invalid MAC")
}
if _, err := ValidateMACAddress(`random invalid string`); err == nil {
t.Fatalf("ValidateMACAddress(`random invalid string`) succeeded; expected failure on invalid MAC")
}
}
func TestValidateLink(t *testing.T) {
valid := []string{
"name",

View File

@ -16,7 +16,7 @@ require (
github.com/distribution/reference v0.6.0
github.com/docker/cli-docs-tool v0.10.0
github.com/docker/distribution v2.8.3+incompatible
github.com/docker/docker v28.5.0-rc.1.0.20251001152618-cd048300a487+incompatible // 28.x branch (v28.5.0-dev)
github.com/docker/docker v28.5.1+incompatible
github.com/docker/docker-credential-helpers v0.9.3
github.com/docker/go-connections v0.6.0
github.com/docker/go-units v0.5.0

View File

@ -57,8 +57,8 @@ github.com/docker/cli-docs-tool v0.10.0/go.mod h1:5EM5zPnT2E7yCLERZmrDA234Vwn09f
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v28.5.0-rc.1.0.20251001152618-cd048300a487+incompatible h1:XSOGPdFlTV1ciuzfKp+9/qcx3Ukka+95OoMnty1wFaI=
github.com/docker/docker v28.5.0-rc.1.0.20251001152618-cd048300a487+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v28.5.1+incompatible h1:Bm8DchhSD2J6PsFzxC35TZo4TLGR2PdW/E69rU45NhM=
github.com/docker/docker v28.5.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8=
github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo=
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=

View File

@ -1938,6 +1938,11 @@ definitions:
Depending on how the image was created, this field may be empty and
is only set for images that were built/created locally. This field
is empty if the image was pulled from an image registry.
> **Deprecated**: This field is only set when using the deprecated
> legacy builder. It is included in API responses for informational
> purposes, but should not be depended on as it will be omitted
> once the legacy builder is removed.
type: "string"
x-nullable: false
example: ""
@ -1963,6 +1968,11 @@ definitions:
The version of Docker that was used to build the image.
Depending on how the image was created, this field may be empty.
> **Deprecated**: This field is only set when using the deprecated
> legacy builder. It is included in API responses for informational
> purposes, but should not be depended on as it will be omitted
> once the legacy builder is removed.
type: "string"
x-nullable: false
example: "27.0.1"
@ -3132,10 +3142,15 @@ definitions:
- Args
properties:
DockerVersion:
description: "Docker Version used to create the plugin"
description: |-
Docker Version used to create the plugin.
Depending on how the plugin was created, this field may be empty or omitted.
Deprecated: this field is no longer set, and will be removed in the next API version.
type: "string"
x-nullable: false
example: "17.06.0-ce"
x-omitempty: true
Description:
type: "string"
x-nullable: false

View File

@ -48,6 +48,8 @@ type InspectResponse struct {
// Depending on how the image was created, this field may be empty and
// is only set for images that were built/created locally. This field
// is empty if the image was pulled from an image registry.
//
// Deprecated: this field is deprecated, and will be removed in the next release.
Parent string
// Comment is an optional message that can be set when committing or
@ -80,6 +82,8 @@ type InspectResponse struct {
// DockerVersion is the version of Docker that was used to build the image.
//
// Depending on how the image was created, this field may be empty.
//
// Deprecated: this field is deprecated, and will be removed in the next release.
DockerVersion string
// Author is the name of the author that was specified when committing the

View File

@ -42,7 +42,11 @@ type PluginConfig struct {
// Required: true
Description string `json:"Description"`
// Docker Version used to create the plugin
// Docker Version used to create the plugin.
//
// Depending on how the plugin was created, this field may be empty or omitted.
//
// Deprecated: this field is no longer set, and will be removed in the next API version.
DockerVersion string `json:"DockerVersion,omitempty"`
// documentation

2
vendor/modules.txt vendored
View File

@ -65,7 +65,7 @@ github.com/docker/distribution/registry/client/transport
github.com/docker/distribution/registry/storage/cache
github.com/docker/distribution/registry/storage/cache/memory
github.com/docker/distribution/uuid
# github.com/docker/docker v28.5.0-rc.1.0.20251001152618-cd048300a487+incompatible
# github.com/docker/docker v28.5.1+incompatible
## explicit
github.com/docker/docker/api
github.com/docker/docker/api/types