Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ecc694264d | |||
| c475c696cf | |||
| 7494d2cee4 | |||
| 5306df36fa | |||
| 5dbaa52325 | |||
| dd832b6d97 | |||
| a99e91cc89 | |||
| 579b72aa06 | |||
| 6fc5891770 | |||
| 9af6cbc489 | |||
| ba53322412 | |||
| 61d2e8aaad | |||
| 6abcd4a2a1 | |||
| 00c129b974 | |||
| fbf5c8a86d | |||
| 4c7d52534f | |||
| 70123c523c | |||
| 4942b2747f | |||
| d8bab71747 | |||
| 36d9523e31 | |||
| 7063a0ef07 | |||
| 22487ad6f6 | |||
| 4737ed4906 | |||
| 8cff0087da | |||
| e8b22ce03c | |||
| e180ab8ab8 | |||
| 0d799c556f |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -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/*
|
||||
|
||||
8
.github/workflows/codeql.yml
vendored
8
.github/workflows/codeql.yml
vendored
@ -63,17 +63,17 @@ jobs:
|
||||
name: Update Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: "1.24.7"
|
||||
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"
|
||||
|
||||
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@ -67,7 +67,7 @@ jobs:
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: "1.24.7"
|
||||
go-version: "1.24.9"
|
||||
-
|
||||
name: Test
|
||||
run: |
|
||||
|
||||
@ -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.7"
|
||||
go: "1.24.9"
|
||||
|
||||
timeout: 5m
|
||||
|
||||
|
||||
@ -8,13 +8,13 @@ ARG BASE_VARIANT=alpine
|
||||
ARG ALPINE_VERSION=3.21
|
||||
ARG BASE_DEBIAN_DISTRO=bookworm
|
||||
|
||||
ARG GO_VERSION=1.24.7
|
||||
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
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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"`
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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"}}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 (
|
||||
|
||||
@ -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(),
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
variable "GO_VERSION" {
|
||||
default = "1.24.7"
|
||||
default = "1.24.9"
|
||||
}
|
||||
variable "VERSION" {
|
||||
default = ""
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG GO_VERSION=1.24.7
|
||||
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/ \
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG GO_VERSION=1.24.7
|
||||
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
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG GO_VERSION=1.24.7
|
||||
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
|
||||
|
||||
2
e2e/testdata/Dockerfile.gencerts
vendored
2
e2e/testdata/Dockerfile.gencerts
vendored
@ -1,6 +1,6 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG GO_VERSION=1.24.7
|
||||
ARG GO_VERSION=1.24.9
|
||||
|
||||
FROM golang:${GO_VERSION}-alpine AS generated
|
||||
ENV GOTOOLCHAIN=local
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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=
|
||||
|
||||
19
vendor/github.com/docker/docker/api/swagger.yaml
generated
vendored
19
vendor/github.com/docker/docker/api/swagger.yaml
generated
vendored
@ -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
|
||||
|
||||
4
vendor/github.com/docker/docker/api/types/image/image_inspect.go
generated
vendored
4
vendor/github.com/docker/docker/api/types/image/image_inspect.go
generated
vendored
@ -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
|
||||
|
||||
6
vendor/github.com/docker/docker/api/types/plugin.go
generated
vendored
6
vendor/github.com/docker/docker/api/types/plugin.go
generated
vendored
@ -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
2
vendor/modules.txt
vendored
@ -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
|
||||
|
||||
Reference in New Issue
Block a user