mirror of
https://github.com/docker/cli.git
synced 2026-08-24 10:05:37 -05:00
Compare commits
@@ -1,11 +1,17 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: "Contributing to Docker"
|
||||
- name: "🗃️ Docker Sandboxes: report an issue"
|
||||
about: "Issues with Docker Sandboxes should be filed in the Docker Desktop feedback tracker (not docker/cli)."
|
||||
url: "https://github.com/docker/desktop-feedback/issues"
|
||||
- name: "🖥️ Docker Desktop: report an issue"
|
||||
about: "General Docker Desktop issues (installation, upgrades, UI, networking on macOS/Windows, WSL2, etc.) should be filed in the Docker Desktop feedback tracker (not docker/cli)."
|
||||
url: "https://github.com/docker/desktop-feedback/issues"
|
||||
- name: "🧑💻 Contributing to Docker"
|
||||
about: "Read guidelines and tips about contributing to Docker."
|
||||
url: "https://github.com/docker/cli/blob/master/CONTRIBUTING.md"
|
||||
- name: "Security and Vulnerabilities"
|
||||
- name: "🔒 Security and Vulnerabilities"
|
||||
about: "Report any security issues or vulnerabilities responsibly to the Docker security team. Do not use the public issue tracker."
|
||||
url: "https://github.com/moby/moby/security/policy"
|
||||
- name: "General Support"
|
||||
- name: "💬 General Support"
|
||||
about: "Get the help you need to build, share, and run your Docker applications"
|
||||
url: "https://www.docker.com/support/"
|
||||
|
||||
@@ -7,3 +7,5 @@ updates:
|
||||
labels:
|
||||
- "area/testing"
|
||||
- "status/2-code-review"
|
||||
cooldown:
|
||||
default-days: 7
|
||||
|
||||
+12
-12
@@ -35,7 +35,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
-
|
||||
name: Create matrix
|
||||
id: platforms
|
||||
@@ -63,10 +63,10 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
||||
-
|
||||
name: Build
|
||||
uses: docker/bake-action@v6
|
||||
uses: docker/bake-action@82490499d2e5613fcead7e128237ef0b0ea210f7 # v7
|
||||
with:
|
||||
targets: ${{ matrix.target }}
|
||||
set: |
|
||||
@@ -88,7 +88,7 @@ jobs:
|
||||
fi
|
||||
-
|
||||
name: Upload artifacts
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
|
||||
with:
|
||||
name: ${{ env.ARTIFACT_NAME }}
|
||||
path: /tmp/out/*
|
||||
@@ -101,20 +101,20 @@ jobs:
|
||||
-
|
||||
name: Login to DockerHub
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_CLIBIN_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_CLIBIN_TOKEN }}
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
||||
-
|
||||
name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6
|
||||
with:
|
||||
images: dockereng/cli-bin
|
||||
tags: |
|
||||
@@ -125,7 +125,7 @@ jobs:
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
-
|
||||
name: Build and push image
|
||||
uses: docker/bake-action@v6
|
||||
uses: docker/bake-action@82490499d2e5613fcead7e128237ef0b0ea210f7 # v7
|
||||
with:
|
||||
files: |
|
||||
./docker-bake.hcl
|
||||
@@ -143,7 +143,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
-
|
||||
name: Create matrix
|
||||
id: platforms
|
||||
@@ -165,10 +165,10 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
||||
-
|
||||
name: Build
|
||||
uses: docker/bake-action@v6
|
||||
uses: docker/bake-action@82490499d2e5613fcead7e128237ef0b0ea210f7 # v7
|
||||
with:
|
||||
targets: plugins-cross
|
||||
set: |
|
||||
|
||||
@@ -46,7 +46,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
fetch-depth: 2
|
||||
# CodeQL 2.16.4's auto-build added support for multi-module repositories,
|
||||
@@ -61,19 +61,20 @@ jobs:
|
||||
ln -s vendor.sum go.sum
|
||||
-
|
||||
name: Update Go
|
||||
uses: actions/setup-go@v6
|
||||
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6
|
||||
with:
|
||||
go-version: "1.25.6"
|
||||
go-version: "1.25.8"
|
||||
cache: false
|
||||
-
|
||||
name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v4
|
||||
uses: github/codeql-action/init@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1
|
||||
with:
|
||||
languages: go
|
||||
-
|
||||
name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v4
|
||||
uses: github/codeql-action/autobuild@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1
|
||||
-
|
||||
name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v4
|
||||
uses: github/codeql-action/analyze@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1
|
||||
with:
|
||||
category: "/language:go"
|
||||
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
-
|
||||
name: Update daemon.json
|
||||
run: |
|
||||
@@ -63,7 +63,7 @@ jobs:
|
||||
docker info
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
||||
-
|
||||
name: Run ${{ matrix.target }}
|
||||
run: |
|
||||
@@ -74,7 +74,7 @@ jobs:
|
||||
TESTFLAGS: -coverprofile=/tmp/coverage/coverage.txt
|
||||
-
|
||||
name: Send to Codecov
|
||||
uses: codecov/codecov-action@v5
|
||||
uses: codecov/codecov-action@1af58845a975a7985b0beb0cbe6fbbb71a41dbad # v5
|
||||
with:
|
||||
files: ./build/coverage/coverage.txt
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
@@ -30,15 +30,15 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
||||
-
|
||||
name: Test
|
||||
uses: docker/bake-action@v6
|
||||
uses: docker/bake-action@82490499d2e5613fcead7e128237ef0b0ea210f7 # v7
|
||||
with:
|
||||
targets: test-coverage
|
||||
-
|
||||
name: Send to Codecov
|
||||
uses: codecov/codecov-action@v5
|
||||
uses: codecov/codecov-action@1af58845a975a7985b0beb0cbe6fbbb71a41dbad # v5
|
||||
with:
|
||||
files: ./build/coverage/coverage.txt
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
@@ -60,14 +60,15 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
path: ${{ env.GOPATH }}/src/github.com/docker/cli
|
||||
-
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@v6
|
||||
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6
|
||||
with:
|
||||
go-version: "1.25.6"
|
||||
go-version: "1.25.8"
|
||||
cache: false
|
||||
-
|
||||
name: Test
|
||||
run: |
|
||||
@@ -80,7 +81,7 @@ jobs:
|
||||
shell: bash
|
||||
-
|
||||
name: Send to Codecov
|
||||
uses: codecov/codecov-action@v5
|
||||
uses: codecov/codecov-action@1af58845a975a7985b0beb0cbe6fbbb71a41dbad # v5
|
||||
with:
|
||||
files: /tmp/coverage.txt
|
||||
working-directory: ${{ env.GOPATH }}/src/github.com/docker/cli
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Run
|
||||
uses: docker/bake-action@v6
|
||||
uses: docker/bake-action@82490499d2e5613fcead7e128237ef0b0ea210f7 # v7
|
||||
with:
|
||||
targets: ${{ matrix.target }}
|
||||
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
-
|
||||
name: Generate
|
||||
shell: 'script --return --quiet --command "bash {0}"'
|
||||
@@ -74,7 +74,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
-
|
||||
name: Run
|
||||
shell: 'script --return --quiet --command "bash {0}"'
|
||||
|
||||
+1
-1
@@ -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.25.6"
|
||||
go: "1.25.8"
|
||||
|
||||
timeout: 5m
|
||||
|
||||
|
||||
+5
-5
@@ -5,14 +5,14 @@ ARG BASE_VARIANT=alpine
|
||||
# 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.22
|
||||
ARG ALPINE_VERSION=3.23
|
||||
ARG BASE_DEBIAN_DISTRO=bookworm
|
||||
|
||||
ARG GO_VERSION=1.25.6
|
||||
ARG GO_VERSION=1.25.8
|
||||
|
||||
# XX_VERSION specifies the version of the xx utility to use.
|
||||
# It must be a valid tag in the docker.io/tonistiigi/xx image repository.
|
||||
ARG XX_VERSION=1.7.0
|
||||
ARG XX_VERSION=1.9.0
|
||||
|
||||
# GOVERSIONINFO_VERSION is the version of GoVersionInfo to install.
|
||||
# It must be a valid tag from https://github.com/josephspurrier/goversioninfo
|
||||
@@ -25,12 +25,12 @@ 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
|
||||
# on Docker Hub.
|
||||
ARG BUILDX_VERSION=0.29.1
|
||||
ARG BUILDX_VERSION=0.31.1
|
||||
|
||||
# COMPOSE_VERSION is the version of compose to install in the dev container.
|
||||
# It must be a tag in the docker.io/docker/compose-bin image repository
|
||||
# on Docker Hub.
|
||||
ARG COMPOSE_VERSION=v2.40.0
|
||||
ARG COMPOSE_VERSION=v5.1.0
|
||||
|
||||
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
|
||||
|
||||
|
||||
@@ -151,7 +151,7 @@ func TestValidateCandidate(t *testing.T) {
|
||||
assert.ErrorContains(t, err, tc.err)
|
||||
case tc.invalid != "":
|
||||
assert.NilError(t, err)
|
||||
assert.Assert(t, is.ErrorType(p.Err, reflect.TypeOf(&pluginError{})))
|
||||
assert.Assert(t, is.ErrorType(p.Err, reflect.TypeFor[*pluginError]()))
|
||||
assert.ErrorContains(t, p.Err, tc.invalid)
|
||||
default:
|
||||
assert.NilError(t, err)
|
||||
|
||||
@@ -57,7 +57,7 @@ func AddPluginCommandStubs(dockerCLI config.Provider, rootCmd *cobra.Command) (e
|
||||
},
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
// Delegate completion to plugin
|
||||
cargs := []string{p.Path, cobra.ShellCompRequestCmd, p.Name}
|
||||
cargs := []string{p.Path, cobra.ShellCompRequestCmd, p.Name} //nolint:prealloc // no need to over-complicate things.
|
||||
cargs = append(cargs, args...)
|
||||
cargs = append(cargs, toComplete)
|
||||
os.Args = cargs
|
||||
|
||||
@@ -28,7 +28,7 @@ func (e *pluginError) Unwrap() error {
|
||||
return e.cause
|
||||
}
|
||||
|
||||
// MarshalText marshalls the pluginError into a textual form.
|
||||
// MarshalText marshals the pluginError into a textual form.
|
||||
func (e *pluginError) MarshalText() (text []byte, err error) {
|
||||
return []byte(e.cause.Error()), nil
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.24
|
||||
|
||||
package manager
|
||||
|
||||
import (
|
||||
@@ -39,11 +42,11 @@ func RunCLICommandHooks(ctx context.Context, dockerCLI config.Provider, rootCmd,
|
||||
|
||||
// RunPluginHooks is the entrypoint for the hooks execution flow
|
||||
// after a plugin command was just executed by the CLI.
|
||||
func RunPluginHooks(ctx context.Context, dockerCLI config.Provider, rootCmd, subCommand *cobra.Command, args []string) {
|
||||
func RunPluginHooks(ctx context.Context, dockerCLI config.Provider, rootCmd, subCommand *cobra.Command, args []string, cmdErrorMessage string) {
|
||||
commandName := strings.Join(args, " ")
|
||||
flags := getNaiveFlags(args)
|
||||
|
||||
runHooks(ctx, dockerCLI.ConfigFile(), rootCmd, subCommand, commandName, flags, "")
|
||||
runHooks(ctx, dockerCLI.ConfigFile(), rootCmd, subCommand, commandName, flags, cmdErrorMessage)
|
||||
}
|
||||
|
||||
func runHooks(ctx context.Context, cfg *configfile.ConfigFile, rootCmd, subCommand *cobra.Command, invokedCommand string, flags map[string]string, cmdErrorMessage string) {
|
||||
@@ -67,7 +70,7 @@ func invokeAndCollectHooks(ctx context.Context, cfg *configfile.ConfigFile, root
|
||||
pluginDirs := getPluginDirs(cfg)
|
||||
nextSteps := make([]string, 0, len(pluginsCfg))
|
||||
for pluginName, pluginCfg := range pluginsCfg {
|
||||
match, ok := pluginMatch(pluginCfg, subCmdStr)
|
||||
match, ok := pluginMatch(pluginCfg, subCmdStr, cmdErrorMessage)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
@@ -135,14 +138,34 @@ func appendNextSteps(nextSteps []string, processed []string) ([]string, bool) {
|
||||
// command being executed (such as 'image ls' – the root 'docker' is omitted)
|
||||
// and, if the configuration includes a hook for the invoked command, returns
|
||||
// the configured hook string.
|
||||
func pluginMatch(pluginCfg map[string]string, subCmd string) (string, bool) {
|
||||
configuredPluginHooks, ok := pluginCfg["hooks"]
|
||||
if !ok || configuredPluginHooks == "" {
|
||||
//
|
||||
// Plugins can declare two types of hooks in their configuration:
|
||||
// - "hooks": fires on every command invocation (success or failure)
|
||||
// - "error-hooks": fires only when a command fails (cmdErrorMessage is non-empty)
|
||||
func pluginMatch(pluginCfg map[string]string, subCmd string, cmdErrorMessage string) (string, bool) {
|
||||
// Check "hooks" first — these always fire regardless of command outcome.
|
||||
if match, ok := matchHookConfig(pluginCfg["hooks"], subCmd); ok {
|
||||
return match, true
|
||||
}
|
||||
|
||||
// Check "error-hooks" — these only fire when there was an error.
|
||||
if cmdErrorMessage != "" {
|
||||
if match, ok := matchHookConfig(pluginCfg["error-hooks"], subCmd); ok {
|
||||
return match, true
|
||||
}
|
||||
}
|
||||
|
||||
return "", false
|
||||
}
|
||||
|
||||
// matchHookConfig checks if a comma-separated hook configuration string
|
||||
// contains a prefix match for the given subcommand.
|
||||
func matchHookConfig(configuredHooks string, subCmd string) (string, bool) {
|
||||
if configuredHooks == "" {
|
||||
return "", false
|
||||
}
|
||||
|
||||
commands := strings.Split(configuredPluginHooks, ",")
|
||||
for _, hookCmd := range commands {
|
||||
for hookCmd := range strings.SplitSeq(configuredHooks, ",") {
|
||||
if hookMatch(hookCmd, subCmd) {
|
||||
return hookCmd, true
|
||||
}
|
||||
|
||||
@@ -1,12 +1,23 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
"github.com/spf13/cobra"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
)
|
||||
|
||||
type fakeConfigProvider struct {
|
||||
cfg *configfile.ConfigFile
|
||||
}
|
||||
|
||||
func (f *fakeConfigProvider) ConfigFile() *configfile.ConfigFile {
|
||||
return f.cfg
|
||||
}
|
||||
|
||||
func TestGetNaiveFlags(t *testing.T) {
|
||||
testCases := []struct {
|
||||
args []string
|
||||
@@ -40,12 +51,15 @@ func TestGetNaiveFlags(t *testing.T) {
|
||||
|
||||
func TestPluginMatch(t *testing.T) {
|
||||
testCases := []struct {
|
||||
commandString string
|
||||
pluginConfig map[string]string
|
||||
expectedMatch string
|
||||
expectedOk bool
|
||||
doc string
|
||||
commandString string
|
||||
pluginConfig map[string]string
|
||||
cmdErrorMessage string
|
||||
expectedMatch string
|
||||
expectedOk bool
|
||||
}{
|
||||
{
|
||||
doc: "hooks prefix match",
|
||||
commandString: "image ls",
|
||||
pluginConfig: map[string]string{
|
||||
"hooks": "image",
|
||||
@@ -54,6 +68,7 @@ func TestPluginMatch(t *testing.T) {
|
||||
expectedOk: true,
|
||||
},
|
||||
{
|
||||
doc: "hooks no match",
|
||||
commandString: "context ls",
|
||||
pluginConfig: map[string]string{
|
||||
"hooks": "build",
|
||||
@@ -62,6 +77,7 @@ func TestPluginMatch(t *testing.T) {
|
||||
expectedOk: false,
|
||||
},
|
||||
{
|
||||
doc: "hooks exact match",
|
||||
commandString: "context ls",
|
||||
pluginConfig: map[string]string{
|
||||
"hooks": "context ls",
|
||||
@@ -70,6 +86,7 @@ func TestPluginMatch(t *testing.T) {
|
||||
expectedOk: true,
|
||||
},
|
||||
{
|
||||
doc: "hooks first match wins",
|
||||
commandString: "image ls",
|
||||
pluginConfig: map[string]string{
|
||||
"hooks": "image ls,image",
|
||||
@@ -78,6 +95,7 @@ func TestPluginMatch(t *testing.T) {
|
||||
expectedOk: true,
|
||||
},
|
||||
{
|
||||
doc: "hooks empty string",
|
||||
commandString: "image ls",
|
||||
pluginConfig: map[string]string{
|
||||
"hooks": "",
|
||||
@@ -86,6 +104,7 @@ func TestPluginMatch(t *testing.T) {
|
||||
expectedOk: false,
|
||||
},
|
||||
{
|
||||
doc: "hooks partial token no match",
|
||||
commandString: "image inspect",
|
||||
pluginConfig: map[string]string{
|
||||
"hooks": "image i",
|
||||
@@ -94,6 +113,7 @@ func TestPluginMatch(t *testing.T) {
|
||||
expectedOk: false,
|
||||
},
|
||||
{
|
||||
doc: "hooks prefix token match",
|
||||
commandString: "image inspect",
|
||||
pluginConfig: map[string]string{
|
||||
"hooks": "image",
|
||||
@@ -101,12 +121,140 @@ func TestPluginMatch(t *testing.T) {
|
||||
expectedMatch: "image",
|
||||
expectedOk: true,
|
||||
},
|
||||
{
|
||||
doc: "error-hooks match on error",
|
||||
commandString: "build",
|
||||
pluginConfig: map[string]string{
|
||||
"error-hooks": "build",
|
||||
},
|
||||
cmdErrorMessage: "exit status 1",
|
||||
expectedMatch: "build",
|
||||
expectedOk: true,
|
||||
},
|
||||
{
|
||||
doc: "error-hooks no match on success",
|
||||
commandString: "build",
|
||||
pluginConfig: map[string]string{
|
||||
"error-hooks": "build",
|
||||
},
|
||||
cmdErrorMessage: "",
|
||||
expectedMatch: "",
|
||||
expectedOk: false,
|
||||
},
|
||||
{
|
||||
doc: "error-hooks prefix match on error",
|
||||
commandString: "compose up",
|
||||
pluginConfig: map[string]string{
|
||||
"error-hooks": "compose",
|
||||
},
|
||||
cmdErrorMessage: "exit status 1",
|
||||
expectedMatch: "compose",
|
||||
expectedOk: true,
|
||||
},
|
||||
{
|
||||
doc: "error-hooks no match for wrong command",
|
||||
commandString: "pull",
|
||||
pluginConfig: map[string]string{
|
||||
"error-hooks": "build",
|
||||
},
|
||||
cmdErrorMessage: "exit status 1",
|
||||
expectedMatch: "",
|
||||
expectedOk: false,
|
||||
},
|
||||
{
|
||||
doc: "hooks takes precedence over error-hooks",
|
||||
commandString: "build",
|
||||
pluginConfig: map[string]string{
|
||||
"hooks": "build",
|
||||
"error-hooks": "build",
|
||||
},
|
||||
cmdErrorMessage: "exit status 1",
|
||||
expectedMatch: "build",
|
||||
expectedOk: true,
|
||||
},
|
||||
{
|
||||
doc: "hooks fires on success even with error-hooks configured",
|
||||
commandString: "build",
|
||||
pluginConfig: map[string]string{
|
||||
"hooks": "build",
|
||||
"error-hooks": "build",
|
||||
},
|
||||
cmdErrorMessage: "",
|
||||
expectedMatch: "build",
|
||||
expectedOk: true,
|
||||
},
|
||||
{
|
||||
doc: "error-hooks with multiple commands",
|
||||
commandString: "compose up",
|
||||
pluginConfig: map[string]string{
|
||||
"error-hooks": "build,compose up,pull",
|
||||
},
|
||||
cmdErrorMessage: "exit status 1",
|
||||
expectedMatch: "compose up",
|
||||
expectedOk: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
match, ok := pluginMatch(tc.pluginConfig, tc.commandString)
|
||||
assert.Equal(t, ok, tc.expectedOk)
|
||||
assert.Equal(t, match, tc.expectedMatch)
|
||||
t.Run(tc.doc, func(t *testing.T) {
|
||||
match, ok := pluginMatch(tc.pluginConfig, tc.commandString, tc.cmdErrorMessage)
|
||||
assert.Equal(t, ok, tc.expectedOk)
|
||||
assert.Equal(t, match, tc.expectedMatch)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchHookConfig(t *testing.T) {
|
||||
testCases := []struct {
|
||||
doc string
|
||||
configuredHooks string
|
||||
subCmd string
|
||||
expectedMatch string
|
||||
expectedOk bool
|
||||
}{
|
||||
{
|
||||
doc: "empty config",
|
||||
configuredHooks: "",
|
||||
subCmd: "build",
|
||||
expectedMatch: "",
|
||||
expectedOk: false,
|
||||
},
|
||||
{
|
||||
doc: "exact match",
|
||||
configuredHooks: "build",
|
||||
subCmd: "build",
|
||||
expectedMatch: "build",
|
||||
expectedOk: true,
|
||||
},
|
||||
{
|
||||
doc: "prefix match",
|
||||
configuredHooks: "image",
|
||||
subCmd: "image ls",
|
||||
expectedMatch: "image",
|
||||
expectedOk: true,
|
||||
},
|
||||
{
|
||||
doc: "comma-separated match",
|
||||
configuredHooks: "pull,build,push",
|
||||
subCmd: "build",
|
||||
expectedMatch: "build",
|
||||
expectedOk: true,
|
||||
},
|
||||
{
|
||||
doc: "no match",
|
||||
configuredHooks: "pull,push",
|
||||
subCmd: "build",
|
||||
expectedMatch: "",
|
||||
expectedOk: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.doc, func(t *testing.T) {
|
||||
match, ok := matchHookConfig(tc.configuredHooks, tc.subCmd)
|
||||
assert.Equal(t, ok, tc.expectedOk)
|
||||
assert.Equal(t, match, tc.expectedMatch)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,3 +289,87 @@ func TestAppendNextSteps(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunPluginHooksPassesErrorMessage(t *testing.T) {
|
||||
cfg := configfile.New("")
|
||||
cfg.Plugins = map[string]map[string]string{
|
||||
"test-plugin": {"hooks": "build"},
|
||||
}
|
||||
provider := &fakeConfigProvider{cfg: cfg}
|
||||
root := &cobra.Command{Use: "docker"}
|
||||
sub := &cobra.Command{Use: "build"}
|
||||
root.AddCommand(sub)
|
||||
|
||||
// Should not panic with empty error message (success case)
|
||||
RunPluginHooks(context.Background(), provider, root, sub, []string{"build"}, "")
|
||||
|
||||
// Should not panic with non-empty error message (failure case)
|
||||
RunPluginHooks(context.Background(), provider, root, sub, []string{"build"}, "exit status 1")
|
||||
}
|
||||
|
||||
func TestRunPluginHooksErrorHooks(t *testing.T) {
|
||||
cfg := configfile.New("")
|
||||
cfg.Plugins = map[string]map[string]string{
|
||||
"test-plugin": {"error-hooks": "build"},
|
||||
}
|
||||
provider := &fakeConfigProvider{cfg: cfg}
|
||||
root := &cobra.Command{Use: "docker"}
|
||||
sub := &cobra.Command{Use: "build"}
|
||||
root.AddCommand(sub)
|
||||
|
||||
// Should not panic — error-hooks with error message
|
||||
RunPluginHooks(context.Background(), provider, root, sub, []string{"build"}, "exit status 1")
|
||||
|
||||
// Should not panic — error-hooks with no error (should be skipped)
|
||||
RunPluginHooks(context.Background(), provider, root, sub, []string{"build"}, "")
|
||||
}
|
||||
|
||||
func TestInvokeAndCollectHooksErrorHooksSkippedOnSuccess(t *testing.T) {
|
||||
cfg := configfile.New("")
|
||||
cfg.Plugins = map[string]map[string]string{
|
||||
"nonexistent": {"error-hooks": "build"},
|
||||
}
|
||||
root := &cobra.Command{Use: "docker"}
|
||||
sub := &cobra.Command{Use: "build"}
|
||||
root.AddCommand(sub)
|
||||
|
||||
// On success, error-hooks should not match, so the plugin
|
||||
// binary is never looked up and no results are returned.
|
||||
result := invokeAndCollectHooks(
|
||||
context.Background(), cfg, root, sub,
|
||||
"build", map[string]string{}, "",
|
||||
)
|
||||
assert.Check(t, is.Len(result, 0))
|
||||
}
|
||||
|
||||
func TestInvokeAndCollectHooksNoPlugins(t *testing.T) {
|
||||
cfg := configfile.New("")
|
||||
root := &cobra.Command{Use: "docker"}
|
||||
sub := &cobra.Command{Use: "build"}
|
||||
root.AddCommand(sub)
|
||||
|
||||
result := invokeAndCollectHooks(
|
||||
context.Background(), cfg, root, sub,
|
||||
"build", map[string]string{}, "some error",
|
||||
)
|
||||
assert.Check(t, is.Len(result, 0))
|
||||
}
|
||||
|
||||
func TestInvokeAndCollectHooksCancelledContext(t *testing.T) {
|
||||
cfg := configfile.New("")
|
||||
cfg.Plugins = map[string]map[string]string{
|
||||
"test-plugin": {"hooks": "build"},
|
||||
}
|
||||
root := &cobra.Command{Use: "docker"}
|
||||
sub := &cobra.Command{Use: "build"}
|
||||
root.AddCommand(sub)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel() // cancel immediately
|
||||
|
||||
result := invokeAndCollectHooks(
|
||||
ctx, cfg, root, sub,
|
||||
"build", map[string]string{}, "exit status 1",
|
||||
)
|
||||
assert.Check(t, is.Nil(result))
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ func ListPlugins(dockerCli config.Provider, rootcmd *cobra.Command) ([]Plugin, e
|
||||
}
|
||||
|
||||
// PluginRunCommand returns an [os/exec.Cmd] which when [os/exec.Cmd.Run] will execute the named plugin.
|
||||
// The rootcmd argument is referenced to determine the set of builtin commands in order to detect conficts.
|
||||
// The rootcmd argument is referenced to determine the set of builtin commands in order to detect conflicts.
|
||||
// The error returned satisfies the [errdefs.IsNotFound] predicate if no plugin was found or if the first candidate plugin was invalid somehow.
|
||||
func PluginRunCommand(dockerCli config.Provider, name string, rootcmd *cobra.Command) (*exec.Cmd, error) {
|
||||
// This uses the full original args, not the args which may
|
||||
|
||||
@@ -89,7 +89,7 @@ func TestListPluginCandidatesEmpty(t *testing.T) {
|
||||
// Regression test for https://github.com/docker/cli/issues/5643.
|
||||
// Check that inaccessible directories that come before accessible ones are ignored
|
||||
// and do not prevent the latter from being processed.
|
||||
func TestListPluginCandidatesInaccesibleDir(t *testing.T) {
|
||||
func TestListPluginCandidatesInaccessibleDir(t *testing.T) {
|
||||
dir := fs.NewDir(t, t.Name(),
|
||||
fs.WithDir("no-perm", fs.WithMode(0)),
|
||||
fs.WithDir("plugins",
|
||||
|
||||
@@ -93,7 +93,7 @@ func (pl *PluginServer) Addr() net.Addr {
|
||||
// Close ensures that the server is no longer accepting new connections and
|
||||
// closes all existing connections. Existing connections will receive [io.EOF].
|
||||
//
|
||||
// The error value is that of the underlying [net.Listner.Close] call.
|
||||
// The error value is that of the underlying [net.Listener.Close] call.
|
||||
func (pl *PluginServer) Close() error {
|
||||
if pl == nil {
|
||||
return nil
|
||||
|
||||
@@ -47,7 +47,7 @@ func TestPluginServer(t *testing.T) {
|
||||
select {
|
||||
case err := <-done:
|
||||
if !errors.Is(err, io.EOF) {
|
||||
t.Fatalf("exepcted EOF error, got: %v", err)
|
||||
t.Fatalf("expected EOF error, got: %v", err)
|
||||
}
|
||||
case <-time.After(10 * time.Millisecond):
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// FIXME(vvoland): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.24
|
||||
|
||||
package container
|
||||
@@ -902,7 +902,7 @@ func convertToStandardNotation(ports []string) ([]string, error) {
|
||||
for _, publish := range ports {
|
||||
if strings.Contains(publish, "=") {
|
||||
params := map[string]string{"protocol": "tcp"}
|
||||
for _, param := range strings.Split(publish, ",") {
|
||||
for param := range strings.SplitSeq(publish, ",") {
|
||||
k, v, ok := strings.Cut(param, "=")
|
||||
if !ok || k == "" {
|
||||
return optsList, fmt.Errorf("invalid publish opts format (should be name=value but got '%s')", param)
|
||||
|
||||
@@ -16,8 +16,6 @@ import (
|
||||
"github.com/moby/moby/api/types"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/moby/moby/client/pkg/progress"
|
||||
"github.com/moby/moby/client/pkg/streamformatter"
|
||||
"github.com/spf13/pflag"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
@@ -129,7 +127,7 @@ func TestRunAttach(t *testing.T) {
|
||||
}
|
||||
|
||||
// end stream from "container" so that we'll detach
|
||||
conn.Close()
|
||||
assert.NilError(t, conn.Close())
|
||||
|
||||
select {
|
||||
case cmdErr := <-cmdErrC:
|
||||
@@ -209,7 +207,7 @@ func TestRunAttachTermination(t *testing.T) {
|
||||
}
|
||||
|
||||
assert.NilError(t, syscall.Kill(syscall.Getpid(), syscall.SIGTERM))
|
||||
conn.Close()
|
||||
assert.NilError(t, conn.Close())
|
||||
|
||||
select {
|
||||
case <-killCh:
|
||||
@@ -245,20 +243,12 @@ func TestRunPullTermination(t *testing.T) {
|
||||
_ = server.Close()
|
||||
})
|
||||
go func() {
|
||||
id := test.RandomID()[:12] // short-ID
|
||||
progressOutput := streamformatter.NewJSONProgressOutput(server, true)
|
||||
for i := 0; i < 100; i++ {
|
||||
for range 100 {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
assert.NilError(t, server.Close(), "failed to close imageCreateFunc server")
|
||||
return
|
||||
default:
|
||||
assert.NilError(t, progressOutput.WriteProgress(progress.Progress{
|
||||
ID: id,
|
||||
Message: "Downloading",
|
||||
Current: int64(i),
|
||||
Total: 100,
|
||||
}))
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,7 @@ import (
|
||||
)
|
||||
|
||||
func TestForwardSignals(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
ctx := t.Context()
|
||||
|
||||
called := make(chan struct{})
|
||||
apiClient := &fakeClient{containerKillFunc: func(ctx context.Context, container string, options client.ContainerKillOptions) (client.ContainerKillResult, error) {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.24
|
||||
|
||||
package container
|
||||
|
||||
import (
|
||||
@@ -337,7 +340,7 @@ func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions)
|
||||
return err
|
||||
}
|
||||
|
||||
for _, line := range strings.Split(statsTextBuffer.String(), "\n") {
|
||||
for line := range strings.SplitSeq(statsTextBuffer.String(), "\n") {
|
||||
// In case the new text is shorter than the one we are writing over,
|
||||
// we'll append the "erase line" escape sequence to clear the remaining text.
|
||||
_, _ = fmt.Fprintln(&statsTextBuffer, line, "\033[K")
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.24
|
||||
|
||||
package container
|
||||
|
||||
import (
|
||||
@@ -60,7 +63,7 @@ func initTtySize(ctx context.Context, cli command.Cli, id string, isExec bool, r
|
||||
if err := rTTYfunc(ctx, cli, id, isExec); err != nil {
|
||||
go func() {
|
||||
var err error
|
||||
for retry := 0; retry < 10; retry++ {
|
||||
for retry := range 10 {
|
||||
time.Sleep(time.Duration(retry+1) * 10 * time.Millisecond)
|
||||
if err = rTTYfunc(ctx, cli, id, isExec); err == nil {
|
||||
break
|
||||
|
||||
@@ -6,6 +6,7 @@ package command
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"maps"
|
||||
|
||||
"github.com/docker/cli/cli/context/store"
|
||||
)
|
||||
@@ -23,9 +24,7 @@ func (dc DockerContext) MarshalJSON() ([]byte, error) {
|
||||
s["Description"] = dc.Description
|
||||
}
|
||||
if dc.AdditionalFields != nil {
|
||||
for k, v := range dc.AdditionalFields {
|
||||
s[k] = v
|
||||
}
|
||||
maps.Copy(s, dc.AdditionalFields)
|
||||
}
|
||||
return json.Marshal(s)
|
||||
}
|
||||
|
||||
@@ -350,7 +350,7 @@ func DisplayablePorts(ports []container.PortSummary) string {
|
||||
last uint16
|
||||
}
|
||||
groupMap := make(map[string]*portGroup)
|
||||
var result []string //nolint:prealloc
|
||||
var result []string
|
||||
var hostMappings []string
|
||||
var groupMapKeys []string
|
||||
sort.Slice(ports, func(i, j int) bool {
|
||||
|
||||
@@ -25,7 +25,7 @@ func (b *buffer) Write(buf []byte) (written int, err error) {
|
||||
m := len(buf)
|
||||
if n+m <= cap(b.a) {
|
||||
b.a = b.a[0 : n+m]
|
||||
for i := 0; i < m; i++ {
|
||||
for i := range m {
|
||||
b.a[n+i] = buf[i]
|
||||
}
|
||||
} else {
|
||||
@@ -669,7 +669,7 @@ func BenchmarkTable(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
w := NewWriter(io.Discard, 4, 4, 1, ' ', 0) // no particular reason for these settings
|
||||
// Write the line h times.
|
||||
for j := 0; j < h; j++ {
|
||||
for range h {
|
||||
w.Write(line)
|
||||
}
|
||||
w.Flush()
|
||||
@@ -681,7 +681,7 @@ func BenchmarkTable(b *testing.B) {
|
||||
w := NewWriter(io.Discard, 4, 4, 1, ' ', 0) // no particular reason for these settings
|
||||
for i := 0; i < b.N; i++ {
|
||||
// Write the line h times.
|
||||
for j := 0; j < h; j++ {
|
||||
for range h {
|
||||
w.Write(line)
|
||||
}
|
||||
w.Flush()
|
||||
@@ -701,7 +701,7 @@ func BenchmarkPyramid(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
w := NewWriter(io.Discard, 4, 4, 1, ' ', 0) // no particular reason for these settings
|
||||
// Write increasing prefixes of that line.
|
||||
for j := 0; j < x; j++ {
|
||||
for j := range x {
|
||||
w.Write(line[:j*2])
|
||||
w.Write([]byte{'\n'})
|
||||
}
|
||||
@@ -723,7 +723,7 @@ func BenchmarkRagged(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
w := NewWriter(io.Discard, 4, 4, 1, ' ', 0) // no particular reason for these settings
|
||||
// Write the lines in turn h times.
|
||||
for j := 0; j < h; j++ {
|
||||
for j := range h {
|
||||
w.Write(lines[j%len(lines)])
|
||||
w.Write([]byte{'\n'})
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ func TestResolveWithCache(t *testing.T) {
|
||||
idResolver := New(apiClient, false)
|
||||
|
||||
ctx := context.Background()
|
||||
for i := 0; i < 2; i++ {
|
||||
for range 2 {
|
||||
id, err := idResolver.Resolve(ctx, swarm.Node{}, "nodeID")
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal("node-foo", id))
|
||||
|
||||
@@ -190,7 +190,7 @@ func (f *fakeBuild) build(_ context.Context, buildContext io.Reader, options cli
|
||||
|
||||
func (f *fakeBuild) headers(t *testing.T) []*tar.Header {
|
||||
t.Helper()
|
||||
headers := []*tar.Header{}
|
||||
var headers []*tar.Header
|
||||
for {
|
||||
hdr, err := f.context.Next()
|
||||
switch err {
|
||||
@@ -206,8 +206,9 @@ func (f *fakeBuild) headers(t *testing.T) []*tar.Header {
|
||||
|
||||
func (f *fakeBuild) filenames(t *testing.T) []string {
|
||||
t.Helper()
|
||||
names := []string{}
|
||||
for _, header := range f.headers(t) {
|
||||
h := f.headers(t)
|
||||
names := make([]string, 0, len(h))
|
||||
for _, header := range h {
|
||||
names = append(names, header.Name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
|
||||
@@ -389,10 +389,7 @@ func generateLegend(out tui.Output, width uint) string {
|
||||
}
|
||||
legend += legendSb371.String()
|
||||
|
||||
r := int(width) - tui.Width(legend)
|
||||
if r < 0 {
|
||||
r = 0
|
||||
}
|
||||
r := max(int(width)-tui.Width(legend), 0)
|
||||
legend = strings.Repeat(" ", r) + legend
|
||||
return legend
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.24
|
||||
|
||||
package manifest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
|
||||
"github.com/containerd/errdefs"
|
||||
"github.com/docker/cli/cli"
|
||||
@@ -164,10 +168,8 @@ func runManifestAnnotate(dockerCLI command.Cli, opts annotateOptions) error {
|
||||
}
|
||||
|
||||
func appendIfUnique(list []string, str string) []string {
|
||||
for _, s := range list {
|
||||
if s == str {
|
||||
return list
|
||||
}
|
||||
if slices.Contains(list, str) {
|
||||
return list
|
||||
}
|
||||
return append(list, str)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.24
|
||||
|
||||
package node
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
@@ -99,9 +103,7 @@ func mergeNodeUpdate(flags *pflag.FlagSet) func(*swarm.Node) error {
|
||||
}
|
||||
if flags.Changed(flagLabelAdd) {
|
||||
labels := flags.Lookup(flagLabelAdd).Value.(*opts.ListOpts).GetSlice()
|
||||
for k, v := range opts.ConvertKVStringsToMap(labels) {
|
||||
spec.Annotations.Labels[k] = v
|
||||
}
|
||||
maps.Copy(spec.Annotations.Labels, opts.ConvertKVStringsToMap(labels))
|
||||
}
|
||||
if flags.Changed(flagLabelRemove) {
|
||||
keys := flags.Lookup(flagLabelRemove).Value.(*opts.ListOpts).GetSlice()
|
||||
|
||||
@@ -144,8 +144,8 @@ func PromptUserForCredentials(ctx context.Context, cli Cli, argUser, argPassword
|
||||
}
|
||||
}
|
||||
|
||||
argPassword = strings.TrimSpace(argPassword)
|
||||
if argPassword == "" {
|
||||
isEmpty := strings.TrimSpace(argPassword) == ""
|
||||
if isEmpty {
|
||||
restoreInput, err := prompt.DisableInputEcho(cli.In())
|
||||
if err != nil {
|
||||
return registrytypes.AuthConfig{}, err
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -88,6 +89,38 @@ func verifyLoginFlags(flags *pflag.FlagSet, opts loginOptions) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// readSecretFromStdin reads the secret from r and returns it as a string.
|
||||
// It trims terminal line-endings (LF, CRLF, or CR), which may be added when
|
||||
// inputting interactively or piping input. The value is otherwise treated as
|
||||
// opaque, preserving any other whitespace, including newlines, per [NIST SP 800-63B §5.1.1.2].
|
||||
// Note that trimming whitespace may still happen elsewhere (see [NIST SP 800-63B (revision 4) §3.1.1.2]);
|
||||
//
|
||||
// > Verifiers **MAY** make limited allowances for mistyping (e.g., removing
|
||||
// > leading and trailing whitespace characters before verification, allowing
|
||||
// > the verification of passwords with differing cases for the leading character)
|
||||
//
|
||||
// [NIST SP 800-63B §5.1.1.2]: https://pages.nist.gov/800-63-3/sp800-63b.html#memsecretver
|
||||
// [NIST SP 800-63B (revision 4) §3.1.1.2]: https://pages.nist.gov/800-63-4/sp800-63b.html#passwordver
|
||||
func readSecretFromStdin(r io.Reader) (string, error) {
|
||||
b, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(b) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
for _, eol := range [][]byte{[]byte("\r\n"), []byte("\n"), []byte("\r")} {
|
||||
var ok bool
|
||||
b, ok = bytes.CutSuffix(b, eol)
|
||||
if ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return string(b), nil
|
||||
}
|
||||
|
||||
func verifyLoginOptions(dockerCLI command.Streams, opts *loginOptions) error {
|
||||
if opts.password != "" {
|
||||
_, _ = fmt.Fprintln(dockerCLI.Err(), "WARNING! Using --password via the CLI is insecure. Use --password-stdin.")
|
||||
@@ -97,14 +130,14 @@ func verifyLoginOptions(dockerCLI command.Streams, opts *loginOptions) error {
|
||||
if opts.user == "" {
|
||||
return errors.New("username is empty")
|
||||
}
|
||||
|
||||
contents, err := io.ReadAll(dockerCLI.In())
|
||||
p, err := readSecretFromStdin(dockerCLI.In())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
opts.password = strings.TrimSuffix(string(contents), "\n")
|
||||
opts.password = strings.TrimSuffix(opts.password, "\r")
|
||||
if strings.TrimSpace(p) == "" {
|
||||
return errors.New("password is empty")
|
||||
}
|
||||
opts.password = p
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"testing/iotest"
|
||||
"time"
|
||||
|
||||
"github.com/creack/pty"
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
configtypes "github.com/docker/cli/cli/config/types"
|
||||
"github.com/docker/cli/cli/streams"
|
||||
"github.com/docker/cli/internal/prompt"
|
||||
@@ -19,7 +22,6 @@ import (
|
||||
"github.com/moby/moby/client"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
"gotest.tools/v3/fs"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -71,8 +73,9 @@ func TestLoginWithCredStoreCreds(t *testing.T) {
|
||||
},
|
||||
}
|
||||
ctx := context.Background()
|
||||
tmpDir := t.TempDir()
|
||||
cli := test.NewFakeCli(&fakeClient{})
|
||||
cli.ConfigFile().Filename = filepath.Join(t.TempDir(), "config.json")
|
||||
cli.SetConfigFile(configfile.New(filepath.Join(tmpDir, "config.json")))
|
||||
for _, tc := range testCases {
|
||||
_, err := loginWithStoredCredentials(ctx, cli, tc.inputAuthConfig)
|
||||
if tc.expectedErrMsg != "" {
|
||||
@@ -91,6 +94,7 @@ func TestRunLogin(t *testing.T) {
|
||||
testCases := []struct {
|
||||
doc string
|
||||
priorCredentials map[string]configtypes.AuthConfig
|
||||
stdIn string
|
||||
input loginOptions
|
||||
expectedCredentials map[string]configtypes.AuthConfig
|
||||
expectedErr string
|
||||
@@ -286,20 +290,125 @@ func TestRunLogin(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
doc: "password stdin empty",
|
||||
priorCredentials: map[string]configtypes.AuthConfig{},
|
||||
input: loginOptions{
|
||||
serverAddress: "reg1",
|
||||
user: "my-username",
|
||||
passwordStdin: true,
|
||||
},
|
||||
expectedErr: `password is empty`,
|
||||
expectedCredentials: map[string]configtypes.AuthConfig{
|
||||
"reg1": {
|
||||
Username: "my-username",
|
||||
ServerAddress: "reg1",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
doc: "password stdin read error",
|
||||
priorCredentials: map[string]configtypes.AuthConfig{},
|
||||
input: loginOptions{
|
||||
serverAddress: "reg1",
|
||||
user: "my-username",
|
||||
passwordStdin: true,
|
||||
},
|
||||
expectedErr: `TEST_READ_ERR`,
|
||||
expectedCredentials: map[string]configtypes.AuthConfig{
|
||||
"reg1": {
|
||||
Username: "my-username",
|
||||
ServerAddress: "reg1",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
doc: "password stdin with line-endings",
|
||||
priorCredentials: map[string]configtypes.AuthConfig{},
|
||||
stdIn: "my password\r\n",
|
||||
input: loginOptions{
|
||||
serverAddress: "reg1",
|
||||
user: "my-username",
|
||||
passwordStdin: true,
|
||||
},
|
||||
expectedCredentials: map[string]configtypes.AuthConfig{
|
||||
"reg1": {
|
||||
Username: "my-username",
|
||||
Password: "my password",
|
||||
ServerAddress: "reg1",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
doc: "password with leading and trailing spaces",
|
||||
priorCredentials: map[string]configtypes.AuthConfig{},
|
||||
input: loginOptions{
|
||||
serverAddress: "reg1",
|
||||
user: "my-username",
|
||||
password: " my password with spaces ",
|
||||
},
|
||||
expectedCredentials: map[string]configtypes.AuthConfig{
|
||||
"reg1": {
|
||||
Username: "my-username",
|
||||
Password: " my password with spaces ",
|
||||
ServerAddress: "reg1",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
doc: "password stdin with line-endings",
|
||||
priorCredentials: map[string]configtypes.AuthConfig{},
|
||||
stdIn: " my password with spaces \r\n",
|
||||
input: loginOptions{
|
||||
serverAddress: "reg1",
|
||||
user: "my-username",
|
||||
passwordStdin: true,
|
||||
},
|
||||
expectedCredentials: map[string]configtypes.AuthConfig{
|
||||
"reg1": {
|
||||
Username: "my-username",
|
||||
Password: " my password with spaces ",
|
||||
ServerAddress: "reg1",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
doc: "password stdin with multiple line-endings",
|
||||
priorCredentials: map[string]configtypes.AuthConfig{},
|
||||
stdIn: " my password\nwith spaces \r\n\r\n",
|
||||
input: loginOptions{
|
||||
serverAddress: "reg1",
|
||||
user: "my-username",
|
||||
passwordStdin: true,
|
||||
},
|
||||
expectedCredentials: map[string]configtypes.AuthConfig{
|
||||
"reg1": {
|
||||
Username: "my-username",
|
||||
Password: " my password\nwith spaces \r\n",
|
||||
ServerAddress: "reg1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.doc, func(t *testing.T) {
|
||||
tmpFile := fs.NewFile(t, "test-run-login")
|
||||
defer tmpFile.Remove()
|
||||
tmpDir := t.TempDir()
|
||||
cfg := configfile.New(filepath.Join(tmpDir, "config.json"))
|
||||
cli := test.NewFakeCli(&fakeClient{})
|
||||
configfile := cli.ConfigFile()
|
||||
configfile.Filename = tmpFile.Path()
|
||||
cli.SetConfigFile(cfg)
|
||||
if tc.input.passwordStdin {
|
||||
if tc.expectedErr == "TEST_READ_ERR" {
|
||||
cli.SetIn(streams.NewIn(io.NopCloser(iotest.ErrReader(errors.New(tc.expectedErr)))))
|
||||
} else {
|
||||
cli.SetIn(streams.NewIn(io.NopCloser(bytes.NewBufferString(tc.stdIn))))
|
||||
}
|
||||
}
|
||||
|
||||
for _, priorCred := range tc.priorCredentials {
|
||||
assert.NilError(t, configfile.GetCredentialsStore(priorCred.ServerAddress).Store(priorCred))
|
||||
assert.NilError(t, cfg.GetCredentialsStore(priorCred.ServerAddress).Store(priorCred))
|
||||
}
|
||||
storedCreds, err := configfile.GetAllCredentials()
|
||||
storedCreds, err := cfg.GetAllCredentials()
|
||||
assert.NilError(t, err)
|
||||
assert.DeepEqual(t, storedCreds, tc.priorCredentials)
|
||||
|
||||
@@ -310,7 +419,7 @@ func TestRunLogin(t *testing.T) {
|
||||
}
|
||||
assert.NilError(t, loginErr)
|
||||
|
||||
outputCreds, err := configfile.GetAllCredentials()
|
||||
outputCreds, err := cfg.GetAllCredentials()
|
||||
assert.Check(t, err)
|
||||
assert.DeepEqual(t, outputCreds, tc.expectedCredentials)
|
||||
})
|
||||
@@ -356,11 +465,10 @@ func TestLoginNonInteractive(t *testing.T) {
|
||||
for _, registryAddr := range registries {
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.doc, func(t *testing.T) {
|
||||
tmpFile := fs.NewFile(t, "test-run-login")
|
||||
defer tmpFile.Remove()
|
||||
tmpDir := t.TempDir()
|
||||
cfg := configfile.New(filepath.Join(tmpDir, "config.json"))
|
||||
cli := test.NewFakeCli(&fakeClient{})
|
||||
cfg := cli.ConfigFile()
|
||||
cfg.Filename = tmpFile.Path()
|
||||
cli.SetConfigFile(cfg)
|
||||
options := loginOptions{
|
||||
serverAddress: registryAddr,
|
||||
}
|
||||
@@ -419,11 +527,10 @@ func TestLoginNonInteractive(t *testing.T) {
|
||||
for _, registryAddr := range registries {
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.doc, func(t *testing.T) {
|
||||
tmpFile := fs.NewFile(t, "test-run-login")
|
||||
defer tmpFile.Remove()
|
||||
tmpDir := t.TempDir()
|
||||
cfg := configfile.New(filepath.Join(tmpDir, "config.json"))
|
||||
cli := test.NewFakeCli(&fakeClient{})
|
||||
cfg := cli.ConfigFile()
|
||||
cfg.Filename = tmpFile.Path()
|
||||
cli.SetConfigFile(cfg)
|
||||
serverAddress := registryAddr
|
||||
if serverAddress == "" {
|
||||
serverAddress = "https://index.docker.io/v1/"
|
||||
@@ -465,17 +572,15 @@ func TestLoginTermination(t *testing.T) {
|
||||
_ = p.Close()
|
||||
})
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
cfg := configfile.New(filepath.Join(tmpDir, "config.json"))
|
||||
cli := test.NewFakeCli(&fakeClient{}, func(fc *test.FakeCli) {
|
||||
fc.SetOut(streams.NewOut(tty))
|
||||
fc.SetIn(streams.NewIn(tty))
|
||||
})
|
||||
tmpFile := fs.NewFile(t, "test-login-termination")
|
||||
defer tmpFile.Remove()
|
||||
cli.SetConfigFile(cfg)
|
||||
|
||||
configFile := cli.ConfigFile()
|
||||
configFile.Filename = tmpFile.Path()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(t.Context())
|
||||
t.Cleanup(cancel)
|
||||
|
||||
runErr := make(chan error)
|
||||
|
||||
@@ -108,7 +108,7 @@ func TestServiceListServiceStatus(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
matrix := make([]testCase, 0)
|
||||
matrix := make([]testCase, 0, len(tests))
|
||||
for _, quiet := range []bool{false, true} {
|
||||
for _, tc := range tests {
|
||||
if quiet {
|
||||
@@ -191,10 +191,7 @@ func generateServices(t *testing.T, opts clusterOpts) client.ServiceListResult {
|
||||
t.Helper()
|
||||
|
||||
// Can't have more global tasks than nodes
|
||||
globalTasks := opts.runningTasks
|
||||
if globalTasks > opts.activeNodes {
|
||||
globalTasks = opts.activeNodes
|
||||
}
|
||||
globalTasks := min(opts.runningTasks, opts.activeNodes)
|
||||
return client.ServiceListResult{
|
||||
Items: []swarm.Service{
|
||||
*builders.Service(
|
||||
@@ -279,7 +276,7 @@ func generateNodes(t *testing.T, activeNodes uint64) client.NodeListResult {
|
||||
t.Helper()
|
||||
nodes := client.NodeListResult{}
|
||||
var i uint64
|
||||
for i = 0; i < activeNodes; i++ {
|
||||
for i = range activeNodes {
|
||||
nodes.Items = append(nodes.Items, swarm.Node{
|
||||
ID: fmt.Sprintf("node-ready-%d", i),
|
||||
Status: swarm.NodeStatus{State: swarm.NodeStateReady},
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.24
|
||||
|
||||
package progress
|
||||
|
||||
import (
|
||||
@@ -662,10 +665,7 @@ func (u *replicatedJobProgressUpdater) writeOverallProgress(active, completed in
|
||||
})
|
||||
|
||||
// actualDesired is the lesser of MaxConcurrent, or the remaining tasks
|
||||
actualDesired := u.total - completed
|
||||
if actualDesired > u.concurrent {
|
||||
actualDesired = u.concurrent
|
||||
}
|
||||
actualDesired := min(u.total-completed, u.concurrent)
|
||||
|
||||
_ = u.progressOut.WriteProgress(progress.Progress{
|
||||
ID: "active tasks",
|
||||
|
||||
@@ -80,7 +80,7 @@ func TestReplicatedProgressUpdaterOneReplica(t *testing.T) {
|
||||
service: service,
|
||||
}
|
||||
|
||||
tasks := []swarm.Task{}
|
||||
var tasks []swarm.Task //nolint:prealloc // no need to over-complicate things.
|
||||
|
||||
ut.testUpdater(tasks, false,
|
||||
[]progress.Progress{
|
||||
@@ -193,7 +193,7 @@ func TestReplicatedProgressUpdaterManyReplicas(t *testing.T) {
|
||||
service: service,
|
||||
}
|
||||
|
||||
tasks := []swarm.Task{}
|
||||
var tasks []swarm.Task
|
||||
|
||||
// No per-task progress bars because there are too many replicas
|
||||
ut.testUpdater(tasks, false,
|
||||
@@ -248,7 +248,7 @@ func TestGlobalProgressUpdaterOneNode(t *testing.T) {
|
||||
service: service,
|
||||
}
|
||||
|
||||
tasks := []swarm.Task{}
|
||||
var tasks []swarm.Task //nolint:prealloc // no need to over-complicate things.
|
||||
|
||||
ut.testUpdater(tasks, false,
|
||||
[]progress.Progress{
|
||||
@@ -362,7 +362,7 @@ func TestGlobalProgressUpdaterManyNodes(t *testing.T) {
|
||||
ut.activeNodes[strconv.Itoa(i)] = struct{}{}
|
||||
}
|
||||
|
||||
tasks := []swarm.Task{}
|
||||
var tasks []swarm.Task
|
||||
|
||||
ut.testUpdater(tasks, false,
|
||||
[]progress.Progress{
|
||||
@@ -459,9 +459,8 @@ func TestReplicatedJobProgressUpdaterSmall(t *testing.T) {
|
||||
})
|
||||
|
||||
// wipe the old tasks out of the list
|
||||
tasks = []swarm.Task{}
|
||||
tasks = append(tasks,
|
||||
swarm.Task{
|
||||
tasks = []swarm.Task{ //nolint:prealloc // no need to over-complicate things.
|
||||
{
|
||||
ID: "task1",
|
||||
Slot: 0,
|
||||
NodeID: "",
|
||||
@@ -469,7 +468,7 @@ func TestReplicatedJobProgressUpdaterSmall(t *testing.T) {
|
||||
Status: swarm.TaskStatus{State: swarm.TaskStateNew},
|
||||
JobIteration: &swarm.Version{Index: service.JobStatus.JobIteration.Index},
|
||||
},
|
||||
swarm.Task{
|
||||
{
|
||||
ID: "task2",
|
||||
Slot: 1,
|
||||
NodeID: "",
|
||||
@@ -477,7 +476,7 @@ func TestReplicatedJobProgressUpdaterSmall(t *testing.T) {
|
||||
Status: swarm.TaskStatus{State: swarm.TaskStateNew},
|
||||
JobIteration: &swarm.Version{Index: service.JobStatus.JobIteration.Index},
|
||||
},
|
||||
)
|
||||
}
|
||||
ut.testUpdater(tasks, false, []progress.Progress{
|
||||
{ID: "1/5", Action: "new ", Current: 1, Total: 10, HideCounts: true},
|
||||
{ID: "2/5", Action: "new ", Current: 1, Total: 10, HideCounts: true},
|
||||
@@ -644,7 +643,7 @@ func TestReplicatedJobProgressUpdaterLarge(t *testing.T) {
|
||||
service: service,
|
||||
}
|
||||
|
||||
tasks := []swarm.Task{}
|
||||
var tasks []swarm.Task
|
||||
|
||||
// see the comments in TestReplicatedJobProgressUpdaterSmall for why
|
||||
// we write this out twice.
|
||||
@@ -741,7 +740,7 @@ func TestGlobalJobProgressUpdaterSmall(t *testing.T) {
|
||||
service: service,
|
||||
}
|
||||
|
||||
tasks := []swarm.Task{
|
||||
tasks := []swarm.Task{ //nolint:prealloc // no need to over-complicate things.
|
||||
{
|
||||
ID: "oldtask1",
|
||||
DesiredState: swarm.TaskStateComplete,
|
||||
@@ -856,7 +855,7 @@ func TestGlobalJobProgressUpdaterLarge(t *testing.T) {
|
||||
}
|
||||
|
||||
activeNodes := map[string]struct{}{}
|
||||
for i := 0; i < 50; i++ {
|
||||
for i := range 50 {
|
||||
activeNodes[fmt.Sprintf("node%v", i)] = struct{}{}
|
||||
}
|
||||
|
||||
@@ -871,7 +870,7 @@ func TestGlobalJobProgressUpdaterLarge(t *testing.T) {
|
||||
service: service,
|
||||
}
|
||||
|
||||
tasks := []swarm.Task{}
|
||||
var tasks []swarm.Task //nolint:prealloc // no need to over-complicate things.
|
||||
for nodeID := range activeNodes {
|
||||
tasks = append(tasks, swarm.Task{
|
||||
ID: "task" + nodeID,
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"net/netip"
|
||||
"slices"
|
||||
"sort"
|
||||
@@ -551,12 +552,7 @@ func updateStringToSlice(flags *pflag.FlagSet, flag string, field *[]string) {
|
||||
}
|
||||
|
||||
func anyChanged(flags *pflag.FlagSet, fields ...string) bool {
|
||||
for _, flag := range fields {
|
||||
if flags.Changed(flag) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return slices.ContainsFunc(fields, flags.Changed)
|
||||
}
|
||||
|
||||
func addGenericResources(flags *pflag.FlagSet, spec *swarm.TaskSpec) error {
|
||||
@@ -685,9 +681,7 @@ func updateContainerLabels(flags *pflag.FlagSet, field *map[string]string) {
|
||||
}
|
||||
|
||||
values := flags.Lookup(flagContainerLabelAdd).Value.(*opts.ListOpts).GetSlice()
|
||||
for key, value := range opts.ConvertKVStringsToMap(values) {
|
||||
(*field)[key] = value
|
||||
}
|
||||
maps.Copy((*field), opts.ConvertKVStringsToMap(values))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -704,9 +698,7 @@ func updateLabels(flags *pflag.FlagSet, field *map[string]string) {
|
||||
}
|
||||
|
||||
values := flags.Lookup(flagLabelAdd).Value.(*opts.ListOpts).GetSlice()
|
||||
for key, value := range opts.ConvertKVStringsToMap(values) {
|
||||
(*field)[key] = value
|
||||
}
|
||||
maps.Copy((*field), opts.ConvertKVStringsToMap(values))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -723,9 +715,7 @@ func updateSysCtls(flags *pflag.FlagSet, field *map[string]string) {
|
||||
}
|
||||
|
||||
values := flags.Lookup(flagSysCtlAdd).Value.(*opts.ListOpts).GetSlice()
|
||||
for key, value := range opts.ConvertKVStringsToMap(values) {
|
||||
(*field)[key] = value
|
||||
}
|
||||
maps.Copy((*field), opts.ConvertKVStringsToMap(values))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1333,7 +1323,7 @@ func updateNetworks(ctx context.Context, apiClient client.NetworkAPIClient, flag
|
||||
}
|
||||
|
||||
existingNetworks := make(map[string]struct{})
|
||||
var newNetworks []swarm.NetworkAttachmentConfig //nolint:prealloc
|
||||
var newNetworks []swarm.NetworkAttachmentConfig
|
||||
for _, nw := range specNetworks {
|
||||
if _, exists := idsToRemove[nw.Target]; exists {
|
||||
continue
|
||||
|
||||
@@ -27,7 +27,6 @@ func loadComposeFile(streams command.Streams, opts deployOptions) (*composetypes
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dicts := getDictsFrom(configDetails.ConfigFiles)
|
||||
config, err := loader.Load(configDetails)
|
||||
if err != nil {
|
||||
var fpe *loader.ForbiddenPropertiesError
|
||||
@@ -39,6 +38,7 @@ func loadComposeFile(streams command.Streams, opts deployOptions) (*composetypes
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dicts := getDictsFrom(configDetails.ConfigFiles)
|
||||
unsupportedProperties := loader.GetUnsupportedProperties(dicts...)
|
||||
if len(unsupportedProperties) > 0 {
|
||||
_, _ = fmt.Fprintf(streams.Err(), "Ignoring unsupported options: %s\n\n",
|
||||
@@ -65,8 +65,7 @@ func loadComposeFile(streams command.Streams, opts deployOptions) (*composetypes
|
||||
}
|
||||
|
||||
func getDictsFrom(configFiles []composetypes.ConfigFile) []map[string]any {
|
||||
dicts := []map[string]any{}
|
||||
|
||||
dicts := make([]map[string]any, 0, len(configFiles))
|
||||
for _, configFile := range configFiles {
|
||||
dicts = append(dicts, configFile.Config)
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ func (*ExternalCAOption) Type() string {
|
||||
|
||||
// String returns a string repr of this option.
|
||||
func (m *ExternalCAOption) String() string {
|
||||
externalCAs := []string{}
|
||||
externalCAs := make([]string, 0, len(m.values))
|
||||
for _, externalCA := range m.values {
|
||||
repr := fmt.Sprintf("%s: %s", externalCA.Protocol, externalCA.URL)
|
||||
externalCAs = append(externalCAs, repr)
|
||||
|
||||
@@ -133,12 +133,12 @@ func prettyPrintEvent(out io.Writer, event events.Message) error {
|
||||
_, _ = fmt.Fprintf(out, "%s %s %s", event.Type, event.Action, event.Actor.ID)
|
||||
|
||||
if len(event.Actor.Attributes) > 0 {
|
||||
var attrs []string
|
||||
var keys []string
|
||||
keys := make([]string, 0, len(event.Actor.Attributes))
|
||||
for k := range event.Actor.Attributes {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
attrs := make([]string, 0, len(keys))
|
||||
for _, k := range keys {
|
||||
v := event.Actor.Attributes[k]
|
||||
attrs = append(attrs, k+"="+v)
|
||||
|
||||
@@ -155,11 +155,11 @@ func (e statusError) Error() string {
|
||||
// Note: The root command's name is excluded. If cmd is the root cmd, return ""
|
||||
func getCommandName(cmd *cobra.Command) string {
|
||||
fullCmdName := getFullCommandName(cmd)
|
||||
i := strings.Index(fullCmdName, " ")
|
||||
if i == -1 {
|
||||
_, after, ok := strings.Cut(fullCmdName, " ")
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return fullCmdName[i+1:]
|
||||
return after
|
||||
}
|
||||
|
||||
// getFullCommandName gets the full cobra command name in the format
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.24
|
||||
|
||||
package volume
|
||||
|
||||
import (
|
||||
@@ -166,7 +169,7 @@ func runCreate(ctx context.Context, dockerCli command.Cli, options createOptions
|
||||
// each topology takes the form segment=value,segment=value
|
||||
// comma-separated list of equal separated maps
|
||||
segments := map[string]string{}
|
||||
for _, segment := range strings.Split(top, ",") {
|
||||
for segment := range strings.SplitSeq(top, ",") {
|
||||
// TODO(dperny): validate topology syntax
|
||||
k, v, _ := strings.Cut(segment, "=")
|
||||
segments[k] = v
|
||||
@@ -181,7 +184,7 @@ func runCreate(ctx context.Context, dockerCli command.Cli, options createOptions
|
||||
// each topology takes the form segment=value,segment=value
|
||||
// comma-separated list of equal separated maps
|
||||
segments := map[string]string{}
|
||||
for _, segment := range strings.Split(top, ",") {
|
||||
for segment := range strings.SplitSeq(top, ",") {
|
||||
// TODO(dperny): validate topology syntax
|
||||
k, v, _ := strings.Cut(segment, "=")
|
||||
segments[k] = v
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.24
|
||||
|
||||
package convert
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"os"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -174,7 +179,11 @@ func Service(
|
||||
}
|
||||
|
||||
func getPlacementPreference(preferences []composetypes.PlacementPreferences) []swarm.PlacementPreference {
|
||||
result := []swarm.PlacementPreference{}
|
||||
if len(preferences) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
result := make([]swarm.PlacementPreference, 0, len(preferences))
|
||||
for _, preference := range preferences {
|
||||
spreadDescriptor := preference.Spread
|
||||
result = append(result, swarm.PlacementPreference{
|
||||
@@ -198,7 +207,7 @@ func convertServiceNetworks(
|
||||
}
|
||||
}
|
||||
|
||||
nets := []swarm.NetworkAttachmentConfig{}
|
||||
nets := make([]swarm.NetworkAttachmentConfig, 0, len(networks))
|
||||
for networkName, nw := range networks {
|
||||
networkConfig, ok := networkConfigs[networkName]
|
||||
if !ok && networkName != defaultNetwork {
|
||||
@@ -241,8 +250,6 @@ func convertServiceSecrets(
|
||||
secrets []composetypes.ServiceSecretConfig,
|
||||
secretSpecs map[string]composetypes.SecretConfig,
|
||||
) ([]*swarm.SecretReference, error) {
|
||||
refs := []*swarm.SecretReference{}
|
||||
|
||||
lookup := func(key string) (composetypes.FileObjectConfig, error) {
|
||||
secretSpec, exists := secretSpecs[key]
|
||||
if !exists {
|
||||
@@ -250,6 +257,8 @@ func convertServiceSecrets(
|
||||
}
|
||||
return composetypes.FileObjectConfig(secretSpec), nil
|
||||
}
|
||||
|
||||
refs := make([]*swarm.SecretReference, 0, len(secrets))
|
||||
for _, secret := range secrets {
|
||||
obj, err := convertFileObject(namespace, composetypes.FileReferenceConfig(secret), lookup)
|
||||
if err != nil {
|
||||
@@ -564,20 +573,17 @@ func convertResources(source composetypes.Resources) (*swarm.ResourceRequirement
|
||||
}
|
||||
|
||||
func convertEndpointSpec(endpointMode string, source []composetypes.ServicePortConfig) *swarm.EndpointSpec {
|
||||
portConfigs := []swarm.PortConfig{}
|
||||
portConfigs := make([]swarm.PortConfig, 0, len(source))
|
||||
for _, port := range source {
|
||||
portConfig := swarm.PortConfig{
|
||||
portConfigs = append(portConfigs, swarm.PortConfig{
|
||||
Protocol: network.IPProtocol(port.Protocol),
|
||||
TargetPort: port.Target,
|
||||
PublishedPort: port.Published,
|
||||
PublishMode: swarm.PortConfigPublishMode(port.Mode),
|
||||
}
|
||||
portConfigs = append(portConfigs, portConfig)
|
||||
})
|
||||
}
|
||||
|
||||
sort.Slice(portConfigs, func(i, j int) bool {
|
||||
return portConfigs[i].PublishedPort < portConfigs[j].PublishedPort
|
||||
})
|
||||
slices.SortFunc(portConfigs, swarm.PortConfig.Compare)
|
||||
|
||||
return &swarm.EndpointSpec{
|
||||
Mode: swarm.ResolutionMode(strings.ToLower(endpointMode)),
|
||||
@@ -698,28 +704,22 @@ func convertCredentialSpec(namespace Namespace, spec composetypes.CredentialSpec
|
||||
}
|
||||
|
||||
func convertUlimits(origUlimits map[string]*composetypes.UlimitsConfig) []*container.Ulimit {
|
||||
newUlimits := make(map[string]*container.Ulimit)
|
||||
ulimits := make([]*container.Ulimit, 0, len(origUlimits))
|
||||
for name, u := range origUlimits {
|
||||
soft, hard := int64(u.Soft), int64(u.Hard)
|
||||
if u.Single != 0 {
|
||||
newUlimits[name] = &container.Ulimit{
|
||||
Name: name,
|
||||
Soft: int64(u.Single),
|
||||
Hard: int64(u.Single),
|
||||
}
|
||||
} else {
|
||||
newUlimits[name] = &container.Ulimit{
|
||||
Name: name,
|
||||
Soft: int64(u.Soft),
|
||||
Hard: int64(u.Hard),
|
||||
}
|
||||
soft, hard = int64(u.Single), int64(u.Single)
|
||||
}
|
||||
|
||||
ulimits = append(ulimits, &container.Ulimit{
|
||||
Name: name,
|
||||
Soft: soft,
|
||||
Hard: hard,
|
||||
})
|
||||
}
|
||||
ulimits := make([]*container.Ulimit, 0, len(newUlimits))
|
||||
for _, ulimit := range newUlimits {
|
||||
ulimits = append(ulimits, ulimit)
|
||||
}
|
||||
sort.SliceStable(ulimits, func(i, j int) bool {
|
||||
return ulimits[i].Name < ulimits[j].Name
|
||||
|
||||
slices.SortFunc(ulimits, func(a, b *container.Ulimit) int {
|
||||
return cmp.Compare(a.Name, b.Name)
|
||||
})
|
||||
return ulimits
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ package loader
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
@@ -249,9 +250,7 @@ func GetDeprecatedProperties(configDicts ...map[string]any) map[string]string {
|
||||
|
||||
for _, configDict := range configDicts {
|
||||
deprecatedProperties := getProperties(getServices(configDict), types.DeprecatedProperties)
|
||||
for key, value := range deprecatedProperties {
|
||||
deprecated[key] = value
|
||||
}
|
||||
maps.Copy(deprecated, deprecatedProperties)
|
||||
}
|
||||
|
||||
return deprecated
|
||||
|
||||
+52
-68
@@ -4,8 +4,11 @@
|
||||
package loader
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"slices"
|
||||
"sort"
|
||||
|
||||
"dario.cat/mergo"
|
||||
@@ -26,36 +29,36 @@ func (s *specials) Transformer(t reflect.Type) func(dst, src reflect.Value) erro
|
||||
func merge(configs []*types.Config) (*types.Config, error) {
|
||||
base := configs[0]
|
||||
for _, override := range configs[1:] {
|
||||
var err error
|
||||
base.Services, err = mergeServices(base.Services, override.Services)
|
||||
if err != nil {
|
||||
return base, fmt.Errorf("cannot merge services from %s: %w", override.Filename, err)
|
||||
var errs []error
|
||||
if services, err := mergeServices(base.Services, override.Services); err != nil {
|
||||
errs = append(errs, fmt.Errorf("cannot merge services: %w", err))
|
||||
} else {
|
||||
base.Services = services
|
||||
}
|
||||
base.Volumes, err = mergeVolumes(base.Volumes, override.Volumes)
|
||||
if err != nil {
|
||||
return base, fmt.Errorf("cannot merge volumes from %s: %w", override.Filename, err)
|
||||
if err := mergo.Map(&base.Volumes, &override.Volumes, mergo.WithOverride); err != nil {
|
||||
errs = append(errs, fmt.Errorf("cannot merge volumes: %w", err))
|
||||
}
|
||||
base.Networks, err = mergeNetworks(base.Networks, override.Networks)
|
||||
if err != nil {
|
||||
return base, fmt.Errorf("cannot merge networks from %s: %w", override.Filename, err)
|
||||
if err := mergo.Map(&base.Networks, &override.Networks, mergo.WithOverride); err != nil {
|
||||
errs = append(errs, fmt.Errorf("cannot merge networks: %w", err))
|
||||
}
|
||||
base.Secrets, err = mergeSecrets(base.Secrets, override.Secrets)
|
||||
if err != nil {
|
||||
return base, fmt.Errorf("cannot merge secrets from %s: %w", override.Filename, err)
|
||||
if err := mergo.Map(&base.Secrets, &override.Secrets, mergo.WithOverride); err != nil {
|
||||
errs = append(errs, fmt.Errorf("cannot merge secrets: %w", err))
|
||||
}
|
||||
base.Configs, err = mergeConfigs(base.Configs, override.Configs)
|
||||
if err != nil {
|
||||
return base, fmt.Errorf("cannot merge configs from %s: %w", override.Filename, err)
|
||||
if err := mergo.Map(&base.Configs, &override.Configs, mergo.WithOverride); err != nil {
|
||||
errs = append(errs, fmt.Errorf("cannot merge configs: %w", err))
|
||||
}
|
||||
if err := errors.Join(errs...); err != nil {
|
||||
return nil, errors.Join(fmt.Errorf("failed to merge file %s", override.Filename), err)
|
||||
}
|
||||
}
|
||||
return base, nil
|
||||
}
|
||||
|
||||
func mergeServices(base, override []types.ServiceConfig) ([]types.ServiceConfig, error) {
|
||||
baseServices := mapByName(base)
|
||||
overrideServices := mapByName(override)
|
||||
specials := &specials{
|
||||
m: map[reflect.Type]func(dst, src reflect.Value) error{
|
||||
mergeOpts := []func(*mergo.Config){
|
||||
mergo.WithAppendSlice,
|
||||
mergo.WithOverride,
|
||||
mergo.WithTransformers(&specials{m: map[reflect.Type]func(dst, src reflect.Value) error{
|
||||
reflect.PointerTo(reflect.TypeFor[types.LoggingConfig]()): safelyMerge(mergeLoggingConfig),
|
||||
reflect.TypeFor[[]types.ServicePortConfig](): mergeSlice(toServicePortConfigsMap, toServicePortConfigsSlice),
|
||||
reflect.TypeFor[[]types.ServiceSecretConfig](): mergeSlice(toServiceSecretConfigsMap, toServiceSecretConfigsSlice),
|
||||
@@ -65,23 +68,34 @@ func mergeServices(base, override []types.ServiceConfig) ([]types.ServiceConfig,
|
||||
reflect.TypeFor[types.ShellCommand](): mergeShellCommand,
|
||||
reflect.PointerTo(reflect.TypeFor[types.ServiceNetworkConfig]()): mergeServiceNetworkConfig,
|
||||
reflect.PointerTo(reflect.TypeFor[uint64]()): mergeUint64,
|
||||
},
|
||||
}}),
|
||||
}
|
||||
for name, overrideService := range overrideServices {
|
||||
if baseService, ok := baseServices[name]; ok {
|
||||
if err := mergo.Merge(&baseService, &overrideService, mergo.WithAppendSlice, mergo.WithOverride, mergo.WithTransformers(specials)); err != nil {
|
||||
return base, fmt.Errorf("cannot merge service %s: %w", name, err)
|
||||
|
||||
baseServices := make(map[string]types.ServiceConfig, len(base))
|
||||
for _, s := range base {
|
||||
baseServices[s.Name] = s
|
||||
}
|
||||
|
||||
for _, overrideService := range override {
|
||||
if baseService, ok := baseServices[overrideService.Name]; ok {
|
||||
if err := mergo.Merge(&baseService, &overrideService, mergeOpts...); err != nil {
|
||||
return nil, fmt.Errorf("cannot merge service %s: %w", overrideService.Name, err)
|
||||
}
|
||||
baseServices[name] = baseService
|
||||
baseServices[overrideService.Name] = baseService
|
||||
continue
|
||||
}
|
||||
baseServices[name] = overrideService
|
||||
baseServices[overrideService.Name] = overrideService
|
||||
}
|
||||
|
||||
services := make([]types.ServiceConfig, 0, len(baseServices))
|
||||
for _, baseService := range baseServices {
|
||||
services = append(services, baseService)
|
||||
}
|
||||
sort.Slice(services, func(i, j int) bool { return services[i].Name < services[j].Name })
|
||||
|
||||
slices.SortFunc(services, func(a, b types.ServiceConfig) int {
|
||||
return cmp.Compare(a.Name, b.Name)
|
||||
})
|
||||
|
||||
return services, nil
|
||||
}
|
||||
|
||||
@@ -134,7 +148,7 @@ func toServiceVolumeConfigsMap(s any) (map[any]any, error) {
|
||||
}
|
||||
|
||||
func toServiceSecretConfigsSlice(dst reflect.Value, m map[any]any) error {
|
||||
s := []types.ServiceSecretConfig{}
|
||||
s := make([]types.ServiceSecretConfig, 0, len(m))
|
||||
for _, v := range m {
|
||||
s = append(s, v.(types.ServiceSecretConfig))
|
||||
}
|
||||
@@ -144,7 +158,7 @@ func toServiceSecretConfigsSlice(dst reflect.Value, m map[any]any) error {
|
||||
}
|
||||
|
||||
func toSServiceConfigObjConfigsSlice(dst reflect.Value, m map[any]any) error {
|
||||
s := []types.ServiceConfigObjConfig{}
|
||||
s := make([]types.ServiceConfigObjConfig, 0, len(m))
|
||||
for _, v := range m {
|
||||
s = append(s, v.(types.ServiceConfigObjConfig))
|
||||
}
|
||||
@@ -154,7 +168,7 @@ func toSServiceConfigObjConfigsSlice(dst reflect.Value, m map[any]any) error {
|
||||
}
|
||||
|
||||
func toServicePortConfigsSlice(dst reflect.Value, m map[any]any) error {
|
||||
s := []types.ServicePortConfig{}
|
||||
s := make([]types.ServicePortConfig, 0, len(m))
|
||||
for _, v := range m {
|
||||
s = append(s, v.(types.ServicePortConfig))
|
||||
}
|
||||
@@ -164,7 +178,7 @@ func toServicePortConfigsSlice(dst reflect.Value, m map[any]any) error {
|
||||
}
|
||||
|
||||
func toServiceVolumeConfigsSlice(dst reflect.Value, m map[any]any) error {
|
||||
s := []types.ServiceVolumeConfig{}
|
||||
s := make([]types.ServiceVolumeConfig, 0, len(m))
|
||||
for _, v := range m {
|
||||
s = append(s, v.(types.ServiceVolumeConfig))
|
||||
}
|
||||
@@ -217,11 +231,13 @@ func sliceToMap(tomap tomapFn, v reflect.Value) (map[any]any, error) {
|
||||
}
|
||||
|
||||
func mergeLoggingConfig(dst, src reflect.Value) error {
|
||||
dstDriver := dst.Elem().FieldByName("Driver").String()
|
||||
srcDriver := src.Elem().FieldByName("Driver").String()
|
||||
|
||||
// Same driver, merging options
|
||||
if getLoggingDriver(dst.Elem()) == getLoggingDriver(src.Elem()) ||
|
||||
getLoggingDriver(dst.Elem()) == "" || getLoggingDriver(src.Elem()) == "" {
|
||||
if getLoggingDriver(dst.Elem()) == "" {
|
||||
dst.Elem().FieldByName("Driver").SetString(getLoggingDriver(src.Elem()))
|
||||
if dstDriver == srcDriver || dstDriver == "" || srcDriver == "" {
|
||||
if dstDriver == "" {
|
||||
dst.Elem().FieldByName("Driver").SetString(srcDriver)
|
||||
}
|
||||
dstOptions := dst.Elem().FieldByName("Options").Interface().(map[string]string)
|
||||
srcOptions := src.Elem().FieldByName("Options").Interface().(map[string]string)
|
||||
@@ -269,35 +285,3 @@ func mergeUint64(dst, src reflect.Value) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getLoggingDriver(v reflect.Value) string {
|
||||
return v.FieldByName("Driver").String()
|
||||
}
|
||||
|
||||
func mapByName(services []types.ServiceConfig) map[string]types.ServiceConfig {
|
||||
m := map[string]types.ServiceConfig{}
|
||||
for _, service := range services {
|
||||
m[service.Name] = service
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func mergeVolumes(base, override map[string]types.VolumeConfig) (map[string]types.VolumeConfig, error) {
|
||||
err := mergo.Map(&base, &override, mergo.WithOverride)
|
||||
return base, err
|
||||
}
|
||||
|
||||
func mergeNetworks(base, override map[string]types.NetworkConfig) (map[string]types.NetworkConfig, error) {
|
||||
err := mergo.Map(&base, &override, mergo.WithOverride)
|
||||
return base, err
|
||||
}
|
||||
|
||||
func mergeSecrets(base, override map[string]types.SecretConfig) (map[string]types.SecretConfig, error) {
|
||||
err := mergo.Map(&base, &override, mergo.WithOverride)
|
||||
return base, err
|
||||
}
|
||||
|
||||
func mergeConfigs(base, override map[string]types.ConfigObjConfig) (map[string]types.ConfigObjConfig, error) {
|
||||
err := mergo.Map(&base, &override, mergo.WithOverride)
|
||||
return base, err
|
||||
}
|
||||
|
||||
@@ -1153,7 +1153,7 @@ func TestLoadMultipleServiceVolumes(t *testing.T) {
|
||||
func TestMergeUlimitsConfig(t *testing.T) {
|
||||
specials := &specials{
|
||||
m: map[reflect.Type]func(dst, src reflect.Value) error{
|
||||
reflect.TypeOf(&types.UlimitsConfig{}): mergeUlimitsConfig,
|
||||
reflect.TypeFor[*types.UlimitsConfig](): mergeUlimitsConfig,
|
||||
},
|
||||
}
|
||||
base := map[string]*types.UlimitsConfig{
|
||||
@@ -1189,7 +1189,7 @@ func TestMergeUlimitsConfig(t *testing.T) {
|
||||
func TestMergeServiceNetworkConfig(t *testing.T) {
|
||||
specials := &specials{
|
||||
m: map[reflect.Type]func(dst, src reflect.Value) error{
|
||||
reflect.TypeOf(&types.ServiceNetworkConfig{}): mergeServiceNetworkConfig,
|
||||
reflect.TypeFor[*types.ServiceNetworkConfig](): mergeServiceNetworkConfig,
|
||||
},
|
||||
}
|
||||
base := map[string]*types.ServiceNetworkConfig{
|
||||
|
||||
@@ -5,6 +5,7 @@ package template
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"maps"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
@@ -140,9 +141,7 @@ func recurseExtract(value any, pattern regexper) map[string]string {
|
||||
case map[string]any:
|
||||
for _, elem := range val {
|
||||
submap := recurseExtract(elem, pattern)
|
||||
for k, v := range submap {
|
||||
m[k] = v
|
||||
}
|
||||
maps.Copy(m, submap)
|
||||
}
|
||||
|
||||
case []any:
|
||||
|
||||
@@ -6,6 +6,7 @@ package types
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"maps"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
@@ -129,9 +130,7 @@ func (c Config) MarshalJSON() ([]byte, error) {
|
||||
if len(c.Configs) > 0 {
|
||||
m["configs"] = c.Configs
|
||||
}
|
||||
for k, v := range c.Extras {
|
||||
m[k] = v
|
||||
}
|
||||
maps.Copy(m, c.Extras)
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
||||
@@ -352,7 +351,7 @@ func (u UnitBytes) MarshalYAML() (any, error) {
|
||||
|
||||
// MarshalJSON makes UnitBytes implement json.Marshaler
|
||||
func (u UnitBytes) MarshalJSON() ([]byte, error) {
|
||||
return []byte(fmt.Sprintf(`"%d"`, u)), nil
|
||||
return fmt.Appendf(nil, `"%d"`, u), nil
|
||||
}
|
||||
|
||||
// RestartPolicy the service restart policy
|
||||
@@ -562,7 +561,7 @@ func (e External) MarshalJSON() ([]byte, error) {
|
||||
if e.Name == "" {
|
||||
return []byte(strconv.FormatBool(e.External)), nil
|
||||
}
|
||||
return []byte(fmt.Sprintf(`{"name": %q}`, e.Name)), nil
|
||||
return fmt.Appendf(nil, `{"name": %q}`, e.Name), nil
|
||||
}
|
||||
|
||||
// CredentialSpecConfig for credential spec on Windows
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.24
|
||||
|
||||
package configfile
|
||||
|
||||
import (
|
||||
@@ -6,6 +9,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"maps"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -374,9 +378,7 @@ func getConfiguredCredentialStore(c *ConfigFile, registryHostname string) string
|
||||
func (configFile *ConfigFile) GetAllCredentials() (map[string]types.AuthConfig, error) {
|
||||
auths := make(map[string]types.AuthConfig)
|
||||
addAll := func(from map[string]types.AuthConfig) {
|
||||
for reg, ac := range from {
|
||||
auths[reg] = ac
|
||||
}
|
||||
maps.Copy(auths, from)
|
||||
}
|
||||
|
||||
defaultStore := configFile.GetCredentialsStore("")
|
||||
|
||||
@@ -73,10 +73,10 @@ func (m *mockCommand) Output() ([]byte, error) {
|
||||
return []byte("program failed"), errCommandExited
|
||||
}
|
||||
case "list":
|
||||
return []byte(fmt.Sprintf(`{"%s": "%s", "%s": "%s"}`, validServerAddress, "foo", validServerAddress2, "<token>")), nil
|
||||
return fmt.Appendf(nil, `{"%s": "%s", "%s": "%s"}`, validServerAddress, "foo", validServerAddress2, "<token>"), nil
|
||||
}
|
||||
|
||||
return []byte(fmt.Sprintf("unknown argument %q with %q", m.arg, inS)), errCommandExited
|
||||
return fmt.Appendf(nil, "unknown argument %q with %q", m.arg, inS), errCommandExited
|
||||
}
|
||||
|
||||
// Input sets the input to send to a remote credentials helper.
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.24
|
||||
|
||||
// Package connhelper provides helpers for connecting to a remote daemon host with custom logic.
|
||||
package connhelper
|
||||
|
||||
@@ -6,6 +9,7 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli/connhelper/commandconn"
|
||||
@@ -89,10 +93,8 @@ func addSSHTimeout(sshFlags []string) []string {
|
||||
// disablePseudoTerminalAllocation disables pseudo-terminal allocation to
|
||||
// prevent SSH from executing as a login shell
|
||||
func disablePseudoTerminalAllocation(sshFlags []string) []string {
|
||||
for _, flag := range sshFlags {
|
||||
if flag == "-T" {
|
||||
return sshFlags
|
||||
}
|
||||
if slices.Contains(sshFlags, "-T") {
|
||||
return sshFlags
|
||||
}
|
||||
return append(sshFlags, "-T")
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ func quoteCommand(commandAndArgs ...string) (string, error) {
|
||||
quotedCmd = a
|
||||
continue
|
||||
}
|
||||
quotedCmd += " " + a //nolint:perfsprint // ignore "concat-loop"; no need to use a string-builder for this.
|
||||
quotedCmd += " " + a
|
||||
}
|
||||
// each part is quoted appropriately, so now we'll have a full
|
||||
// shell command to pass off to "ssh"
|
||||
|
||||
@@ -17,7 +17,7 @@ require (
|
||||
github.com/spf13/cobra v1.10.1
|
||||
github.com/spf13/pflag v1.0.10
|
||||
github.com/theupdateframework/notary v0.7.1-0.20210315103452-bf96a202a09a
|
||||
go.opentelemetry.io/otel v1.38.0
|
||||
go.opentelemetry.io/otel v1.40.0
|
||||
gotest.tools/v3 v3.5.2
|
||||
)
|
||||
|
||||
@@ -57,19 +57,19 @@ require (
|
||||
github.com/prometheus/common v0.62.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.40.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.40.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.40.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.40.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
|
||||
golang.org/x/crypto v0.45.0 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/sys v0.40.0 // indirect
|
||||
golang.org/x/term v0.37.0 // indirect
|
||||
golang.org/x/text v0.31.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect
|
||||
|
||||
+16
-16
@@ -213,8 +213,8 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg
|
||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
@@ -252,26 +252,26 @@ github.com/zmap/zcrypto v0.0.0-20210511125630-18f1e0152cfc h1:zkGwegkOW709y0oiAr
|
||||
github.com/zmap/zcrypto v0.0.0-20210511125630-18f1e0152cfc/go.mod h1:FM4U1E3NzlNMRnSUTU3P1UdukWhYGifqEsjk9fn7BCk=
|
||||
github.com/zmap/zlint/v3 v3.1.0 h1:WjVytZo79m/L1+/Mlphl09WBob6YTGljN5IGWZFpAv0=
|
||||
github.com/zmap/zlint/v3 v3.1.0/go.mod h1:L7t8s3sEKkb0A2BxGy1IWrxt1ZATa1R4QfJZaQOD3zU=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg=
|
||||
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
||||
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
|
||||
go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
|
||||
go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 h1:vl9obrcoWVKp/lwl8tRE33853I8Xru9HFbw/skNeLs8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0/go.mod h1:GAXRxmLJcVM3u22IjTg74zWBrRCKq8BnOqUVLodpcpw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4Dc5leUqENgGuQImwLo4WnuXFPetmPpkLi2IrX54=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0/go.mod h1:Kz/oCE7z5wuyhPxsXDuaPteSWqjSBD5YaSdbxZYGbGk=
|
||||
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
|
||||
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
|
||||
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
|
||||
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
||||
go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=
|
||||
go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=
|
||||
go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=
|
||||
go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=
|
||||
go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
|
||||
go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
|
||||
go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=
|
||||
go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
@@ -302,8 +302,8 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
|
||||
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
|
||||
|
||||
@@ -22,8 +22,7 @@ import (
|
||||
var pluginFilename = "docker-buildx"
|
||||
|
||||
func TestBuildWithBuilder(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.TODO())
|
||||
defer cancel()
|
||||
ctx := t.Context()
|
||||
|
||||
testcases := []struct {
|
||||
name string
|
||||
@@ -131,8 +130,7 @@ func (*fakeClient) Ping(context.Context, client.PingOptions) (client.PingResult,
|
||||
}
|
||||
|
||||
func TestBuildkitDisabled(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.TODO())
|
||||
defer cancel()
|
||||
ctx := t.Context()
|
||||
|
||||
t.Setenv("DOCKER_BUILDKIT", "0")
|
||||
|
||||
@@ -172,8 +170,7 @@ func TestBuildkitDisabled(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBuilderBroken(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.TODO())
|
||||
defer cancel()
|
||||
ctx := t.Context()
|
||||
|
||||
dir := fs.NewDir(t, t.Name(),
|
||||
fs.WithFile(pluginFilename, `#!/bin/sh exit 1`, fs.WithMode(0o777)),
|
||||
@@ -212,8 +209,7 @@ func TestBuilderBroken(t *testing.T) {
|
||||
|
||||
func TestBuilderBrokenEnforced(t *testing.T) {
|
||||
t.Setenv("DOCKER_BUILDKIT", "1")
|
||||
ctx, cancel := context.WithCancel(context.TODO())
|
||||
defer cancel()
|
||||
ctx := t.Context()
|
||||
|
||||
dir := fs.NewDir(t, t.Name(),
|
||||
fs.WithFile(pluginFilename, `#!/bin/sh exit 1`, fs.WithMode(0o777)),
|
||||
|
||||
+26
-13
@@ -1,3 +1,6 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.24
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -7,6 +10,7 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"slices"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
@@ -115,6 +119,20 @@ func getExitCode(err error) int {
|
||||
return 1
|
||||
}
|
||||
|
||||
// cmdErrorMessage extracts an error message suitable for passing to plugin
|
||||
// hooks. If the error's message is empty (e.g. a StatusError with only an
|
||||
// exit code), it falls back to a generic message so that hooks can still
|
||||
// detect that the command failed.
|
||||
func cmdErrorMessage(err error) string {
|
||||
if err == nil {
|
||||
return ""
|
||||
}
|
||||
if msg := err.Error(); msg != "" {
|
||||
return msg
|
||||
}
|
||||
return fmt.Sprintf("exited with code %d", getExitCode(err))
|
||||
}
|
||||
|
||||
func newDockerCommand(dockerCli *command.DockerCli) *cli.TopLevelCommand {
|
||||
var (
|
||||
opts *cliflags.ClientOptions
|
||||
@@ -410,7 +428,7 @@ func forceExitAfter3TerminationSignals(ctx context.Context, streams command.Stre
|
||||
signal.Notify(sig, platformsignals.TerminationSignals...)
|
||||
|
||||
// once we have received a total of 3 signals we force exit the cli
|
||||
for i := 0; i < 2; i++ {
|
||||
for range 2 {
|
||||
<-sig
|
||||
}
|
||||
_, _ = fmt.Fprint(streams.Err(), "\ngot 3 SIGTERM/SIGINTs, forcefully exiting\n")
|
||||
@@ -476,10 +494,11 @@ func runDocker(ctx context.Context, dockerCli *command.DockerCli) error {
|
||||
subCommand = ccmd
|
||||
if err != nil || pluginmanager.IsPluginCommand(ccmd) {
|
||||
err := tryPluginRun(ctx, dockerCli, cmd, args[0], envs)
|
||||
if ccmd != nil && dockerCli.Out().IsTerminal() && dockerCli.HooksEnabled() && !errdefs.IsNotFound(err) {
|
||||
errMessage := cmdErrorMessage(err)
|
||||
pluginmanager.RunPluginHooks(ctx, dockerCli, cmd, ccmd, args, errMessage)
|
||||
}
|
||||
if err == nil {
|
||||
if ccmd != nil && dockerCli.Out().IsTerminal() && dockerCli.HooksEnabled() {
|
||||
pluginmanager.RunPluginHooks(ctx, dockerCli, cmd, ccmd, args)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if !errdefs.IsNotFound(err) {
|
||||
@@ -503,11 +522,7 @@ func runDocker(ctx context.Context, dockerCli *command.DockerCli) error {
|
||||
// If the command is being executed in an interactive terminal
|
||||
// and hook are enabled, run the plugin hooks.
|
||||
if subCommand != nil && dockerCli.Out().IsTerminal() && dockerCli.HooksEnabled() {
|
||||
var errMessage string
|
||||
if err != nil {
|
||||
errMessage = err.Error()
|
||||
}
|
||||
pluginmanager.RunCLICommandHooks(ctx, dockerCli, cmd, subCommand, errMessage)
|
||||
pluginmanager.RunCLICommandHooks(ctx, dockerCli, cmd, subCommand, cmdErrorMessage(err))
|
||||
}
|
||||
|
||||
return err
|
||||
@@ -617,10 +632,8 @@ func findCommand(cmd *cobra.Command, cmds []string) bool {
|
||||
if cmd == nil {
|
||||
return false
|
||||
}
|
||||
for _, c := range cmds {
|
||||
if c == cmd.Name() {
|
||||
return true
|
||||
}
|
||||
if slices.Contains(cmds, cmd.Name()) {
|
||||
return true
|
||||
}
|
||||
return findCommand(cmd.Parent(), cmds)
|
||||
}
|
||||
|
||||
@@ -4,12 +4,14 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
dockercli "github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/commands"
|
||||
"github.com/docker/cli/cli/debug"
|
||||
@@ -38,8 +40,7 @@ func TestDisableFlagsInUseLineIsSet(t *testing.T) {
|
||||
|
||||
func TestClientDebugEnabled(t *testing.T) {
|
||||
defer debug.Disable()
|
||||
ctx, cancel := context.WithCancel(context.TODO())
|
||||
defer cancel()
|
||||
ctx := t.Context()
|
||||
|
||||
cli, err := command.NewDockerCli(command.WithBaseContext(ctx))
|
||||
assert.NilError(t, err)
|
||||
@@ -127,6 +128,66 @@ func TestUserTerminatedError(t *testing.T) {
|
||||
assert.Equal(t, getExitCode(context.Cause(notifyCtx)), 143)
|
||||
}
|
||||
|
||||
func TestGetExitCode(t *testing.T) {
|
||||
t.Run("nil error returns 0", func(t *testing.T) {
|
||||
assert.Equal(t, getExitCode(nil), 0)
|
||||
})
|
||||
|
||||
t.Run("generic error returns 1", func(t *testing.T) {
|
||||
assert.Equal(t, getExitCode(errors.New("some failure")), 1)
|
||||
})
|
||||
|
||||
t.Run("StatusError with code", func(t *testing.T) {
|
||||
err := dockercli.StatusError{StatusCode: 42}
|
||||
assert.Equal(t, getExitCode(err), 42)
|
||||
})
|
||||
|
||||
t.Run("StatusError with zero code falls back to 1", func(t *testing.T) {
|
||||
err := dockercli.StatusError{StatusCode: 0, Status: "something went wrong"}
|
||||
assert.Equal(t, getExitCode(err), 1)
|
||||
})
|
||||
|
||||
t.Run("wrapped StatusError", func(t *testing.T) {
|
||||
err := fmt.Errorf("wrapper: %w", dockercli.StatusError{StatusCode: 99})
|
||||
assert.Equal(t, getExitCode(err), 99)
|
||||
})
|
||||
|
||||
t.Run("SIGINT returns 130", func(t *testing.T) {
|
||||
err := errCtxSignalTerminated{signal: syscall.SIGINT}
|
||||
assert.Equal(t, getExitCode(err), 130)
|
||||
})
|
||||
|
||||
t.Run("SIGTERM returns 143", func(t *testing.T) {
|
||||
err := errCtxSignalTerminated{signal: syscall.SIGTERM}
|
||||
assert.Equal(t, getExitCode(err), 143)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCmdErrorMessage(t *testing.T) {
|
||||
t.Run("nil error returns empty string", func(t *testing.T) {
|
||||
assert.Equal(t, cmdErrorMessage(nil), "")
|
||||
})
|
||||
|
||||
t.Run("generic error returns error message", func(t *testing.T) {
|
||||
assert.Equal(t, cmdErrorMessage(errors.New("something broke")), "something broke")
|
||||
})
|
||||
|
||||
t.Run("StatusError with Status field", func(t *testing.T) {
|
||||
err := dockercli.StatusError{Status: "build failed", StatusCode: 1}
|
||||
assert.Equal(t, cmdErrorMessage(err), "build failed")
|
||||
})
|
||||
|
||||
t.Run("StatusError with only exit code falls back to generic message", func(t *testing.T) {
|
||||
err := dockercli.StatusError{StatusCode: 42}
|
||||
assert.Equal(t, cmdErrorMessage(err), "exited with code 42")
|
||||
})
|
||||
|
||||
t.Run("wrapped error preserves message", func(t *testing.T) {
|
||||
err := fmt.Errorf("wrapper: %w", errors.New("inner failure"))
|
||||
assert.Equal(t, cmdErrorMessage(err), "wrapper: inner failure")
|
||||
})
|
||||
}
|
||||
|
||||
func TestVisitAll(t *testing.T) {
|
||||
root := &cobra.Command{Use: "root"}
|
||||
sub1 := &cobra.Command{Use: "sub1"}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
variable "GO_VERSION" {
|
||||
default = "1.25.6"
|
||||
default = null
|
||||
}
|
||||
variable "VERSION" {
|
||||
default = ""
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# ALPINE_VERSION sets the version of the alpine base image to use.
|
||||
# It must be a supported tag in the docker.io/library/alpine image repository.
|
||||
ARG ALPINE_VERSION=3.22
|
||||
ARG ALPINE_VERSION=3.23
|
||||
|
||||
FROM alpine:${ALPINE_VERSION} AS gen
|
||||
RUN apk add --no-cache bash git
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG GO_VERSION=1.25.6
|
||||
ARG GO_VERSION=1.25.8
|
||||
|
||||
# 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.22
|
||||
ARG ALPINE_VERSION=3.23
|
||||
|
||||
# BUILDX_VERSION sets the version of buildx to install in the dev container.
|
||||
# It must be a valid tag in the docker.io/docker/buildx-bin image repository
|
||||
# on Docker Hub.
|
||||
ARG BUILDX_VERSION=0.29.1
|
||||
ARG BUILDX_VERSION=0.31.1
|
||||
FROM docker/buildx-bin:${BUILDX_VERSION} AS buildx
|
||||
|
||||
FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS golang
|
||||
@@ -18,7 +18,7 @@ ENV GOTOOLCHAIN=local
|
||||
ENV CGO_ENABLED=0
|
||||
|
||||
FROM golang AS gofumpt
|
||||
ARG GOFUMPT_VERSION=v0.7.0
|
||||
ARG GOFUMPT_VERSION=v0.9.2
|
||||
RUN --mount=type=cache,target=/root/.cache/go-build \
|
||||
--mount=type=cache,target=/go/pkg/mod \
|
||||
--mount=type=tmpfs,target=/go/src/ \
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG GO_VERSION=1.25.6
|
||||
ARG GO_VERSION=1.25.8
|
||||
|
||||
# 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.22
|
||||
ARG ALPINE_VERSION=3.23
|
||||
# GOLANGCI_LINT_VERSION sets the version of the golangci/golangci-lint image to use.
|
||||
ARG GOLANGCI_LINT_VERSION=v2.6.1
|
||||
ARG GOLANGCI_LINT_VERSION=v2.9.0
|
||||
|
||||
FROM golangci/golangci-lint:${GOLANGCI_LINT_VERSION}-alpine AS golangci-lint
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG GO_VERSION=1.25.6
|
||||
ARG GO_VERSION=1.25.8
|
||||
|
||||
# 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.22
|
||||
ARG ALPINE_VERSION=3.23
|
||||
ARG MODOUTDATED_VERSION=v0.8.0
|
||||
|
||||
FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS base
|
||||
|
||||
@@ -52,7 +52,7 @@ Build an image from a Dockerfile
|
||||
> This configuration is only relevant if you're building Windows containers.
|
||||
>
|
||||
> For information about the default `docker build`, using Buildx,
|
||||
> see [`docker buildx build`](https://docs.docker.com/reference/cli/docker/build/).
|
||||
> see [`docker buildx build`](https://docs.docker.com/reference/cli/docker/buildx/build/).
|
||||
|
||||
When building with legacy builder, images are created from a Dockerfile by
|
||||
running a sequence of [commits](./container_commit.md). This process is
|
||||
|
||||
@@ -132,8 +132,8 @@ multiple times, or by passing a comma-separated list of platforms to remove.
|
||||
The following uses of this option are equivalent;
|
||||
|
||||
```console
|
||||
$ docker image rm --plaform linux/amd64 --platform linux/ppc64le myimage
|
||||
$ docker image rm --plaform linux/amd64,linux/ppc64le myimage
|
||||
$ docker image rm --platform linux/amd64 --platform linux/ppc64le myimage
|
||||
$ docker image rm --platform linux/amd64,linux/ppc64le myimage
|
||||
```
|
||||
|
||||
The following example removes the `linux/amd64` and `linux/ppc64le` variants
|
||||
|
||||
@@ -454,7 +454,23 @@ The following options can only be used for bind mounts (`type=bind`):
|
||||
Read-only mounts are made recursively read-only if kernel is v5.12 or later.
|
||||
Otherwise the Engine raises an error.</li>
|
||||
</ul>
|
||||
When the option is not specified, the default behavior correponds to setting <tt>enabled</tt>.
|
||||
When the option is not specified, the default behavior corresponds to setting <tt>enabled</tt>.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>bind-create-src</b></td>
|
||||
<td>
|
||||
By default, bind mounts require the source path to exist on the daemon host. This is a significant difference
|
||||
from the <tt>-v</tt> flag, which creates the source path if it doesn't exist.<br />
|
||||
<br />
|
||||
Set <tt>bind-create-src</tt> to create the source path on the daemon host if it doesn't exist.<br />
|
||||
<br />
|
||||
A value is optional:<br />
|
||||
<br />
|
||||
<ul>
|
||||
<li><tt>true</tt> or <tt>1</tt>: Create path on the daemon host if it doesn't exist.</li>
|
||||
<li><tt>false</tt> or <tt>0</tt>: Default behavior. Produces an error if the source path doesn't exist on the daemon host.</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -591,7 +607,8 @@ or `--volume` flag for `docker run`, with some important exceptions:
|
||||
|
||||
- When you use `--mount` with `type=bind`, the host-path must refer to an *existing*
|
||||
path on the host. The path will not be created for you and the service will fail
|
||||
with an error if the path does not exist.
|
||||
with an error if the path does not exist. You can use `bind-create-src` to
|
||||
create the host path if it doesn't exist.
|
||||
|
||||
- The `--mount` flag does not allow you to relabel a volume with `Z` or `z` flags,
|
||||
which are used for `selinux` labeling.
|
||||
|
||||
@@ -189,14 +189,14 @@ also shows the non-truncated task IDs, and error-messages, as can be seen below:
|
||||
$ docker stack ps --no-trunc voting
|
||||
|
||||
ID NAME IMAGE NODE DESIRED STATE CURREN STATE ERROR PORTS
|
||||
xim5bcqtgk1bxqz91jzo4a1s5 voting_worker.1 dockersamples/examplevotingapp_worker:latest@sha256:3e4ddf59c15f432280a2c0679c4fc5a2ee5a797023c8ef0d3baf7b1385e9fed node2 Running Runnin 32 minutes ago
|
||||
q7yik0ks1in6kv32gg6y6yjf7 voting_result.1 dockersamples/examplevotingapp_result:before@sha256:83b56996e930c292a6ae5187fda84dd6568a19d97cdb933720be15c757b7463 node1 Running Runnin 32 minutes ago
|
||||
rx5yo0866nfxc58zf4irsss6n voting_vote.1 dockersamples/examplevotingapp_vote:before@sha256:8e64b182c87de902f2b72321c89b4af4e2b942d76d0b772532ff27ec4c6ebf6 node3 Running Runnin 32 minutes ago
|
||||
tz6j82jnwrx7n2offljp3mn03 voting_db.1 postgres:9.4@sha256:6046af499eae34d2074c0b53f9a8b404716d415e4a03e68bc1d2f8064f2b027 node1 Running Runnin 32 minutes ago
|
||||
w48spazhbmxcmbjfi54gs7x90 voting_redis.1 redis:alpine@sha256:9cd405cd1ec1410eaab064a1383d0d8854d1ef74a54e1e4a92fb4ec7bdc3ee7 node2 Running Runnin 32 minutes ago
|
||||
6jj1m02freg1n3z9n1evrzsbl voting_visualizer.1 dockersamples/visualizer:stable@sha256:f924ad66c8e94b10baaf7bdb9cd491ef4e982a1d048a56a17e02bf5945401e5 node1 Running Runnin 32 minutes ago
|
||||
kqgdmededccbhz2wuc0e9hx7g voting_vote.2 dockersamples/examplevotingapp_vote:before@sha256:8e64b182c87de902f2b72321c89b4af4e2b942d76d0b772532ff27ec4c6ebf6 node2 Running Runnin 32 minutes ago
|
||||
t72q3z038jehe1wbh9gdum076 voting_redis.2 redis:alpine@sha256:9cd405cd1ec1410eaab064a1383d0d8854d1ef74a54e1e4a92fb4ec7bdc3ee7 node3 Running Runnin 32 minutes ago
|
||||
xim5bcqtgk1bxqz91jzo4a1s5 voting_worker.1 dockersamples/examplevotingapp_worker:latest@sha256:3e4ddf59c15f432280a2c0679c4fc5a2ee5a797023c8ef0d3baf7b1385e9fed node2 Running Running 32 minutes ago
|
||||
q7yik0ks1in6kv32gg6y6yjf7 voting_result.1 dockersamples/examplevotingapp_result:before@sha256:83b56996e930c292a6ae5187fda84dd6568a19d97cdb933720be15c757b7463 node1 Running Running 32 minutes ago
|
||||
rx5yo0866nfxc58zf4irsss6n voting_vote.1 dockersamples/examplevotingapp_vote:before@sha256:8e64b182c87de902f2b72321c89b4af4e2b942d76d0b772532ff27ec4c6ebf6 node3 Running Running 32 minutes ago
|
||||
tz6j82jnwrx7n2offljp3mn03 voting_db.1 postgres:9.4@sha256:6046af499eae34d2074c0b53f9a8b404716d415e4a03e68bc1d2f8064f2b027 node1 Running Running 32 minutes ago
|
||||
w48spazhbmxcmbjfi54gs7x90 voting_redis.1 redis:alpine@sha256:9cd405cd1ec1410eaab064a1383d0d8854d1ef74a54e1e4a92fb4ec7bdc3ee7 node2 Running Running 32 minutes ago
|
||||
6jj1m02freg1n3z9n1evrzsbl voting_visualizer.1 dockersamples/visualizer:stable@sha256:f924ad66c8e94b10baaf7bdb9cd491ef4e982a1d048a56a17e02bf5945401e5 node1 Running Running 32 minutes ago
|
||||
kqgdmededccbhz2wuc0e9hx7g voting_vote.2 dockersamples/examplevotingapp_vote:before@sha256:8e64b182c87de902f2b72321c89b4af4e2b942d76d0b772532ff27ec4c6ebf6 node2 Running Running 32 minutes ago
|
||||
t72q3z038jehe1wbh9gdum076 voting_redis.2 redis:alpine@sha256:9cd405cd1ec1410eaab064a1383d0d8854d1ef74a54e1e4a92fb4ec7bdc3ee7 node3 Running Running 32 minutes ago
|
||||
```
|
||||
|
||||
### <a name="quiet"></a> Only display task IDs (-q, --quiet)
|
||||
|
||||
@@ -28,7 +28,6 @@ WARNING! This will remove anonymous local volumes not used by at least one conta
|
||||
Are you sure you want to continue? [y/N] y
|
||||
Deleted Volumes:
|
||||
07c7bdf3e34ab76d921894c2b834f073721fccfbbcba792aa7648e3a7a664c2e
|
||||
my-named-vol
|
||||
|
||||
Total reclaimed space: 36 B
|
||||
```
|
||||
|
||||
@@ -610,7 +610,7 @@ $ sudo dockerd --add-runtime <runtime>=<path>
|
||||
|
||||
Defining runtime arguments via the command line is not supported.
|
||||
|
||||
For an example configuration for a runc drop-in replacment, see
|
||||
For an example configuration for a runc drop-in replacement, see
|
||||
[Alternative container runtimes > youki](https://docs.docker.com/engine/daemon/alternative-runtimes/#youki)
|
||||
|
||||
##### Configure the default container runtime
|
||||
|
||||
@@ -252,6 +252,14 @@ two paths. The `source` path is the location on the host that you want to
|
||||
bind mount into the container. The `target` path is the mount destination
|
||||
inside the container.
|
||||
|
||||
By default, bind mounts require the source path to exist on the daemon host. If the
|
||||
source path doesn't exist, an error is returned. To create the source path on
|
||||
the daemon host if it doesn't exist, use the `bind-create-src` option:
|
||||
|
||||
```console
|
||||
$ docker run -it --mount type=bind,source=[PATH],target=[PATH],bind-create-src busybox
|
||||
```
|
||||
|
||||
Bind mounts are read-write by default, meaning that you can both read and write
|
||||
files to and from the mounted location from the container. Changes that you
|
||||
make, such as adding or editing files, are reflected on the host filesystem:
|
||||
|
||||
@@ -4,7 +4,7 @@ services:
|
||||
image: 'registry:3'
|
||||
|
||||
engine:
|
||||
image: 'docker:${ENGINE_VERSION:-28}-dind'
|
||||
image: 'docker:${ENGINE_VERSION:-29}-dind'
|
||||
privileged: true
|
||||
command: ['--insecure-registry=registry:5000', '--experimental']
|
||||
environment:
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"io"
|
||||
"math/rand"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
@@ -160,6 +161,36 @@ func TestMountSubvolume(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMountBindCreateMountpoint(t *testing.T) {
|
||||
environment.SkipIfDaemonNotLinux(t)
|
||||
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
value string
|
||||
expectSuccess bool
|
||||
}{
|
||||
{name: "flag only", value: "bind-create-src", expectSuccess: true},
|
||||
{name: "true", value: "bind-create-src=true", expectSuccess: true},
|
||||
{name: "1", value: "bind-create-src=1", expectSuccess: true},
|
||||
{name: "false", value: "bind-create-src=false", expectSuccess: false},
|
||||
{name: "0", value: "bind-create-src=0", expectSuccess: false},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
srcPath := filepath.Join("/tmp", t.Name(), "does", "not", "exist")
|
||||
result := icmd.RunCommand("docker", "run", "--rm",
|
||||
"--mount", "type=bind,src="+srcPath+",dst=/mnt,"+tc.value,
|
||||
fixtures.AlpineImage, "cat", "/proc/mounts")
|
||||
if tc.expectSuccess {
|
||||
result.Assert(t, icmd.Success)
|
||||
assert.Check(t, is.Contains(result.Stdout(), "/mnt"))
|
||||
} else {
|
||||
result.Assert(t, icmd.Expected{ExitCode: 125})
|
||||
assert.Check(t, is.Contains(result.Stderr(), srcPath))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessTermination(t *testing.T) {
|
||||
var out bytes.Buffer
|
||||
cmd := icmd.Command("docker", "run", "--rm", "-i", fixtures.AlpineImage,
|
||||
|
||||
+1
-4
@@ -2,16 +2,13 @@
|
||||
|
||||
# ENGINE_VERSION is the version of the (docker-in-docker) Docker Engine to
|
||||
# test against.
|
||||
ARG ENGINE_VERSION=28
|
||||
ARG ENGINE_VERSION=29
|
||||
|
||||
FROM docker:${ENGINE_VERSION}-dind
|
||||
|
||||
# the openssh-client update is needed for security reasons when using docker:23.0-dind, currently maintained as an lts by mirantis
|
||||
RUN apk --no-cache add openssl openssh-client openssh-server shadow && \
|
||||
apk --no-cache upgrade openssl openssh-client openssh-server && \
|
||||
# TODO(krissetto): `groupadd` can be removed once we only test against moby >= v24
|
||||
# see https://github.com/docker-library/docker/pull/470
|
||||
groupadd -f docker && \
|
||||
useradd --create-home --shell /bin/sh --password $(head -c32 /dev/urandom | base64) penguin && \
|
||||
usermod -aG docker penguin && \
|
||||
ssh-keygen -A
|
||||
|
||||
@@ -7,9 +7,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/docker/cli/cli/streams"
|
||||
"github.com/docker/cli/internal/test"
|
||||
"github.com/moby/moby/client/pkg/progress"
|
||||
"github.com/moby/moby/client/pkg/streamformatter"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
@@ -23,23 +20,12 @@ func TestDisplay(t *testing.T) {
|
||||
})
|
||||
|
||||
go func() {
|
||||
id := test.RandomID()[:12] // short-ID
|
||||
progressOutput := streamformatter.NewJSONProgressOutput(server, true)
|
||||
for i := 0; i < 100; i++ {
|
||||
for range 100 {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
assert.NilError(t, server.Close(), "failed to close jsonmessage server")
|
||||
return
|
||||
default:
|
||||
err := progressOutput.WriteProgress(progress.Progress{
|
||||
ID: id,
|
||||
Message: "Downloading",
|
||||
Current: int64(i),
|
||||
Total: 100,
|
||||
})
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
@@ -50,8 +36,7 @@ func TestDisplay(t *testing.T) {
|
||||
|
||||
done := make(chan error)
|
||||
go func() {
|
||||
out := streams.NewOut(io.Discard)
|
||||
done <- Display(streamCtx, client, out)
|
||||
done <- Display(streamCtx, client, streams.NewOut(io.Discard))
|
||||
}()
|
||||
|
||||
cancelStream()
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.24
|
||||
|
||||
package test
|
||||
|
||||
import (
|
||||
@@ -15,12 +18,12 @@ func CompareMultipleValues(t *testing.T, value, expected string) {
|
||||
// be guaranteed to have the same order as our expected value
|
||||
// We'll create maps and use reflect.DeepEquals to check instead:
|
||||
entriesMap := make(map[string]string)
|
||||
for _, entry := range strings.Split(value, ",") {
|
||||
for entry := range strings.SplitSeq(value, ",") {
|
||||
k, v, _ := strings.Cut(entry, "=")
|
||||
entriesMap[k] = v
|
||||
}
|
||||
expMap := make(map[string]string)
|
||||
for _, exp := range strings.Split(expected, ",") {
|
||||
for exp := range strings.SplitSeq(expected, ",") {
|
||||
k, v, _ := strings.Cut(exp, "=")
|
||||
expMap[k] = v
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.24
|
||||
|
||||
package volumespec
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
@@ -25,7 +29,7 @@ func Parse(spec string) (VolumeConfig, error) {
|
||||
return volume, nil
|
||||
}
|
||||
|
||||
var buffer []rune
|
||||
buffer := make([]rune, 0, len(spec))
|
||||
for _, char := range spec + string(endOfSpec) {
|
||||
switch {
|
||||
case isWindowsDrive(buffer, char):
|
||||
@@ -35,7 +39,7 @@ func Parse(spec string) (VolumeConfig, error) {
|
||||
populateType(&volume)
|
||||
return volume, fmt.Errorf("invalid spec: %s: %w", spec, err)
|
||||
}
|
||||
buffer = []rune{}
|
||||
buffer = buffer[:0] // reset, but reuse capacity
|
||||
default:
|
||||
buffer = append(buffer, char)
|
||||
}
|
||||
@@ -67,7 +71,7 @@ func populateFieldFromBuffer(char rune, buffer []rune, volume *VolumeConfig) err
|
||||
case char == ':':
|
||||
return errors.New("too many colons")
|
||||
}
|
||||
for _, option := range strings.Split(strBuffer, ",") {
|
||||
for option := range strings.SplitSeq(strBuffer, ",") {
|
||||
switch option {
|
||||
case "ro":
|
||||
volume.ReadOnly = true
|
||||
@@ -86,12 +90,7 @@ func populateFieldFromBuffer(char rune, buffer []rune, volume *VolumeConfig) err
|
||||
}
|
||||
|
||||
func isBindOption(option string) bool {
|
||||
for _, propagation := range mount.Propagations {
|
||||
if mount.Propagation(option) == propagation {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return slices.Contains(mount.Propagations, mount.Propagation(option))
|
||||
}
|
||||
|
||||
func populateType(volume *VolumeConfig) {
|
||||
|
||||
+2
-2
@@ -86,7 +86,7 @@ Repository entries separated by a colon for all images:
|
||||
|
||||
docker image ls --format "{{.ID}}: {{.Repository}}"
|
||||
77af4d6b9913: <none>
|
||||
b6fa739cedf5: committ
|
||||
b6fa739cedf5: commit
|
||||
78a85c484bad: ipbabble
|
||||
30557a29d5ab: docker
|
||||
5ed6274db6ce: <none>
|
||||
@@ -100,7 +100,7 @@ To list all images with their repository and tag in a table format you can use:
|
||||
docker image ls --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}"
|
||||
IMAGE ID REPOSITORY TAG
|
||||
77af4d6b9913 <none> <none>
|
||||
b6fa739cedf5 committ latest
|
||||
b6fa739cedf5 commit latest
|
||||
78a85c484bad ipbabble <none>
|
||||
30557a29d5ab docker latest
|
||||
5ed6274db6ce <none> <none>
|
||||
|
||||
+1
-1
@@ -105,7 +105,7 @@ func (*GpuOpts) Type() string {
|
||||
|
||||
// String returns a string repr of this option
|
||||
func (o *GpuOpts) String() string {
|
||||
gpus := []string{}
|
||||
gpus := make([]string, 0, len(o.values))
|
||||
for _, gpu := range o.values {
|
||||
gpus = append(gpus, fmt.Sprintf("%v", gpu))
|
||||
}
|
||||
|
||||
+7
-2
@@ -57,7 +57,7 @@ func (m *MountOpt) Set(value string) error {
|
||||
|
||||
if !hasValue {
|
||||
switch key {
|
||||
case "readonly", "ro", "volume-nocopy", "bind-nonrecursive":
|
||||
case "readonly", "ro", "volume-nocopy", "bind-nonrecursive", "bind-create-src":
|
||||
// boolean values
|
||||
default:
|
||||
return fmt.Errorf("invalid field '%s' must be a key=value pair", field)
|
||||
@@ -102,6 +102,11 @@ func (m *MountOpt) Set(value string) error {
|
||||
default:
|
||||
return fmt.Errorf(`invalid value for %s: %s (must be "enabled", "disabled", "writable", or "readonly")`, key, val)
|
||||
}
|
||||
case "bind-create-src":
|
||||
ensureBindOptions(&mount).CreateMountpoint, err = parseBoolValue(key, val, hasValue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "volume-subpath":
|
||||
ensureVolumeOptions(&mount).Subpath = val
|
||||
case "volume-nocopy":
|
||||
@@ -151,7 +156,7 @@ func (*MountOpt) Type() string {
|
||||
|
||||
// String returns a string repr of this option
|
||||
func (m *MountOpt) String() string {
|
||||
mounts := []string{}
|
||||
mounts := make([]string, 0, len(m.values))
|
||||
for _, mount := range m.values {
|
||||
repr := fmt.Sprintf("%s %s %s", mount.Type, mount.Source, mount.Target)
|
||||
mounts = append(mounts, repr)
|
||||
|
||||
@@ -475,6 +475,50 @@ func TestMountOptSetTmpfsNoError(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMountOptSetBindCreateSrc(t *testing.T) {
|
||||
tests := []struct {
|
||||
value string
|
||||
exp bool
|
||||
expErr string
|
||||
}{
|
||||
{value: "", exp: false},
|
||||
{value: "bind-create-src", exp: true},
|
||||
{value: "bind-create-src=", expErr: `invalid value for 'bind-create-src': value is empty`},
|
||||
{value: "bind-create-src= true", expErr: `invalid value for 'bind-create-src' in 'bind-create-src= true': value should not have whitespace`},
|
||||
{value: "bind-create-src=no", expErr: `invalid value for 'bind-create-src': invalid boolean value ("no"): must be one of "true", "1", "false", or "0" (default "true")`},
|
||||
{value: "bind-create-src=1", exp: true},
|
||||
{value: "bind-create-src=true", exp: true},
|
||||
{value: "bind-create-src=0", exp: false},
|
||||
{value: "bind-create-src=false", exp: false},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
name := tc.value
|
||||
if name == "" {
|
||||
name = "not set"
|
||||
}
|
||||
t.Run(name, func(t *testing.T) {
|
||||
val := "type=bind,target=/foo,source=/foo"
|
||||
if tc.value != "" {
|
||||
val += "," + tc.value
|
||||
}
|
||||
var m MountOpt
|
||||
err := m.Set(val)
|
||||
if tc.expErr != "" {
|
||||
assert.Error(t, err, tc.expErr)
|
||||
return
|
||||
}
|
||||
assert.NilError(t, err)
|
||||
if tc.value == "" {
|
||||
assert.Check(t, is.Nil(m.values[0].BindOptions))
|
||||
} else {
|
||||
assert.Check(t, m.values[0].BindOptions != nil)
|
||||
assert.Check(t, is.Equal(m.values[0].BindOptions.CreateMountpoint, tc.exp))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMountOptSetBindRecursive(t *testing.T) {
|
||||
t.Run("enabled", func(t *testing.T) {
|
||||
var m MountOpt
|
||||
|
||||
+5
-6
@@ -1,3 +1,6 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.24
|
||||
|
||||
package opts
|
||||
|
||||
import (
|
||||
@@ -7,6 +10,7 @@ import (
|
||||
"math/big"
|
||||
"net"
|
||||
"path"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/internal/lazyregexp"
|
||||
@@ -104,12 +108,7 @@ func (opts *ListOpts) GetAllOrEmpty() []string {
|
||||
|
||||
// Get checks the existence of the specified key.
|
||||
func (opts *ListOpts) Get(key string) bool {
|
||||
for _, k := range *opts.values {
|
||||
if k == key {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return slices.Contains(*opts.values, key)
|
||||
}
|
||||
|
||||
// Len returns the amount of element in the slice.
|
||||
|
||||
@@ -86,7 +86,7 @@ func (*ConfigOpt) Type() string {
|
||||
|
||||
// String returns a string repr of this option
|
||||
func (o *ConfigOpt) String() string {
|
||||
configs := []string{}
|
||||
configs := make([]string, 0, len(o.values))
|
||||
for _, config := range o.values {
|
||||
repr := fmt.Sprintf("%s -> %s", config.ConfigName, config.File.Name)
|
||||
configs = append(configs, repr)
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.24
|
||||
|
||||
package swarmopts
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/go-connections/nat"
|
||||
@@ -371,13 +375,7 @@ func TestConvertPortToPortConfigWithIP(t *testing.T) {
|
||||
|
||||
func assertContains(t *testing.T, portConfigs []swarm.PortConfig, expected swarm.PortConfig) {
|
||||
t.Helper()
|
||||
contains := false
|
||||
for _, portConfig := range portConfigs {
|
||||
if portConfig == expected {
|
||||
contains = true
|
||||
break
|
||||
}
|
||||
}
|
||||
contains := slices.Contains(portConfigs, expected)
|
||||
if !contains {
|
||||
t.Errorf("expected %v to contain %v, did not", portConfigs, expected)
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ func (*SecretOpt) Type() string {
|
||||
|
||||
// String returns a string repr of this option
|
||||
func (o *SecretOpt) String() string {
|
||||
secrets := []string{}
|
||||
secrets := make([]string, 0, len(o.values))
|
||||
for _, secret := range o.values {
|
||||
repr := fmt.Sprintf("%s -> %s", secret.SecretName, secret.File.Name)
|
||||
secrets = append(secrets, repr)
|
||||
|
||||
@@ -102,6 +102,15 @@ if [ "$CGO_ENABLED" = "1" ] && [ "$GO_LINKMODE" = "static" ]; then
|
||||
# compiling statically with CGO enabled requires osusergo and netgo to be set.
|
||||
GO_BUILDTAGS="$GO_BUILDTAGS osusergo netgo"
|
||||
fi
|
||||
# XXX: Disable netgo on Windows and use Windows system resolver instead.
|
||||
#
|
||||
# go1.19 and newer added support for netgo on Windows (https://go.dev/doc/go1.19#net),
|
||||
# which may not respect VPN adaptors (such as Twingate) due to resolver ordering,
|
||||
# resulting in queries being sent through the local network adapter instead of the
|
||||
# VPN tunnel. See https://github.com/docker/cli/issues/6665
|
||||
if [ "$(go env GOOS)" = "windows" ]; then
|
||||
GO_BUILDTAGS=$(echo "$GO_BUILDTAGS" | sed 's/\(^\| \)netgo\( \|$\)/\1/g')
|
||||
fi
|
||||
if [ -n "$GO_STRIP" ]; then
|
||||
# if stripping enabled and building with llvm < 12 against darwin/amd64
|
||||
# platform, it will fail with:
|
||||
|
||||
+23
-23
@@ -26,10 +26,10 @@ require (
|
||||
github.com/google/go-cmp v0.7.0
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/mattn/go-runewidth v0.0.19
|
||||
github.com/mattn/go-runewidth v0.0.20
|
||||
github.com/moby/go-archive v0.2.0
|
||||
github.com/moby/moby/api v1.53.0
|
||||
github.com/moby/moby/client v0.2.2
|
||||
github.com/moby/moby/api v1.54.0
|
||||
github.com/moby/moby/client v0.3.0
|
||||
github.com/moby/patternmatcher v0.6.0
|
||||
github.com/moby/swarmkit/v2 v2.1.1
|
||||
github.com/moby/sys/atomicwriter v0.1.0
|
||||
@@ -47,19 +47,19 @@ require (
|
||||
github.com/spf13/pflag v1.0.10
|
||||
github.com/tonistiigi/go-rosetta v0.0.0-20220804170347-3f4430f2d346
|
||||
github.com/xeipuuv/gojsonschema v1.2.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0
|
||||
go.opentelemetry.io/otel v1.38.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0
|
||||
go.opentelemetry.io/otel/metric v1.38.0
|
||||
go.opentelemetry.io/otel/sdk v1.38.0
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0
|
||||
go.opentelemetry.io/otel/trace v1.38.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0
|
||||
go.opentelemetry.io/otel v1.40.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.40.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0
|
||||
go.opentelemetry.io/otel/metric v1.40.0
|
||||
go.opentelemetry.io/otel/sdk v1.40.0
|
||||
go.opentelemetry.io/otel/sdk/metric v1.40.0
|
||||
go.opentelemetry.io/otel/trace v1.40.0
|
||||
go.yaml.in/yaml/v3 v3.0.4
|
||||
golang.org/x/sync v0.19.0
|
||||
golang.org/x/sys v0.40.0
|
||||
golang.org/x/term v0.39.0
|
||||
golang.org/x/text v0.33.0
|
||||
golang.org/x/sys v0.41.0
|
||||
golang.org/x/term v0.40.0
|
||||
golang.org/x/text v0.34.0
|
||||
gotest.tools/v3 v3.5.2
|
||||
tags.cncf.io/container-device-interface v1.1.0
|
||||
)
|
||||
@@ -80,9 +80,9 @@ require (
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/klauspost/compress v1.18.3 // indirect
|
||||
github.com/klauspost/compress v1.18.4 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/sys/user v0.4.0 // indirect
|
||||
github.com/moby/sys/userns v0.1.0 // indirect
|
||||
@@ -96,12 +96,12 @@ require (
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
go.etcd.io/etcd/raft/v3 v3.5.16 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
|
||||
golang.org/x/net v0.49.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
|
||||
golang.org/x/net v0.50.0 // indirect
|
||||
golang.org/x/time v0.14.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect
|
||||
google.golang.org/grpc v1.75.0 // indirect
|
||||
google.golang.org/protobuf v1.36.9 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect
|
||||
google.golang.org/grpc v1.78.0 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
)
|
||||
|
||||
+46
-46
@@ -87,8 +87,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 h1:X+2YciYSxvMQK0UZ7sg45ZVabVZBeBuvMkmuI2V3Fak=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7/go.mod h1:lW34nIZuQ8UDPdkon5fmfp2l3+ZkQ2me/+oecHYLOII=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
@@ -96,8 +96,8 @@ github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.18.3 h1:9PJRvfbmTabkOX8moIpXPbMMbYN60bWImDDU7L+/6zw=
|
||||
github.com/klauspost/compress v1.18.3/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||
github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c=
|
||||
github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
@@ -106,17 +106,17 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
|
||||
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||
github.com/mattn/go-runewidth v0.0.20 h1:WcT52H91ZUAwy8+HUkdM3THM6gXqXuLJi9O3rjcQQaQ=
|
||||
github.com/mattn/go-runewidth v0.0.20/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8=
|
||||
github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU=
|
||||
github.com/moby/moby/api v1.53.0 h1:PihqG1ncw4W+8mZs69jlwGXdaYBeb5brF6BL7mPIS/w=
|
||||
github.com/moby/moby/api v1.53.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc=
|
||||
github.com/moby/moby/client v0.2.2 h1:Pt4hRMCAIlyjL3cr8M5TrXCwKzguebPAc2do2ur7dEM=
|
||||
github.com/moby/moby/client v0.2.2/go.mod h1:2EkIPVNCqR05CMIzL1mfA07t0HvVUUOl85pasRz/GmQ=
|
||||
github.com/moby/moby/api v1.54.0 h1:7kbUgyiKcoBhm0UrWbdrMs7RX8dnwzURKVbZGy2GnL0=
|
||||
github.com/moby/moby/api v1.54.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc=
|
||||
github.com/moby/moby/client v0.3.0 h1:UUGL5okry+Aomj3WhGt9Aigl3ZOxZGqR7XPo+RLPlKs=
|
||||
github.com/moby/moby/client v0.3.0/go.mod h1:HJgFbJRvogDQjbM8fqc1MCEm4mIAGMLjXbgwoZp6jCQ=
|
||||
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
|
||||
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
|
||||
github.com/moby/swarmkit/v2 v2.1.1 h1:yvTJ8MMCc3f0qTA44J6R59EZ5yZawdYopkpuLk4+ICU=
|
||||
@@ -206,26 +206,26 @@ go.etcd.io/etcd/raft/v3 v3.5.16 h1:zBXA3ZUpYs1AwiLGPafYAKKl/CORn/uaxYDwlNwndAk=
|
||||
go.etcd.io/etcd/raft/v3 v3.5.16/go.mod h1:P4UP14AxofMJ/54boWilabqqWoW9eLodl6I5GdGzazI=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg=
|
||||
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
||||
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 h1:vl9obrcoWVKp/lwl8tRE33853I8Xru9HFbw/skNeLs8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0/go.mod h1:GAXRxmLJcVM3u22IjTg74zWBrRCKq8BnOqUVLodpcpw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4Dc5leUqENgGuQImwLo4WnuXFPetmPpkLi2IrX54=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0/go.mod h1:Kz/oCE7z5wuyhPxsXDuaPteSWqjSBD5YaSdbxZYGbGk=
|
||||
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
|
||||
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
|
||||
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
|
||||
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
||||
go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=
|
||||
go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 h1:7iP2uCb7sGddAr30RRS6xjKy7AZ2JtTOPA3oolgVSw8=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0/go.mod h1:c7hN3ddxs/z6q9xwvfLPk+UHlWRQyaeR1LdgfL/66l0=
|
||||
go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
|
||||
go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.40.0 h1:NOyNnS19BF2SUDApbOKbDtWZ0IK7b8FJ2uAGdIWOGb0=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.40.0/go.mod h1:VL6EgVikRLcJa9ftukrHu/ZkkhFBSo1lzvdBC9CF1ss=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 h1:QKdN8ly8zEMrByybbQgv8cWBcdAarwmIPZ6FThrWXJs=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0/go.mod h1:bTdK1nhqF76qiPoCCdyFIV+N/sRHYXYCTQc+3VCi3MI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 h1:DvJDOPmSWQHWywQS6lKL+pb8s3gBLOZUtw4N+mavW1I=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0/go.mod h1:EtekO9DEJb4/jRyN4v4Qjc2yA7AtfCBuz2FynRUWTXs=
|
||||
go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=
|
||||
go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=
|
||||
go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=
|
||||
go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=
|
||||
go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
|
||||
go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
|
||||
go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A=
|
||||
go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
@@ -242,8 +242,8 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
|
||||
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
|
||||
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
|
||||
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -259,14 +259,14 @@ golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
|
||||
golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
|
||||
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
|
||||
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg=
|
||||
golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
|
||||
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
|
||||
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
|
||||
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
|
||||
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
||||
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -279,14 +279,14 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc=
|
||||
google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4=
|
||||
google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
|
||||
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
|
||||
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
|
||||
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
|
||||
+1
@@ -27,6 +27,7 @@ go_library(
|
||||
"//internal/httprule",
|
||||
"//utilities",
|
||||
"@org_golang_google_genproto_googleapis_api//httpbody",
|
||||
"@org_golang_google_grpc//:grpc",
|
||||
"@org_golang_google_grpc//codes",
|
||||
"@org_golang_google_grpc//grpclog",
|
||||
"@org_golang_google_grpc//health/grpc_health_v1",
|
||||
|
||||
+3
-3
@@ -201,13 +201,13 @@ func annotateContext(ctx context.Context, mux *ServeMux, req *http.Request, rpcM
|
||||
if timeout != 0 {
|
||||
ctx, _ = context.WithTimeout(ctx, timeout)
|
||||
}
|
||||
if len(pairs) == 0 {
|
||||
return ctx, nil, nil
|
||||
}
|
||||
md := metadata.Pairs(pairs...)
|
||||
for _, mda := range mux.metadataAnnotators {
|
||||
md = metadata.Join(md, mda(ctx, req))
|
||||
}
|
||||
if len(md) == 0 {
|
||||
return ctx, nil, nil
|
||||
}
|
||||
return ctx, md, nil
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -66,7 +66,7 @@ func (j *JSONPb) marshalTo(w io.Writer, v interface{}) error {
|
||||
|
||||
var (
|
||||
// protoMessageType is stored to prevent constant lookup of the same type at runtime.
|
||||
protoMessageType = reflect.TypeOf((*proto.Message)(nil)).Elem()
|
||||
protoMessageType = reflect.TypeFor[proto.Message]()
|
||||
)
|
||||
|
||||
// marshalNonProto marshals a non-message field of a protobuf message.
|
||||
@@ -325,9 +325,9 @@ type protoEnum interface {
|
||||
EnumDescriptor() ([]byte, []int)
|
||||
}
|
||||
|
||||
var typeProtoEnum = reflect.TypeOf((*protoEnum)(nil)).Elem()
|
||||
var typeProtoEnum = reflect.TypeFor[protoEnum]()
|
||||
|
||||
var typeProtoMessage = reflect.TypeOf((*proto.Message)(nil)).Elem()
|
||||
var typeProtoMessage = reflect.TypeFor[proto.Message]()
|
||||
|
||||
// Delimiter for newline encoded JSON streams.
|
||||
func (j *JSONPb) Delimiter() []byte {
|
||||
|
||||
+13
-5
@@ -10,6 +10,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
"google.golang.org/grpc/health/grpc_health_v1"
|
||||
@@ -281,15 +282,22 @@ func WithHealthEndpointAt(healthCheckClient grpc_health_v1.HealthClient, endpoin
|
||||
http.MethodGet, endpointPath, func(w http.ResponseWriter, r *http.Request, _ map[string]string,
|
||||
) {
|
||||
_, outboundMarshaler := MarshalerForRequest(s, r)
|
||||
|
||||
resp, err := healthCheckClient.Check(r.Context(), &grpc_health_v1.HealthCheckRequest{
|
||||
Service: r.URL.Query().Get("service"),
|
||||
})
|
||||
annotatedContext, err := AnnotateContext(r.Context(), s, r, grpc_health_v1.Health_Check_FullMethodName, WithHTTPPathPattern(endpointPath))
|
||||
if err != nil {
|
||||
s.errorHandler(r.Context(), s, outboundMarshaler, w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
var md ServerMetadata
|
||||
resp, err := healthCheckClient.Check(annotatedContext, &grpc_health_v1.HealthCheckRequest{
|
||||
Service: r.URL.Query().Get("service"),
|
||||
}, grpc.Header(&md.HeaderMD), grpc.Trailer(&md.TrailerMD))
|
||||
annotatedContext = NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
s.errorHandler(annotatedContext, s, outboundMarshaler, w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
if resp.GetStatus() != grpc_health_v1.HealthCheckResponse_SERVING {
|
||||
@@ -300,7 +308,7 @@ func WithHealthEndpointAt(healthCheckClient grpc_health_v1.HealthClient, endpoin
|
||||
err = status.Error(codes.NotFound, resp.String())
|
||||
}
|
||||
|
||||
s.errorHandler(r.Context(), s, outboundMarshaler, w, r, err)
|
||||
s.errorHandler(annotatedContext, s, outboundMarshaler, w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
+15
-8
@@ -7,7 +7,7 @@ This package provides various compression algorithms.
|
||||
* Optimized [deflate](https://godoc.org/github.com/klauspost/compress/flate) packages which can be used as a dropin replacement for [gzip](https://godoc.org/github.com/klauspost/compress/gzip), [zip](https://godoc.org/github.com/klauspost/compress/zip) and [zlib](https://godoc.org/github.com/klauspost/compress/zlib).
|
||||
* [snappy](https://github.com/klauspost/compress/tree/master/snappy) is a drop-in replacement for `github.com/golang/snappy` offering better compression and concurrent streams.
|
||||
* [huff0](https://github.com/klauspost/compress/tree/master/huff0) and [FSE](https://github.com/klauspost/compress/tree/master/fse) implementations for raw entropy encoding.
|
||||
* [gzhttp](https://github.com/klauspost/compress/tree/master/gzhttp) Provides client and server wrappers for handling gzipped requests efficiently.
|
||||
* [gzhttp](https://github.com/klauspost/compress/tree/master/gzhttp) Provides client and server wrappers for handling gzipped/zstd HTTP requests efficiently.
|
||||
* [pgzip](https://github.com/klauspost/pgzip) is a separate package that provides a very fast parallel gzip implementation.
|
||||
|
||||
[](https://pkg.go.dev/github.com/klauspost/compress?tab=subdirectories)
|
||||
@@ -26,8 +26,14 @@ This package will support the current Go version and 2 versions back.
|
||||
Use the links above for more information on each.
|
||||
|
||||
# changelog
|
||||
* Jan 16th, 2026 [1.18.3](https://github.com/klauspost/compress/releases/tag/v1.18.3)
|
||||
* Downstream CVE-2025-61728. See [golang/go#77102](https://github.com/golang/go/issues/77102).
|
||||
|
||||
* Oct 20, 2025 - [1.18.1](https://github.com/klauspost/compress/releases/tag/v1.18.1)
|
||||
* Dec 1st, 2025 - [1.18.2](https://github.com/klauspost/compress/releases/tag/v1.18.2)
|
||||
* flate: Fix invalid encoding on level 9 with single value input in https://github.com/klauspost/compress/pull/1115
|
||||
* flate: reduce stateless allocations by @RXamzin in https://github.com/klauspost/compress/pull/1106
|
||||
|
||||
* Oct 20, 2025 - [1.18.1](https://github.com/klauspost/compress/releases/tag/v1.18.1) - RETRACTED
|
||||
* zstd: Add simple zstd EncodeTo/DecodeTo functions https://github.com/klauspost/compress/pull/1079
|
||||
* zstd: Fix incorrect buffer size in dictionary encodes https://github.com/klauspost/compress/pull/1059
|
||||
* s2: check for cap, not len of buffer in EncodeBetter/Best by @vdarulis in https://github.com/klauspost/compress/pull/1080
|
||||
@@ -603,7 +609,7 @@ While the release has been extensively tested, it is recommended to testing when
|
||||
|
||||
# deflate usage
|
||||
|
||||
The packages are drop-in replacements for standard libraries. Simply replace the import path to use them:
|
||||
The packages are drop-in replacements for standard library [deflate](https://godoc.org/github.com/klauspost/compress/flate), [gzip](https://godoc.org/github.com/klauspost/compress/gzip), [zip](https://godoc.org/github.com/klauspost/compress/zip), and [zlib](https://godoc.org/github.com/klauspost/compress/zlib). Simply replace the import path to use them:
|
||||
|
||||
Typical speed is about 2x of the standard library packages.
|
||||
|
||||
@@ -614,17 +620,15 @@ Typical speed is about 2x of the standard library packages.
|
||||
| `archive/zip` | `github.com/klauspost/compress/zip` | [zip](https://pkg.go.dev/github.com/klauspost/compress/zip?tab=doc) |
|
||||
| `compress/flate` | `github.com/klauspost/compress/flate` | [flate](https://pkg.go.dev/github.com/klauspost/compress/flate?tab=doc) |
|
||||
|
||||
* Optimized [deflate](https://godoc.org/github.com/klauspost/compress/flate) packages which can be used as a dropin replacement for [gzip](https://godoc.org/github.com/klauspost/compress/gzip), [zip](https://godoc.org/github.com/klauspost/compress/zip) and [zlib](https://godoc.org/github.com/klauspost/compress/zlib).
|
||||
You may also be interested in [pgzip](https://github.com/klauspost/pgzip), which is a drop-in replacement for gzip, which support multithreaded compression on big files and the optimized [crc32](https://github.com/klauspost/crc32) package used by these packages.
|
||||
|
||||
You may also be interested in [pgzip](https://github.com/klauspost/pgzip), which is a drop in replacement for gzip, which support multithreaded compression on big files and the optimized [crc32](https://github.com/klauspost/crc32) package used by these packages.
|
||||
|
||||
The packages contains the same as the standard library, so you can use the godoc for that: [gzip](http://golang.org/pkg/compress/gzip/), [zip](http://golang.org/pkg/archive/zip/), [zlib](http://golang.org/pkg/compress/zlib/), [flate](http://golang.org/pkg/compress/flate/).
|
||||
The packages implement the same API as the standard library, so you can use the original godoc documentation: [gzip](http://golang.org/pkg/compress/gzip/), [zip](http://golang.org/pkg/archive/zip/), [zlib](http://golang.org/pkg/compress/zlib/), [flate](http://golang.org/pkg/compress/flate/).
|
||||
|
||||
Currently there is only minor speedup on decompression (mostly CRC32 calculation).
|
||||
|
||||
Memory usage is typically 1MB for a Writer. stdlib is in the same range.
|
||||
If you expect to have a lot of concurrently allocated Writers consider using
|
||||
the stateless compress described below.
|
||||
the stateless compression described below.
|
||||
|
||||
For compression performance, see: [this spreadsheet](https://docs.google.com/spreadsheets/d/1nuNE2nPfuINCZJRMt6wFWhKpToF95I47XjSsc-1rbPQ/edit?usp=sharing).
|
||||
|
||||
@@ -684,3 +688,6 @@ Here are other packages of good quality and pure Go (no cgo wrappers or autoconv
|
||||
|
||||
This code is licensed under the same conditions as the original Go code. See LICENSE file.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+19
-9
@@ -39,9 +39,6 @@ type Decoder struct {
|
||||
|
||||
frame *frameDec
|
||||
|
||||
// Custom dictionaries.
|
||||
dicts map[uint32]*dict
|
||||
|
||||
// streamWg is the waitgroup for all streams
|
||||
streamWg sync.WaitGroup
|
||||
}
|
||||
@@ -101,12 +98,10 @@ func NewReader(r io.Reader, opts ...DOption) (*Decoder, error) {
|
||||
d.current.err = ErrDecoderNilInput
|
||||
}
|
||||
|
||||
// Transfer option dicts.
|
||||
d.dicts = make(map[uint32]*dict, len(d.o.dicts))
|
||||
for _, dc := range d.o.dicts {
|
||||
d.dicts[dc.id] = dc
|
||||
// Initialize dict map if needed.
|
||||
if d.o.dicts == nil {
|
||||
d.o.dicts = make(map[uint32]*dict)
|
||||
}
|
||||
d.o.dicts = nil
|
||||
|
||||
// Create decoders
|
||||
d.decoders = make(chan *blockDec, d.o.concurrent)
|
||||
@@ -238,6 +233,21 @@ func (d *Decoder) Reset(r io.Reader) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ResetWithOptions will reset the decoder and apply the given options
|
||||
// for the next stream or DecodeAll operation.
|
||||
// Options are applied on top of the existing options.
|
||||
// Some options cannot be changed on reset and will return an error.
|
||||
func (d *Decoder) ResetWithOptions(r io.Reader, opts ...DOption) error {
|
||||
d.o.resetOpt = true
|
||||
defer func() { d.o.resetOpt = false }()
|
||||
for _, o := range opts {
|
||||
if err := o(&d.o); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return d.Reset(r)
|
||||
}
|
||||
|
||||
// drainOutput will drain the output until errEndOfStream is sent.
|
||||
func (d *Decoder) drainOutput() {
|
||||
if d.current.cancel != nil {
|
||||
@@ -930,7 +940,7 @@ decodeStream:
|
||||
}
|
||||
|
||||
func (d *Decoder) setDict(frame *frameDec) (err error) {
|
||||
dict, ok := d.dicts[frame.DictionaryID]
|
||||
dict, ok := d.o.dicts[frame.DictionaryID]
|
||||
if ok {
|
||||
if debugDecoder {
|
||||
println("setting dict", frame.DictionaryID)
|
||||
|
||||
+52
-8
@@ -20,10 +20,11 @@ type decoderOptions struct {
|
||||
concurrent int
|
||||
maxDecodedSize uint64
|
||||
maxWindowSize uint64
|
||||
dicts []*dict
|
||||
dicts map[uint32]*dict
|
||||
ignoreChecksum bool
|
||||
limitToCap bool
|
||||
decodeBufsBelow int
|
||||
resetOpt bool
|
||||
}
|
||||
|
||||
func (o *decoderOptions) setDefault() {
|
||||
@@ -42,8 +43,15 @@ func (o *decoderOptions) setDefault() {
|
||||
|
||||
// WithDecoderLowmem will set whether to use a lower amount of memory,
|
||||
// but possibly have to allocate more while running.
|
||||
// Cannot be changed with ResetWithOptions.
|
||||
func WithDecoderLowmem(b bool) DOption {
|
||||
return func(o *decoderOptions) error { o.lowMem = b; return nil }
|
||||
return func(o *decoderOptions) error {
|
||||
if o.resetOpt && b != o.lowMem {
|
||||
return errors.New("WithDecoderLowmem cannot be changed on Reset")
|
||||
}
|
||||
o.lowMem = b
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithDecoderConcurrency sets the number of created decoders.
|
||||
@@ -53,18 +61,23 @@ func WithDecoderLowmem(b bool) DOption {
|
||||
// inflight blocks.
|
||||
// When decoding streams and setting maximum to 1,
|
||||
// no async decoding will be done.
|
||||
// The value supplied must be at least 0.
|
||||
// When a value of 0 is provided GOMAXPROCS will be used.
|
||||
// By default this will be set to 4 or GOMAXPROCS, whatever is lower.
|
||||
// Cannot be changed with ResetWithOptions.
|
||||
func WithDecoderConcurrency(n int) DOption {
|
||||
return func(o *decoderOptions) error {
|
||||
if n < 0 {
|
||||
return errors.New("concurrency must be at least 1")
|
||||
return errors.New("concurrency must be at least 0")
|
||||
}
|
||||
newVal := n
|
||||
if n == 0 {
|
||||
o.concurrent = runtime.GOMAXPROCS(0)
|
||||
} else {
|
||||
o.concurrent = n
|
||||
newVal = runtime.GOMAXPROCS(0)
|
||||
}
|
||||
if o.resetOpt && newVal != o.concurrent {
|
||||
return errors.New("WithDecoderConcurrency cannot be changed on Reset")
|
||||
}
|
||||
o.concurrent = newVal
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -73,6 +86,7 @@ func WithDecoderConcurrency(n int) DOption {
|
||||
// non-streaming operations or maximum window size for streaming operations.
|
||||
// This can be used to control memory usage of potentially hostile content.
|
||||
// Maximum is 1 << 63 bytes. Default is 64GiB.
|
||||
// Can be changed with ResetWithOptions.
|
||||
func WithDecoderMaxMemory(n uint64) DOption {
|
||||
return func(o *decoderOptions) error {
|
||||
if n == 0 {
|
||||
@@ -92,16 +106,20 @@ func WithDecoderMaxMemory(n uint64) DOption {
|
||||
// "zstd --train" from the Zstandard reference implementation.
|
||||
//
|
||||
// If several dictionaries with the same ID are provided, the last one will be used.
|
||||
// Can be changed with ResetWithOptions.
|
||||
//
|
||||
// [dictionary format]: https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#dictionary-format
|
||||
func WithDecoderDicts(dicts ...[]byte) DOption {
|
||||
return func(o *decoderOptions) error {
|
||||
if o.dicts == nil {
|
||||
o.dicts = make(map[uint32]*dict)
|
||||
}
|
||||
for _, b := range dicts {
|
||||
d, err := loadDict(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.dicts = append(o.dicts, d)
|
||||
o.dicts[d.id] = d
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -109,12 +127,16 @@ func WithDecoderDicts(dicts ...[]byte) DOption {
|
||||
|
||||
// WithDecoderDictRaw registers a dictionary that may be used by the decoder.
|
||||
// The slice content can be arbitrary data.
|
||||
// Can be changed with ResetWithOptions.
|
||||
func WithDecoderDictRaw(id uint32, content []byte) DOption {
|
||||
return func(o *decoderOptions) error {
|
||||
if bits.UintSize > 32 && uint(len(content)) > dictMaxLength {
|
||||
return fmt.Errorf("dictionary of size %d > 2GiB too large", len(content))
|
||||
}
|
||||
o.dicts = append(o.dicts, &dict{id: id, content: content, offsets: [3]int{1, 4, 8}})
|
||||
if o.dicts == nil {
|
||||
o.dicts = make(map[uint32]*dict)
|
||||
}
|
||||
o.dicts[id] = &dict{id: id, content: content, offsets: [3]int{1, 4, 8}}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -124,6 +146,7 @@ func WithDecoderDictRaw(id uint32, content []byte) DOption {
|
||||
// The Decoder will likely allocate more memory based on the WithDecoderLowmem setting.
|
||||
// If WithDecoderMaxMemory is set to a lower value, that will be used.
|
||||
// Default is 512MB, Maximum is ~3.75 TB as per zstandard spec.
|
||||
// Can be changed with ResetWithOptions.
|
||||
func WithDecoderMaxWindow(size uint64) DOption {
|
||||
return func(o *decoderOptions) error {
|
||||
if size < MinWindowSize {
|
||||
@@ -141,6 +164,7 @@ func WithDecoderMaxWindow(size uint64) DOption {
|
||||
// or any size set in WithDecoderMaxMemory.
|
||||
// This can be used to limit decoding to a specific maximum output size.
|
||||
// Disabled by default.
|
||||
// Can be changed with ResetWithOptions.
|
||||
func WithDecodeAllCapLimit(b bool) DOption {
|
||||
return func(o *decoderOptions) error {
|
||||
o.limitToCap = b
|
||||
@@ -153,17 +177,37 @@ func WithDecodeAllCapLimit(b bool) DOption {
|
||||
// This typically uses less allocations but will have the full decompressed object in memory.
|
||||
// Note that DecodeAllCapLimit will disable this, as well as giving a size of 0 or less.
|
||||
// Default is 128KiB.
|
||||
// Cannot be changed with ResetWithOptions.
|
||||
func WithDecodeBuffersBelow(size int) DOption {
|
||||
return func(o *decoderOptions) error {
|
||||
if o.resetOpt && size != o.decodeBufsBelow {
|
||||
return errors.New("WithDecodeBuffersBelow cannot be changed on Reset")
|
||||
}
|
||||
o.decodeBufsBelow = size
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// IgnoreChecksum allows to forcibly ignore checksum checking.
|
||||
// Can be changed with ResetWithOptions.
|
||||
func IgnoreChecksum(b bool) DOption {
|
||||
return func(o *decoderOptions) error {
|
||||
o.ignoreChecksum = b
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithDecoderDictDelete removes dictionaries by ID.
|
||||
// If no ids are passed, all dictionaries are deleted.
|
||||
// Should be used with ResetWithOptions.
|
||||
func WithDecoderDictDelete(ids ...uint32) DOption {
|
||||
return func(o *decoderOptions) error {
|
||||
if len(ids) == 0 {
|
||||
clear(o.dicts)
|
||||
}
|
||||
for _, id := range ids {
|
||||
delete(o.dicts, id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
+16
@@ -131,6 +131,22 @@ func (e *Encoder) Reset(w io.Writer) {
|
||||
s.frameContentSize = 0
|
||||
}
|
||||
|
||||
// ResetWithOptions will re-initialize the writer and apply the given options
|
||||
// as a new, independent stream.
|
||||
// Options are applied on top of the existing options.
|
||||
// Some options cannot be changed on reset and will return an error.
|
||||
func (e *Encoder) ResetWithOptions(w io.Writer, opts ...EOption) error {
|
||||
e.o.resetOpt = true
|
||||
defer func() { e.o.resetOpt = false }()
|
||||
for _, o := range opts {
|
||||
if err := o(&e.o); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
e.Reset(w)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ResetContentSize will reset and set a content size for the next stream.
|
||||
// If the bytes written does not match the size given an error will be returned
|
||||
// when calling Close().
|
||||
|
||||
+41
-3
@@ -14,6 +14,7 @@ type EOption func(*encoderOptions) error
|
||||
|
||||
// options retains accumulated state of multiple options.
|
||||
type encoderOptions struct {
|
||||
resetOpt bool
|
||||
concurrent int
|
||||
level EncoderLevel
|
||||
single *bool
|
||||
@@ -71,19 +72,28 @@ func (o encoderOptions) encoder() encoder {
|
||||
|
||||
// WithEncoderCRC will add CRC value to output.
|
||||
// Output will be 4 bytes larger.
|
||||
// Can be changed with ResetWithOptions.
|
||||
func WithEncoderCRC(b bool) EOption {
|
||||
return func(o *encoderOptions) error { o.crc = b; return nil }
|
||||
}
|
||||
|
||||
// WithEncoderConcurrency will set the concurrency,
|
||||
// meaning the maximum number of encoders to run concurrently.
|
||||
// The value supplied must be at least 1.
|
||||
// The value supplied must be at least 0.
|
||||
// When a value of 0 is provided GOMAXPROCS will be used.
|
||||
// For streams, setting a value of 1 will disable async compression.
|
||||
// By default this will be set to GOMAXPROCS.
|
||||
// Cannot be changed with ResetWithOptions.
|
||||
func WithEncoderConcurrency(n int) EOption {
|
||||
return func(o *encoderOptions) error {
|
||||
if n <= 0 {
|
||||
return fmt.Errorf("concurrency must be at least 1")
|
||||
if n < 0 {
|
||||
return errors.New("concurrency must at least 0")
|
||||
}
|
||||
if n == 0 {
|
||||
n = runtime.GOMAXPROCS(0)
|
||||
}
|
||||
if o.resetOpt && n != o.concurrent {
|
||||
return errors.New("WithEncoderConcurrency cannot be changed on Reset")
|
||||
}
|
||||
o.concurrent = n
|
||||
return nil
|
||||
@@ -95,6 +105,7 @@ func WithEncoderConcurrency(n int) EOption {
|
||||
// A larger value will enable better compression but allocate more memory and,
|
||||
// for above-default values, take considerably longer.
|
||||
// The default value is determined by the compression level and max 8MB.
|
||||
// Cannot be changed with ResetWithOptions.
|
||||
func WithWindowSize(n int) EOption {
|
||||
return func(o *encoderOptions) error {
|
||||
switch {
|
||||
@@ -105,6 +116,9 @@ func WithWindowSize(n int) EOption {
|
||||
case (n & (n - 1)) != 0:
|
||||
return errors.New("window size must be a power of 2")
|
||||
}
|
||||
if o.resetOpt && n != o.windowSize {
|
||||
return errors.New("WithWindowSize cannot be changed on Reset")
|
||||
}
|
||||
|
||||
o.windowSize = n
|
||||
o.customWindow = true
|
||||
@@ -122,6 +136,7 @@ func WithWindowSize(n int) EOption {
|
||||
// n must be > 0 and <= 1GB, 1<<30 bytes.
|
||||
// The padded area will be filled with data from crypto/rand.Reader.
|
||||
// If `EncodeAll` is used with data already in the destination, the total size will be multiple of this.
|
||||
// Can be changed with ResetWithOptions.
|
||||
func WithEncoderPadding(n int) EOption {
|
||||
return func(o *encoderOptions) error {
|
||||
if n <= 0 {
|
||||
@@ -215,12 +230,16 @@ func (e EncoderLevel) String() string {
|
||||
}
|
||||
|
||||
// WithEncoderLevel specifies a predefined compression level.
|
||||
// Cannot be changed with ResetWithOptions.
|
||||
func WithEncoderLevel(l EncoderLevel) EOption {
|
||||
return func(o *encoderOptions) error {
|
||||
switch {
|
||||
case l <= speedNotSet || l >= speedLast:
|
||||
return fmt.Errorf("unknown encoder level")
|
||||
}
|
||||
if o.resetOpt && l != o.level {
|
||||
return errors.New("WithEncoderLevel cannot be changed on Reset")
|
||||
}
|
||||
o.level = l
|
||||
if !o.customWindow {
|
||||
switch o.level {
|
||||
@@ -248,6 +267,7 @@ func WithEncoderLevel(l EncoderLevel) EOption {
|
||||
// WithZeroFrames will encode 0 length input as full frames.
|
||||
// This can be needed for compatibility with zstandard usage,
|
||||
// but is not needed for this package.
|
||||
// Can be changed with ResetWithOptions.
|
||||
func WithZeroFrames(b bool) EOption {
|
||||
return func(o *encoderOptions) error {
|
||||
o.fullZero = b
|
||||
@@ -259,6 +279,7 @@ func WithZeroFrames(b bool) EOption {
|
||||
// Disabling this will skip incompressible data faster, but in cases with no matches but
|
||||
// skewed character distribution compression is lost.
|
||||
// Default value depends on the compression level selected.
|
||||
// Can be changed with ResetWithOptions.
|
||||
func WithAllLitEntropyCompression(b bool) EOption {
|
||||
return func(o *encoderOptions) error {
|
||||
o.customALEntropy = true
|
||||
@@ -270,6 +291,7 @@ func WithAllLitEntropyCompression(b bool) EOption {
|
||||
// WithNoEntropyCompression will always skip entropy compression of literals.
|
||||
// This can be useful if content has matches, but unlikely to benefit from entropy
|
||||
// compression. Usually the slight speed improvement is not worth enabling this.
|
||||
// Can be changed with ResetWithOptions.
|
||||
func WithNoEntropyCompression(b bool) EOption {
|
||||
return func(o *encoderOptions) error {
|
||||
o.noEntropy = b
|
||||
@@ -287,6 +309,7 @@ func WithNoEntropyCompression(b bool) EOption {
|
||||
// This is only a recommendation, each decoder is free to support higher or lower limits, depending on local limitations.
|
||||
// If this is not specified, block encodes will automatically choose this based on the input size and the window size.
|
||||
// This setting has no effect on streamed encodes.
|
||||
// Can be changed with ResetWithOptions.
|
||||
func WithSingleSegment(b bool) EOption {
|
||||
return func(o *encoderOptions) error {
|
||||
o.single = &b
|
||||
@@ -298,8 +321,12 @@ func WithSingleSegment(b bool) EOption {
|
||||
// slower encoding speed.
|
||||
// This will not change the window size which is the primary function for reducing
|
||||
// memory usage. See WithWindowSize.
|
||||
// Cannot be changed with ResetWithOptions.
|
||||
func WithLowerEncoderMem(b bool) EOption {
|
||||
return func(o *encoderOptions) error {
|
||||
if o.resetOpt && b != o.lowMem {
|
||||
return errors.New("WithLowerEncoderMem cannot be changed on Reset")
|
||||
}
|
||||
o.lowMem = b
|
||||
return nil
|
||||
}
|
||||
@@ -311,6 +338,7 @@ func WithLowerEncoderMem(b bool) EOption {
|
||||
// "zstd --train" from the Zstandard reference implementation.
|
||||
//
|
||||
// The encoder *may* choose to use no dictionary instead for certain payloads.
|
||||
// Can be changed with ResetWithOptions.
|
||||
//
|
||||
// [dictionary format]: https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#dictionary-format
|
||||
func WithEncoderDict(dict []byte) EOption {
|
||||
@@ -328,6 +356,7 @@ func WithEncoderDict(dict []byte) EOption {
|
||||
//
|
||||
// The slice content may contain arbitrary data. It will be used as an initial
|
||||
// history.
|
||||
// Can be changed with ResetWithOptions.
|
||||
func WithEncoderDictRaw(id uint32, content []byte) EOption {
|
||||
return func(o *encoderOptions) error {
|
||||
if bits.UintSize > 32 && uint(len(content)) > dictMaxLength {
|
||||
@@ -337,3 +366,12 @@ func WithEncoderDictRaw(id uint32, content []byte) EOption {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithEncoderDictDelete clears the dictionary, so no dictionary will be used.
|
||||
// Should be used with ResetWithOptions.
|
||||
func WithEncoderDictDelete() EOption {
|
||||
return func(o *encoderOptions) error {
|
||||
o.dict = nil
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
+318
-281
@@ -6,46 +6,47 @@ var combining = table{
|
||||
{0x0300, 0x036F}, {0x0483, 0x0489}, {0x07EB, 0x07F3},
|
||||
{0x0C00, 0x0C00}, {0x0C04, 0x0C04}, {0x0CF3, 0x0CF3},
|
||||
{0x0D00, 0x0D01}, {0x135D, 0x135F}, {0x1A7F, 0x1A7F},
|
||||
{0x1AB0, 0x1ACE}, {0x1B6B, 0x1B73}, {0x1DC0, 0x1DFF},
|
||||
{0x20D0, 0x20F0}, {0x2CEF, 0x2CF1}, {0x2DE0, 0x2DFF},
|
||||
{0x3099, 0x309A}, {0xA66F, 0xA672}, {0xA674, 0xA67D},
|
||||
{0xA69E, 0xA69F}, {0xA6F0, 0xA6F1}, {0xA8E0, 0xA8F1},
|
||||
{0xFE20, 0xFE2F}, {0x101FD, 0x101FD}, {0x10376, 0x1037A},
|
||||
{0x10EAB, 0x10EAC}, {0x10F46, 0x10F50}, {0x10F82, 0x10F85},
|
||||
{0x11300, 0x11301}, {0x1133B, 0x1133C}, {0x11366, 0x1136C},
|
||||
{0x11370, 0x11374}, {0x16AF0, 0x16AF4}, {0x1CF00, 0x1CF2D},
|
||||
{0x1CF30, 0x1CF46}, {0x1D165, 0x1D169}, {0x1D16D, 0x1D172},
|
||||
{0x1D17B, 0x1D182}, {0x1D185, 0x1D18B}, {0x1D1AA, 0x1D1AD},
|
||||
{0x1D242, 0x1D244}, {0x1E000, 0x1E006}, {0x1E008, 0x1E018},
|
||||
{0x1E01B, 0x1E021}, {0x1E023, 0x1E024}, {0x1E026, 0x1E02A},
|
||||
{0x1E08F, 0x1E08F}, {0x1E8D0, 0x1E8D6},
|
||||
{0x1AB0, 0x1ADD}, {0x1AE0, 0x1AEB}, {0x1B6B, 0x1B73},
|
||||
{0x1DC0, 0x1DFF}, {0x20D0, 0x20F0}, {0x2CEF, 0x2CF1},
|
||||
{0x2DE0, 0x2DFF}, {0x3099, 0x309A}, {0xA66F, 0xA672},
|
||||
{0xA674, 0xA67D}, {0xA69E, 0xA69F}, {0xA6F0, 0xA6F1},
|
||||
{0xA8E0, 0xA8F1}, {0xFE20, 0xFE2F}, {0x101FD, 0x101FD},
|
||||
{0x10376, 0x1037A}, {0x10EAB, 0x10EAC}, {0x10F46, 0x10F50},
|
||||
{0x10F82, 0x10F85}, {0x11300, 0x11301}, {0x1133B, 0x1133C},
|
||||
{0x11366, 0x1136C}, {0x11370, 0x11374}, {0x16AF0, 0x16AF4},
|
||||
{0x1CF00, 0x1CF2D}, {0x1CF30, 0x1CF46}, {0x1D165, 0x1D169},
|
||||
{0x1D16D, 0x1D172}, {0x1D17B, 0x1D182}, {0x1D185, 0x1D18B},
|
||||
{0x1D1AA, 0x1D1AD}, {0x1D242, 0x1D244}, {0x1E000, 0x1E006},
|
||||
{0x1E008, 0x1E018}, {0x1E01B, 0x1E021}, {0x1E023, 0x1E024},
|
||||
{0x1E026, 0x1E02A}, {0x1E08F, 0x1E08F}, {0x1E8D0, 0x1E8D6},
|
||||
}
|
||||
|
||||
var doublewidth = table{
|
||||
{0x1100, 0x115F}, {0x231A, 0x231B}, {0x2329, 0x232A},
|
||||
{0x23E9, 0x23EC}, {0x23F0, 0x23F0}, {0x23F3, 0x23F3},
|
||||
{0x25FD, 0x25FE}, {0x2614, 0x2615}, {0x2648, 0x2653},
|
||||
{0x267F, 0x267F}, {0x2693, 0x2693}, {0x26A1, 0x26A1},
|
||||
{0x26AA, 0x26AB}, {0x26BD, 0x26BE}, {0x26C4, 0x26C5},
|
||||
{0x26CE, 0x26CE}, {0x26D4, 0x26D4}, {0x26EA, 0x26EA},
|
||||
{0x26F2, 0x26F3}, {0x26F5, 0x26F5}, {0x26FA, 0x26FA},
|
||||
{0x26FD, 0x26FD}, {0x2705, 0x2705}, {0x270A, 0x270B},
|
||||
{0x2728, 0x2728}, {0x274C, 0x274C}, {0x274E, 0x274E},
|
||||
{0x2753, 0x2755}, {0x2757, 0x2757}, {0x2795, 0x2797},
|
||||
{0x27B0, 0x27B0}, {0x27BF, 0x27BF}, {0x2B1B, 0x2B1C},
|
||||
{0x2B50, 0x2B50}, {0x2B55, 0x2B55}, {0x2E80, 0x2E99},
|
||||
{0x2E9B, 0x2EF3}, {0x2F00, 0x2FD5}, {0x2FF0, 0x303E},
|
||||
{0x3041, 0x3096}, {0x3099, 0x30FF}, {0x3105, 0x312F},
|
||||
{0x3131, 0x318E}, {0x3190, 0x31E3}, {0x31EF, 0x321E},
|
||||
{0x3220, 0x3247}, {0x3250, 0x4DBF}, {0x4E00, 0xA48C},
|
||||
{0xA490, 0xA4C6}, {0xA960, 0xA97C}, {0xAC00, 0xD7A3},
|
||||
{0xF900, 0xFAFF}, {0xFE10, 0xFE19}, {0xFE30, 0xFE52},
|
||||
{0xFE54, 0xFE66}, {0xFE68, 0xFE6B}, {0xFF01, 0xFF60},
|
||||
{0xFFE0, 0xFFE6}, {0x16FE0, 0x16FE4}, {0x16FF0, 0x16FF1},
|
||||
{0x17000, 0x187F7}, {0x18800, 0x18CD5}, {0x18D00, 0x18D08},
|
||||
{0x1AFF0, 0x1AFF3}, {0x1AFF5, 0x1AFFB}, {0x1AFFD, 0x1AFFE},
|
||||
{0x1B000, 0x1B122}, {0x1B132, 0x1B132}, {0x1B150, 0x1B152},
|
||||
{0x1B155, 0x1B155}, {0x1B164, 0x1B167}, {0x1B170, 0x1B2FB},
|
||||
{0x25FD, 0x25FE}, {0x2614, 0x2615}, {0x2630, 0x2637},
|
||||
{0x2648, 0x2653}, {0x267F, 0x267F}, {0x268A, 0x268F},
|
||||
{0x2693, 0x2693}, {0x26A1, 0x26A1}, {0x26AA, 0x26AB},
|
||||
{0x26BD, 0x26BE}, {0x26C4, 0x26C5}, {0x26CE, 0x26CE},
|
||||
{0x26D4, 0x26D4}, {0x26EA, 0x26EA}, {0x26F2, 0x26F3},
|
||||
{0x26F5, 0x26F5}, {0x26FA, 0x26FA}, {0x26FD, 0x26FD},
|
||||
{0x2705, 0x2705}, {0x270A, 0x270B}, {0x2728, 0x2728},
|
||||
{0x274C, 0x274C}, {0x274E, 0x274E}, {0x2753, 0x2755},
|
||||
{0x2757, 0x2757}, {0x2795, 0x2797}, {0x27B0, 0x27B0},
|
||||
{0x27BF, 0x27BF}, {0x2B1B, 0x2B1C}, {0x2B50, 0x2B50},
|
||||
{0x2B55, 0x2B55}, {0x2E80, 0x2E99}, {0x2E9B, 0x2EF3},
|
||||
{0x2F00, 0x2FD5}, {0x2FF0, 0x303E}, {0x3041, 0x3096},
|
||||
{0x3099, 0x30FF}, {0x3105, 0x312F}, {0x3131, 0x318E},
|
||||
{0x3190, 0x31E5}, {0x31EF, 0x321E}, {0x3220, 0x3247},
|
||||
{0x3250, 0xA48C}, {0xA490, 0xA4C6}, {0xA960, 0xA97C},
|
||||
{0xAC00, 0xD7A3}, {0xF900, 0xFAFF}, {0xFE10, 0xFE19},
|
||||
{0xFE30, 0xFE52}, {0xFE54, 0xFE66}, {0xFE68, 0xFE6B},
|
||||
{0xFF01, 0xFF60}, {0xFFE0, 0xFFE6}, {0x16FE0, 0x16FE4},
|
||||
{0x16FF0, 0x16FF6}, {0x17000, 0x18CD5}, {0x18CFF, 0x18D1E},
|
||||
{0x18D80, 0x18DF2}, {0x1AFF0, 0x1AFF3}, {0x1AFF5, 0x1AFFB},
|
||||
{0x1AFFD, 0x1AFFE}, {0x1B000, 0x1B122}, {0x1B132, 0x1B132},
|
||||
{0x1B150, 0x1B152}, {0x1B155, 0x1B155}, {0x1B164, 0x1B167},
|
||||
{0x1B170, 0x1B2FB}, {0x1D300, 0x1D356}, {0x1D360, 0x1D376},
|
||||
{0x1F004, 0x1F004}, {0x1F0CF, 0x1F0CF}, {0x1F18E, 0x1F18E},
|
||||
{0x1F191, 0x1F19A}, {0x1F200, 0x1F202}, {0x1F210, 0x1F23B},
|
||||
{0x1F240, 0x1F248}, {0x1F250, 0x1F251}, {0x1F260, 0x1F265},
|
||||
@@ -56,12 +57,12 @@ var doublewidth = table{
|
||||
{0x1F54B, 0x1F54E}, {0x1F550, 0x1F567}, {0x1F57A, 0x1F57A},
|
||||
{0x1F595, 0x1F596}, {0x1F5A4, 0x1F5A4}, {0x1F5FB, 0x1F64F},
|
||||
{0x1F680, 0x1F6C5}, {0x1F6CC, 0x1F6CC}, {0x1F6D0, 0x1F6D2},
|
||||
{0x1F6D5, 0x1F6D7}, {0x1F6DC, 0x1F6DF}, {0x1F6EB, 0x1F6EC},
|
||||
{0x1F6D5, 0x1F6D8}, {0x1F6DC, 0x1F6DF}, {0x1F6EB, 0x1F6EC},
|
||||
{0x1F6F4, 0x1F6FC}, {0x1F7E0, 0x1F7EB}, {0x1F7F0, 0x1F7F0},
|
||||
{0x1F90C, 0x1F93A}, {0x1F93C, 0x1F945}, {0x1F947, 0x1F9FF},
|
||||
{0x1FA70, 0x1FA7C}, {0x1FA80, 0x1FA88}, {0x1FA90, 0x1FABD},
|
||||
{0x1FABF, 0x1FAC5}, {0x1FACE, 0x1FADB}, {0x1FAE0, 0x1FAE8},
|
||||
{0x1FAF0, 0x1FAF8}, {0x20000, 0x2FFFD}, {0x30000, 0x3FFFD},
|
||||
{0x1FA70, 0x1FA7C}, {0x1FA80, 0x1FA8A}, {0x1FA8E, 0x1FAC6},
|
||||
{0x1FAC8, 0x1FAC8}, {0x1FACD, 0x1FADC}, {0x1FADF, 0x1FAEA},
|
||||
{0x1FAEF, 0x1FAF8}, {0x20000, 0x2FFFD}, {0x30000, 0x3FFFD},
|
||||
}
|
||||
|
||||
var ambiguous = table{
|
||||
@@ -159,164 +160,162 @@ var neutral = table{
|
||||
{0x0600, 0x070D}, {0x070F, 0x074A}, {0x074D, 0x07B1},
|
||||
{0x07C0, 0x07FA}, {0x07FD, 0x082D}, {0x0830, 0x083E},
|
||||
{0x0840, 0x085B}, {0x085E, 0x085E}, {0x0860, 0x086A},
|
||||
{0x0870, 0x088E}, {0x0890, 0x0891}, {0x0898, 0x0983},
|
||||
{0x0985, 0x098C}, {0x098F, 0x0990}, {0x0993, 0x09A8},
|
||||
{0x09AA, 0x09B0}, {0x09B2, 0x09B2}, {0x09B6, 0x09B9},
|
||||
{0x09BC, 0x09C4}, {0x09C7, 0x09C8}, {0x09CB, 0x09CE},
|
||||
{0x09D7, 0x09D7}, {0x09DC, 0x09DD}, {0x09DF, 0x09E3},
|
||||
{0x09E6, 0x09FE}, {0x0A01, 0x0A03}, {0x0A05, 0x0A0A},
|
||||
{0x0A0F, 0x0A10}, {0x0A13, 0x0A28}, {0x0A2A, 0x0A30},
|
||||
{0x0A32, 0x0A33}, {0x0A35, 0x0A36}, {0x0A38, 0x0A39},
|
||||
{0x0A3C, 0x0A3C}, {0x0A3E, 0x0A42}, {0x0A47, 0x0A48},
|
||||
{0x0A4B, 0x0A4D}, {0x0A51, 0x0A51}, {0x0A59, 0x0A5C},
|
||||
{0x0A5E, 0x0A5E}, {0x0A66, 0x0A76}, {0x0A81, 0x0A83},
|
||||
{0x0A85, 0x0A8D}, {0x0A8F, 0x0A91}, {0x0A93, 0x0AA8},
|
||||
{0x0AAA, 0x0AB0}, {0x0AB2, 0x0AB3}, {0x0AB5, 0x0AB9},
|
||||
{0x0ABC, 0x0AC5}, {0x0AC7, 0x0AC9}, {0x0ACB, 0x0ACD},
|
||||
{0x0AD0, 0x0AD0}, {0x0AE0, 0x0AE3}, {0x0AE6, 0x0AF1},
|
||||
{0x0AF9, 0x0AFF}, {0x0B01, 0x0B03}, {0x0B05, 0x0B0C},
|
||||
{0x0B0F, 0x0B10}, {0x0B13, 0x0B28}, {0x0B2A, 0x0B30},
|
||||
{0x0B32, 0x0B33}, {0x0B35, 0x0B39}, {0x0B3C, 0x0B44},
|
||||
{0x0B47, 0x0B48}, {0x0B4B, 0x0B4D}, {0x0B55, 0x0B57},
|
||||
{0x0B5C, 0x0B5D}, {0x0B5F, 0x0B63}, {0x0B66, 0x0B77},
|
||||
{0x0B82, 0x0B83}, {0x0B85, 0x0B8A}, {0x0B8E, 0x0B90},
|
||||
{0x0B92, 0x0B95}, {0x0B99, 0x0B9A}, {0x0B9C, 0x0B9C},
|
||||
{0x0B9E, 0x0B9F}, {0x0BA3, 0x0BA4}, {0x0BA8, 0x0BAA},
|
||||
{0x0BAE, 0x0BB9}, {0x0BBE, 0x0BC2}, {0x0BC6, 0x0BC8},
|
||||
{0x0BCA, 0x0BCD}, {0x0BD0, 0x0BD0}, {0x0BD7, 0x0BD7},
|
||||
{0x0BE6, 0x0BFA}, {0x0C00, 0x0C0C}, {0x0C0E, 0x0C10},
|
||||
{0x0C12, 0x0C28}, {0x0C2A, 0x0C39}, {0x0C3C, 0x0C44},
|
||||
{0x0C46, 0x0C48}, {0x0C4A, 0x0C4D}, {0x0C55, 0x0C56},
|
||||
{0x0C58, 0x0C5A}, {0x0C5D, 0x0C5D}, {0x0C60, 0x0C63},
|
||||
{0x0C66, 0x0C6F}, {0x0C77, 0x0C8C}, {0x0C8E, 0x0C90},
|
||||
{0x0C92, 0x0CA8}, {0x0CAA, 0x0CB3}, {0x0CB5, 0x0CB9},
|
||||
{0x0CBC, 0x0CC4}, {0x0CC6, 0x0CC8}, {0x0CCA, 0x0CCD},
|
||||
{0x0CD5, 0x0CD6}, {0x0CDD, 0x0CDE}, {0x0CE0, 0x0CE3},
|
||||
{0x0CE6, 0x0CEF}, {0x0CF1, 0x0CF3}, {0x0D00, 0x0D0C},
|
||||
{0x0D0E, 0x0D10}, {0x0D12, 0x0D44}, {0x0D46, 0x0D48},
|
||||
{0x0D4A, 0x0D4F}, {0x0D54, 0x0D63}, {0x0D66, 0x0D7F},
|
||||
{0x0D81, 0x0D83}, {0x0D85, 0x0D96}, {0x0D9A, 0x0DB1},
|
||||
{0x0DB3, 0x0DBB}, {0x0DBD, 0x0DBD}, {0x0DC0, 0x0DC6},
|
||||
{0x0DCA, 0x0DCA}, {0x0DCF, 0x0DD4}, {0x0DD6, 0x0DD6},
|
||||
{0x0DD8, 0x0DDF}, {0x0DE6, 0x0DEF}, {0x0DF2, 0x0DF4},
|
||||
{0x0E01, 0x0E3A}, {0x0E3F, 0x0E5B}, {0x0E81, 0x0E82},
|
||||
{0x0E84, 0x0E84}, {0x0E86, 0x0E8A}, {0x0E8C, 0x0EA3},
|
||||
{0x0EA5, 0x0EA5}, {0x0EA7, 0x0EBD}, {0x0EC0, 0x0EC4},
|
||||
{0x0EC6, 0x0EC6}, {0x0EC8, 0x0ECE}, {0x0ED0, 0x0ED9},
|
||||
{0x0EDC, 0x0EDF}, {0x0F00, 0x0F47}, {0x0F49, 0x0F6C},
|
||||
{0x0F71, 0x0F97}, {0x0F99, 0x0FBC}, {0x0FBE, 0x0FCC},
|
||||
{0x0FCE, 0x0FDA}, {0x1000, 0x10C5}, {0x10C7, 0x10C7},
|
||||
{0x10CD, 0x10CD}, {0x10D0, 0x10FF}, {0x1160, 0x1248},
|
||||
{0x124A, 0x124D}, {0x1250, 0x1256}, {0x1258, 0x1258},
|
||||
{0x125A, 0x125D}, {0x1260, 0x1288}, {0x128A, 0x128D},
|
||||
{0x1290, 0x12B0}, {0x12B2, 0x12B5}, {0x12B8, 0x12BE},
|
||||
{0x12C0, 0x12C0}, {0x12C2, 0x12C5}, {0x12C8, 0x12D6},
|
||||
{0x12D8, 0x1310}, {0x1312, 0x1315}, {0x1318, 0x135A},
|
||||
{0x135D, 0x137C}, {0x1380, 0x1399}, {0x13A0, 0x13F5},
|
||||
{0x13F8, 0x13FD}, {0x1400, 0x169C}, {0x16A0, 0x16F8},
|
||||
{0x1700, 0x1715}, {0x171F, 0x1736}, {0x1740, 0x1753},
|
||||
{0x1760, 0x176C}, {0x176E, 0x1770}, {0x1772, 0x1773},
|
||||
{0x1780, 0x17DD}, {0x17E0, 0x17E9}, {0x17F0, 0x17F9},
|
||||
{0x1800, 0x1819}, {0x1820, 0x1878}, {0x1880, 0x18AA},
|
||||
{0x18B0, 0x18F5}, {0x1900, 0x191E}, {0x1920, 0x192B},
|
||||
{0x1930, 0x193B}, {0x1940, 0x1940}, {0x1944, 0x196D},
|
||||
{0x1970, 0x1974}, {0x1980, 0x19AB}, {0x19B0, 0x19C9},
|
||||
{0x19D0, 0x19DA}, {0x19DE, 0x1A1B}, {0x1A1E, 0x1A5E},
|
||||
{0x1A60, 0x1A7C}, {0x1A7F, 0x1A89}, {0x1A90, 0x1A99},
|
||||
{0x1AA0, 0x1AAD}, {0x1AB0, 0x1ACE}, {0x1B00, 0x1B4C},
|
||||
{0x1B50, 0x1B7E}, {0x1B80, 0x1BF3}, {0x1BFC, 0x1C37},
|
||||
{0x1C3B, 0x1C49}, {0x1C4D, 0x1C88}, {0x1C90, 0x1CBA},
|
||||
{0x1CBD, 0x1CC7}, {0x1CD0, 0x1CFA}, {0x1D00, 0x1F15},
|
||||
{0x1F18, 0x1F1D}, {0x1F20, 0x1F45}, {0x1F48, 0x1F4D},
|
||||
{0x1F50, 0x1F57}, {0x1F59, 0x1F59}, {0x1F5B, 0x1F5B},
|
||||
{0x1F5D, 0x1F5D}, {0x1F5F, 0x1F7D}, {0x1F80, 0x1FB4},
|
||||
{0x1FB6, 0x1FC4}, {0x1FC6, 0x1FD3}, {0x1FD6, 0x1FDB},
|
||||
{0x1FDD, 0x1FEF}, {0x1FF2, 0x1FF4}, {0x1FF6, 0x1FFE},
|
||||
{0x2000, 0x200F}, {0x2011, 0x2012}, {0x2017, 0x2017},
|
||||
{0x201A, 0x201B}, {0x201E, 0x201F}, {0x2023, 0x2023},
|
||||
{0x2028, 0x202F}, {0x2031, 0x2031}, {0x2034, 0x2034},
|
||||
{0x2036, 0x203A}, {0x203C, 0x203D}, {0x203F, 0x2064},
|
||||
{0x2066, 0x2071}, {0x2075, 0x207E}, {0x2080, 0x2080},
|
||||
{0x2085, 0x208E}, {0x2090, 0x209C}, {0x20A0, 0x20A8},
|
||||
{0x20AA, 0x20AB}, {0x20AD, 0x20C0}, {0x20D0, 0x20F0},
|
||||
{0x2100, 0x2102}, {0x2104, 0x2104}, {0x2106, 0x2108},
|
||||
{0x210A, 0x2112}, {0x2114, 0x2115}, {0x2117, 0x2120},
|
||||
{0x2123, 0x2125}, {0x2127, 0x212A}, {0x212C, 0x2152},
|
||||
{0x2155, 0x215A}, {0x215F, 0x215F}, {0x216C, 0x216F},
|
||||
{0x217A, 0x2188}, {0x218A, 0x218B}, {0x219A, 0x21B7},
|
||||
{0x21BA, 0x21D1}, {0x21D3, 0x21D3}, {0x21D5, 0x21E6},
|
||||
{0x21E8, 0x21FF}, {0x2201, 0x2201}, {0x2204, 0x2206},
|
||||
{0x2209, 0x220A}, {0x220C, 0x220E}, {0x2210, 0x2210},
|
||||
{0x2212, 0x2214}, {0x2216, 0x2219}, {0x221B, 0x221C},
|
||||
{0x2221, 0x2222}, {0x2224, 0x2224}, {0x2226, 0x2226},
|
||||
{0x222D, 0x222D}, {0x222F, 0x2233}, {0x2238, 0x223B},
|
||||
{0x223E, 0x2247}, {0x2249, 0x224B}, {0x224D, 0x2251},
|
||||
{0x2253, 0x225F}, {0x2262, 0x2263}, {0x2268, 0x2269},
|
||||
{0x226C, 0x226D}, {0x2270, 0x2281}, {0x2284, 0x2285},
|
||||
{0x2288, 0x2294}, {0x2296, 0x2298}, {0x229A, 0x22A4},
|
||||
{0x22A6, 0x22BE}, {0x22C0, 0x2311}, {0x2313, 0x2319},
|
||||
{0x231C, 0x2328}, {0x232B, 0x23E8}, {0x23ED, 0x23EF},
|
||||
{0x23F1, 0x23F2}, {0x23F4, 0x2426}, {0x2440, 0x244A},
|
||||
{0x24EA, 0x24EA}, {0x254C, 0x254F}, {0x2574, 0x257F},
|
||||
{0x2590, 0x2591}, {0x2596, 0x259F}, {0x25A2, 0x25A2},
|
||||
{0x25AA, 0x25B1}, {0x25B4, 0x25B5}, {0x25B8, 0x25BB},
|
||||
{0x25BE, 0x25BF}, {0x25C2, 0x25C5}, {0x25C9, 0x25CA},
|
||||
{0x25CC, 0x25CD}, {0x25D2, 0x25E1}, {0x25E6, 0x25EE},
|
||||
{0x25F0, 0x25FC}, {0x25FF, 0x2604}, {0x2607, 0x2608},
|
||||
{0x260A, 0x260D}, {0x2610, 0x2613}, {0x2616, 0x261B},
|
||||
{0x261D, 0x261D}, {0x261F, 0x263F}, {0x2641, 0x2641},
|
||||
{0x0870, 0x0891}, {0x0897, 0x0983}, {0x0985, 0x098C},
|
||||
{0x098F, 0x0990}, {0x0993, 0x09A8}, {0x09AA, 0x09B0},
|
||||
{0x09B2, 0x09B2}, {0x09B6, 0x09B9}, {0x09BC, 0x09C4},
|
||||
{0x09C7, 0x09C8}, {0x09CB, 0x09CE}, {0x09D7, 0x09D7},
|
||||
{0x09DC, 0x09DD}, {0x09DF, 0x09E3}, {0x09E6, 0x09FE},
|
||||
{0x0A01, 0x0A03}, {0x0A05, 0x0A0A}, {0x0A0F, 0x0A10},
|
||||
{0x0A13, 0x0A28}, {0x0A2A, 0x0A30}, {0x0A32, 0x0A33},
|
||||
{0x0A35, 0x0A36}, {0x0A38, 0x0A39}, {0x0A3C, 0x0A3C},
|
||||
{0x0A3E, 0x0A42}, {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D},
|
||||
{0x0A51, 0x0A51}, {0x0A59, 0x0A5C}, {0x0A5E, 0x0A5E},
|
||||
{0x0A66, 0x0A76}, {0x0A81, 0x0A83}, {0x0A85, 0x0A8D},
|
||||
{0x0A8F, 0x0A91}, {0x0A93, 0x0AA8}, {0x0AAA, 0x0AB0},
|
||||
{0x0AB2, 0x0AB3}, {0x0AB5, 0x0AB9}, {0x0ABC, 0x0AC5},
|
||||
{0x0AC7, 0x0AC9}, {0x0ACB, 0x0ACD}, {0x0AD0, 0x0AD0},
|
||||
{0x0AE0, 0x0AE3}, {0x0AE6, 0x0AF1}, {0x0AF9, 0x0AFF},
|
||||
{0x0B01, 0x0B03}, {0x0B05, 0x0B0C}, {0x0B0F, 0x0B10},
|
||||
{0x0B13, 0x0B28}, {0x0B2A, 0x0B30}, {0x0B32, 0x0B33},
|
||||
{0x0B35, 0x0B39}, {0x0B3C, 0x0B44}, {0x0B47, 0x0B48},
|
||||
{0x0B4B, 0x0B4D}, {0x0B55, 0x0B57}, {0x0B5C, 0x0B5D},
|
||||
{0x0B5F, 0x0B63}, {0x0B66, 0x0B77}, {0x0B82, 0x0B83},
|
||||
{0x0B85, 0x0B8A}, {0x0B8E, 0x0B90}, {0x0B92, 0x0B95},
|
||||
{0x0B99, 0x0B9A}, {0x0B9C, 0x0B9C}, {0x0B9E, 0x0B9F},
|
||||
{0x0BA3, 0x0BA4}, {0x0BA8, 0x0BAA}, {0x0BAE, 0x0BB9},
|
||||
{0x0BBE, 0x0BC2}, {0x0BC6, 0x0BC8}, {0x0BCA, 0x0BCD},
|
||||
{0x0BD0, 0x0BD0}, {0x0BD7, 0x0BD7}, {0x0BE6, 0x0BFA},
|
||||
{0x0C00, 0x0C0C}, {0x0C0E, 0x0C10}, {0x0C12, 0x0C28},
|
||||
{0x0C2A, 0x0C39}, {0x0C3C, 0x0C44}, {0x0C46, 0x0C48},
|
||||
{0x0C4A, 0x0C4D}, {0x0C55, 0x0C56}, {0x0C58, 0x0C5A},
|
||||
{0x0C5C, 0x0C5D}, {0x0C60, 0x0C63}, {0x0C66, 0x0C6F},
|
||||
{0x0C77, 0x0C8C}, {0x0C8E, 0x0C90}, {0x0C92, 0x0CA8},
|
||||
{0x0CAA, 0x0CB3}, {0x0CB5, 0x0CB9}, {0x0CBC, 0x0CC4},
|
||||
{0x0CC6, 0x0CC8}, {0x0CCA, 0x0CCD}, {0x0CD5, 0x0CD6},
|
||||
{0x0CDC, 0x0CDE}, {0x0CE0, 0x0CE3}, {0x0CE6, 0x0CEF},
|
||||
{0x0CF1, 0x0CF3}, {0x0D00, 0x0D0C}, {0x0D0E, 0x0D10},
|
||||
{0x0D12, 0x0D44}, {0x0D46, 0x0D48}, {0x0D4A, 0x0D4F},
|
||||
{0x0D54, 0x0D63}, {0x0D66, 0x0D7F}, {0x0D81, 0x0D83},
|
||||
{0x0D85, 0x0D96}, {0x0D9A, 0x0DB1}, {0x0DB3, 0x0DBB},
|
||||
{0x0DBD, 0x0DBD}, {0x0DC0, 0x0DC6}, {0x0DCA, 0x0DCA},
|
||||
{0x0DCF, 0x0DD4}, {0x0DD6, 0x0DD6}, {0x0DD8, 0x0DDF},
|
||||
{0x0DE6, 0x0DEF}, {0x0DF2, 0x0DF4}, {0x0E01, 0x0E3A},
|
||||
{0x0E3F, 0x0E5B}, {0x0E81, 0x0E82}, {0x0E84, 0x0E84},
|
||||
{0x0E86, 0x0E8A}, {0x0E8C, 0x0EA3}, {0x0EA5, 0x0EA5},
|
||||
{0x0EA7, 0x0EBD}, {0x0EC0, 0x0EC4}, {0x0EC6, 0x0EC6},
|
||||
{0x0EC8, 0x0ECE}, {0x0ED0, 0x0ED9}, {0x0EDC, 0x0EDF},
|
||||
{0x0F00, 0x0F47}, {0x0F49, 0x0F6C}, {0x0F71, 0x0F97},
|
||||
{0x0F99, 0x0FBC}, {0x0FBE, 0x0FCC}, {0x0FCE, 0x0FDA},
|
||||
{0x1000, 0x10C5}, {0x10C7, 0x10C7}, {0x10CD, 0x10CD},
|
||||
{0x10D0, 0x10FF}, {0x1160, 0x1248}, {0x124A, 0x124D},
|
||||
{0x1250, 0x1256}, {0x1258, 0x1258}, {0x125A, 0x125D},
|
||||
{0x1260, 0x1288}, {0x128A, 0x128D}, {0x1290, 0x12B0},
|
||||
{0x12B2, 0x12B5}, {0x12B8, 0x12BE}, {0x12C0, 0x12C0},
|
||||
{0x12C2, 0x12C5}, {0x12C8, 0x12D6}, {0x12D8, 0x1310},
|
||||
{0x1312, 0x1315}, {0x1318, 0x135A}, {0x135D, 0x137C},
|
||||
{0x1380, 0x1399}, {0x13A0, 0x13F5}, {0x13F8, 0x13FD},
|
||||
{0x1400, 0x169C}, {0x16A0, 0x16F8}, {0x1700, 0x1715},
|
||||
{0x171F, 0x1736}, {0x1740, 0x1753}, {0x1760, 0x176C},
|
||||
{0x176E, 0x1770}, {0x1772, 0x1773}, {0x1780, 0x17DD},
|
||||
{0x17E0, 0x17E9}, {0x17F0, 0x17F9}, {0x1800, 0x1819},
|
||||
{0x1820, 0x1878}, {0x1880, 0x18AA}, {0x18B0, 0x18F5},
|
||||
{0x1900, 0x191E}, {0x1920, 0x192B}, {0x1930, 0x193B},
|
||||
{0x1940, 0x1940}, {0x1944, 0x196D}, {0x1970, 0x1974},
|
||||
{0x1980, 0x19AB}, {0x19B0, 0x19C9}, {0x19D0, 0x19DA},
|
||||
{0x19DE, 0x1A1B}, {0x1A1E, 0x1A5E}, {0x1A60, 0x1A7C},
|
||||
{0x1A7F, 0x1A89}, {0x1A90, 0x1A99}, {0x1AA0, 0x1AAD},
|
||||
{0x1AB0, 0x1ADD}, {0x1AE0, 0x1AEB}, {0x1B00, 0x1B4C},
|
||||
{0x1B4E, 0x1BF3}, {0x1BFC, 0x1C37}, {0x1C3B, 0x1C49},
|
||||
{0x1C4D, 0x1C8A}, {0x1C90, 0x1CBA}, {0x1CBD, 0x1CC7},
|
||||
{0x1CD0, 0x1CFA}, {0x1D00, 0x1F15}, {0x1F18, 0x1F1D},
|
||||
{0x1F20, 0x1F45}, {0x1F48, 0x1F4D}, {0x1F50, 0x1F57},
|
||||
{0x1F59, 0x1F59}, {0x1F5B, 0x1F5B}, {0x1F5D, 0x1F5D},
|
||||
{0x1F5F, 0x1F7D}, {0x1F80, 0x1FB4}, {0x1FB6, 0x1FC4},
|
||||
{0x1FC6, 0x1FD3}, {0x1FD6, 0x1FDB}, {0x1FDD, 0x1FEF},
|
||||
{0x1FF2, 0x1FF4}, {0x1FF6, 0x1FFE}, {0x2000, 0x200F},
|
||||
{0x2011, 0x2012}, {0x2017, 0x2017}, {0x201A, 0x201B},
|
||||
{0x201E, 0x201F}, {0x2023, 0x2023}, {0x2028, 0x202F},
|
||||
{0x2031, 0x2031}, {0x2034, 0x2034}, {0x2036, 0x203A},
|
||||
{0x203C, 0x203D}, {0x203F, 0x2064}, {0x2066, 0x2071},
|
||||
{0x2075, 0x207E}, {0x2080, 0x2080}, {0x2085, 0x208E},
|
||||
{0x2090, 0x209C}, {0x20A0, 0x20A8}, {0x20AA, 0x20AB},
|
||||
{0x20AD, 0x20C1}, {0x20D0, 0x20F0}, {0x2100, 0x2102},
|
||||
{0x2104, 0x2104}, {0x2106, 0x2108}, {0x210A, 0x2112},
|
||||
{0x2114, 0x2115}, {0x2117, 0x2120}, {0x2123, 0x2125},
|
||||
{0x2127, 0x212A}, {0x212C, 0x2152}, {0x2155, 0x215A},
|
||||
{0x215F, 0x215F}, {0x216C, 0x216F}, {0x217A, 0x2188},
|
||||
{0x218A, 0x218B}, {0x219A, 0x21B7}, {0x21BA, 0x21D1},
|
||||
{0x21D3, 0x21D3}, {0x21D5, 0x21E6}, {0x21E8, 0x21FF},
|
||||
{0x2201, 0x2201}, {0x2204, 0x2206}, {0x2209, 0x220A},
|
||||
{0x220C, 0x220E}, {0x2210, 0x2210}, {0x2212, 0x2214},
|
||||
{0x2216, 0x2219}, {0x221B, 0x221C}, {0x2221, 0x2222},
|
||||
{0x2224, 0x2224}, {0x2226, 0x2226}, {0x222D, 0x222D},
|
||||
{0x222F, 0x2233}, {0x2238, 0x223B}, {0x223E, 0x2247},
|
||||
{0x2249, 0x224B}, {0x224D, 0x2251}, {0x2253, 0x225F},
|
||||
{0x2262, 0x2263}, {0x2268, 0x2269}, {0x226C, 0x226D},
|
||||
{0x2270, 0x2281}, {0x2284, 0x2285}, {0x2288, 0x2294},
|
||||
{0x2296, 0x2298}, {0x229A, 0x22A4}, {0x22A6, 0x22BE},
|
||||
{0x22C0, 0x2311}, {0x2313, 0x2319}, {0x231C, 0x2328},
|
||||
{0x232B, 0x23E8}, {0x23ED, 0x23EF}, {0x23F1, 0x23F2},
|
||||
{0x23F4, 0x2429}, {0x2440, 0x244A}, {0x24EA, 0x24EA},
|
||||
{0x254C, 0x254F}, {0x2574, 0x257F}, {0x2590, 0x2591},
|
||||
{0x2596, 0x259F}, {0x25A2, 0x25A2}, {0x25AA, 0x25B1},
|
||||
{0x25B4, 0x25B5}, {0x25B8, 0x25BB}, {0x25BE, 0x25BF},
|
||||
{0x25C2, 0x25C5}, {0x25C9, 0x25CA}, {0x25CC, 0x25CD},
|
||||
{0x25D2, 0x25E1}, {0x25E6, 0x25EE}, {0x25F0, 0x25FC},
|
||||
{0x25FF, 0x2604}, {0x2607, 0x2608}, {0x260A, 0x260D},
|
||||
{0x2610, 0x2613}, {0x2616, 0x261B}, {0x261D, 0x261D},
|
||||
{0x261F, 0x262F}, {0x2638, 0x263F}, {0x2641, 0x2641},
|
||||
{0x2643, 0x2647}, {0x2654, 0x265F}, {0x2662, 0x2662},
|
||||
{0x2666, 0x2666}, {0x266B, 0x266B}, {0x266E, 0x266E},
|
||||
{0x2670, 0x267E}, {0x2680, 0x2692}, {0x2694, 0x269D},
|
||||
{0x26A0, 0x26A0}, {0x26A2, 0x26A9}, {0x26AC, 0x26BC},
|
||||
{0x26C0, 0x26C3}, {0x26E2, 0x26E2}, {0x26E4, 0x26E7},
|
||||
{0x2700, 0x2704}, {0x2706, 0x2709}, {0x270C, 0x2727},
|
||||
{0x2729, 0x273C}, {0x273E, 0x274B}, {0x274D, 0x274D},
|
||||
{0x274F, 0x2752}, {0x2756, 0x2756}, {0x2758, 0x2775},
|
||||
{0x2780, 0x2794}, {0x2798, 0x27AF}, {0x27B1, 0x27BE},
|
||||
{0x27C0, 0x27E5}, {0x27EE, 0x2984}, {0x2987, 0x2B1A},
|
||||
{0x2B1D, 0x2B4F}, {0x2B51, 0x2B54}, {0x2B5A, 0x2B73},
|
||||
{0x2B76, 0x2B95}, {0x2B97, 0x2CF3}, {0x2CF9, 0x2D25},
|
||||
{0x2670, 0x267E}, {0x2680, 0x2689}, {0x2690, 0x2692},
|
||||
{0x2694, 0x269D}, {0x26A0, 0x26A0}, {0x26A2, 0x26A9},
|
||||
{0x26AC, 0x26BC}, {0x26C0, 0x26C3}, {0x26E2, 0x26E2},
|
||||
{0x26E4, 0x26E7}, {0x2700, 0x2704}, {0x2706, 0x2709},
|
||||
{0x270C, 0x2727}, {0x2729, 0x273C}, {0x273E, 0x274B},
|
||||
{0x274D, 0x274D}, {0x274F, 0x2752}, {0x2756, 0x2756},
|
||||
{0x2758, 0x2775}, {0x2780, 0x2794}, {0x2798, 0x27AF},
|
||||
{0x27B1, 0x27BE}, {0x27C0, 0x27E5}, {0x27EE, 0x2984},
|
||||
{0x2987, 0x2B1A}, {0x2B1D, 0x2B4F}, {0x2B51, 0x2B54},
|
||||
{0x2B5A, 0x2B73}, {0x2B76, 0x2CF3}, {0x2CF9, 0x2D25},
|
||||
{0x2D27, 0x2D27}, {0x2D2D, 0x2D2D}, {0x2D30, 0x2D67},
|
||||
{0x2D6F, 0x2D70}, {0x2D7F, 0x2D96}, {0x2DA0, 0x2DA6},
|
||||
{0x2DA8, 0x2DAE}, {0x2DB0, 0x2DB6}, {0x2DB8, 0x2DBE},
|
||||
{0x2DC0, 0x2DC6}, {0x2DC8, 0x2DCE}, {0x2DD0, 0x2DD6},
|
||||
{0x2DD8, 0x2DDE}, {0x2DE0, 0x2E5D}, {0x303F, 0x303F},
|
||||
{0x4DC0, 0x4DFF}, {0xA4D0, 0xA62B}, {0xA640, 0xA6F7},
|
||||
{0xA700, 0xA7CA}, {0xA7D0, 0xA7D1}, {0xA7D3, 0xA7D3},
|
||||
{0xA7D5, 0xA7D9}, {0xA7F2, 0xA82C}, {0xA830, 0xA839},
|
||||
{0xA840, 0xA877}, {0xA880, 0xA8C5}, {0xA8CE, 0xA8D9},
|
||||
{0xA8E0, 0xA953}, {0xA95F, 0xA95F}, {0xA980, 0xA9CD},
|
||||
{0xA9CF, 0xA9D9}, {0xA9DE, 0xA9FE}, {0xAA00, 0xAA36},
|
||||
{0xAA40, 0xAA4D}, {0xAA50, 0xAA59}, {0xAA5C, 0xAAC2},
|
||||
{0xAADB, 0xAAF6}, {0xAB01, 0xAB06}, {0xAB09, 0xAB0E},
|
||||
{0xAB11, 0xAB16}, {0xAB20, 0xAB26}, {0xAB28, 0xAB2E},
|
||||
{0xAB30, 0xAB6B}, {0xAB70, 0xABED}, {0xABF0, 0xABF9},
|
||||
{0xD7B0, 0xD7C6}, {0xD7CB, 0xD7FB}, {0xD800, 0xDFFF},
|
||||
{0xFB00, 0xFB06}, {0xFB13, 0xFB17}, {0xFB1D, 0xFB36},
|
||||
{0xFB38, 0xFB3C}, {0xFB3E, 0xFB3E}, {0xFB40, 0xFB41},
|
||||
{0xFB43, 0xFB44}, {0xFB46, 0xFBC2}, {0xFBD3, 0xFD8F},
|
||||
{0xFD92, 0xFDC7}, {0xFDCF, 0xFDCF}, {0xFDF0, 0xFDFF},
|
||||
{0xFE20, 0xFE2F}, {0xFE70, 0xFE74}, {0xFE76, 0xFEFC},
|
||||
{0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFC}, {0x10000, 0x1000B},
|
||||
{0x1000D, 0x10026}, {0x10028, 0x1003A}, {0x1003C, 0x1003D},
|
||||
{0x1003F, 0x1004D}, {0x10050, 0x1005D}, {0x10080, 0x100FA},
|
||||
{0x10100, 0x10102}, {0x10107, 0x10133}, {0x10137, 0x1018E},
|
||||
{0x10190, 0x1019C}, {0x101A0, 0x101A0}, {0x101D0, 0x101FD},
|
||||
{0x10280, 0x1029C}, {0x102A0, 0x102D0}, {0x102E0, 0x102FB},
|
||||
{0x10300, 0x10323}, {0x1032D, 0x1034A}, {0x10350, 0x1037A},
|
||||
{0x10380, 0x1039D}, {0x1039F, 0x103C3}, {0x103C8, 0x103D5},
|
||||
{0x10400, 0x1049D}, {0x104A0, 0x104A9}, {0x104B0, 0x104D3},
|
||||
{0x104D8, 0x104FB}, {0x10500, 0x10527}, {0x10530, 0x10563},
|
||||
{0x1056F, 0x1057A}, {0x1057C, 0x1058A}, {0x1058C, 0x10592},
|
||||
{0x10594, 0x10595}, {0x10597, 0x105A1}, {0x105A3, 0x105B1},
|
||||
{0x105B3, 0x105B9}, {0x105BB, 0x105BC}, {0x10600, 0x10736},
|
||||
{0xA4D0, 0xA62B}, {0xA640, 0xA6F7}, {0xA700, 0xA7DC},
|
||||
{0xA7F1, 0xA82C}, {0xA830, 0xA839}, {0xA840, 0xA877},
|
||||
{0xA880, 0xA8C5}, {0xA8CE, 0xA8D9}, {0xA8E0, 0xA953},
|
||||
{0xA95F, 0xA95F}, {0xA980, 0xA9CD}, {0xA9CF, 0xA9D9},
|
||||
{0xA9DE, 0xA9FE}, {0xAA00, 0xAA36}, {0xAA40, 0xAA4D},
|
||||
{0xAA50, 0xAA59}, {0xAA5C, 0xAAC2}, {0xAADB, 0xAAF6},
|
||||
{0xAB01, 0xAB06}, {0xAB09, 0xAB0E}, {0xAB11, 0xAB16},
|
||||
{0xAB20, 0xAB26}, {0xAB28, 0xAB2E}, {0xAB30, 0xAB6B},
|
||||
{0xAB70, 0xABED}, {0xABF0, 0xABF9}, {0xD7B0, 0xD7C6},
|
||||
{0xD7CB, 0xD7FB}, {0xD800, 0xDFFF}, {0xFB00, 0xFB06},
|
||||
{0xFB13, 0xFB17}, {0xFB1D, 0xFB36}, {0xFB38, 0xFB3C},
|
||||
{0xFB3E, 0xFB3E}, {0xFB40, 0xFB41}, {0xFB43, 0xFB44},
|
||||
{0xFB46, 0xFDCF}, {0xFDF0, 0xFDFF}, {0xFE20, 0xFE2F},
|
||||
{0xFE70, 0xFE74}, {0xFE76, 0xFEFC}, {0xFEFF, 0xFEFF},
|
||||
{0xFFF9, 0xFFFC}, {0x10000, 0x1000B}, {0x1000D, 0x10026},
|
||||
{0x10028, 0x1003A}, {0x1003C, 0x1003D}, {0x1003F, 0x1004D},
|
||||
{0x10050, 0x1005D}, {0x10080, 0x100FA}, {0x10100, 0x10102},
|
||||
{0x10107, 0x10133}, {0x10137, 0x1018E}, {0x10190, 0x1019C},
|
||||
{0x101A0, 0x101A0}, {0x101D0, 0x101FD}, {0x10280, 0x1029C},
|
||||
{0x102A0, 0x102D0}, {0x102E0, 0x102FB}, {0x10300, 0x10323},
|
||||
{0x1032D, 0x1034A}, {0x10350, 0x1037A}, {0x10380, 0x1039D},
|
||||
{0x1039F, 0x103C3}, {0x103C8, 0x103D5}, {0x10400, 0x1049D},
|
||||
{0x104A0, 0x104A9}, {0x104B0, 0x104D3}, {0x104D8, 0x104FB},
|
||||
{0x10500, 0x10527}, {0x10530, 0x10563}, {0x1056F, 0x1057A},
|
||||
{0x1057C, 0x1058A}, {0x1058C, 0x10592}, {0x10594, 0x10595},
|
||||
{0x10597, 0x105A1}, {0x105A3, 0x105B1}, {0x105B3, 0x105B9},
|
||||
{0x105BB, 0x105BC}, {0x105C0, 0x105F3}, {0x10600, 0x10736},
|
||||
{0x10740, 0x10755}, {0x10760, 0x10767}, {0x10780, 0x10785},
|
||||
{0x10787, 0x107B0}, {0x107B2, 0x107BA}, {0x10800, 0x10805},
|
||||
{0x10808, 0x10808}, {0x1080A, 0x10835}, {0x10837, 0x10838},
|
||||
{0x1083C, 0x1083C}, {0x1083F, 0x10855}, {0x10857, 0x1089E},
|
||||
{0x108A7, 0x108AF}, {0x108E0, 0x108F2}, {0x108F4, 0x108F5},
|
||||
{0x108FB, 0x1091B}, {0x1091F, 0x10939}, {0x1093F, 0x1093F},
|
||||
{0x108FB, 0x1091B}, {0x1091F, 0x10939}, {0x1093F, 0x10959},
|
||||
{0x10980, 0x109B7}, {0x109BC, 0x109CF}, {0x109D2, 0x10A03},
|
||||
{0x10A05, 0x10A06}, {0x10A0C, 0x10A13}, {0x10A15, 0x10A17},
|
||||
{0x10A19, 0x10A35}, {0x10A38, 0x10A3A}, {0x10A3F, 0x10A48},
|
||||
@@ -325,53 +324,63 @@ var neutral = table{
|
||||
{0x10B58, 0x10B72}, {0x10B78, 0x10B91}, {0x10B99, 0x10B9C},
|
||||
{0x10BA9, 0x10BAF}, {0x10C00, 0x10C48}, {0x10C80, 0x10CB2},
|
||||
{0x10CC0, 0x10CF2}, {0x10CFA, 0x10D27}, {0x10D30, 0x10D39},
|
||||
{0x10D40, 0x10D65}, {0x10D69, 0x10D85}, {0x10D8E, 0x10D8F},
|
||||
{0x10E60, 0x10E7E}, {0x10E80, 0x10EA9}, {0x10EAB, 0x10EAD},
|
||||
{0x10EB0, 0x10EB1}, {0x10EFD, 0x10F27}, {0x10F30, 0x10F59},
|
||||
{0x10F70, 0x10F89}, {0x10FB0, 0x10FCB}, {0x10FE0, 0x10FF6},
|
||||
{0x11000, 0x1104D}, {0x11052, 0x11075}, {0x1107F, 0x110C2},
|
||||
{0x110CD, 0x110CD}, {0x110D0, 0x110E8}, {0x110F0, 0x110F9},
|
||||
{0x11100, 0x11134}, {0x11136, 0x11147}, {0x11150, 0x11176},
|
||||
{0x11180, 0x111DF}, {0x111E1, 0x111F4}, {0x11200, 0x11211},
|
||||
{0x11213, 0x11241}, {0x11280, 0x11286}, {0x11288, 0x11288},
|
||||
{0x1128A, 0x1128D}, {0x1128F, 0x1129D}, {0x1129F, 0x112A9},
|
||||
{0x112B0, 0x112EA}, {0x112F0, 0x112F9}, {0x11300, 0x11303},
|
||||
{0x11305, 0x1130C}, {0x1130F, 0x11310}, {0x11313, 0x11328},
|
||||
{0x1132A, 0x11330}, {0x11332, 0x11333}, {0x11335, 0x11339},
|
||||
{0x1133B, 0x11344}, {0x11347, 0x11348}, {0x1134B, 0x1134D},
|
||||
{0x11350, 0x11350}, {0x11357, 0x11357}, {0x1135D, 0x11363},
|
||||
{0x11366, 0x1136C}, {0x11370, 0x11374}, {0x11400, 0x1145B},
|
||||
{0x1145D, 0x11461}, {0x11480, 0x114C7}, {0x114D0, 0x114D9},
|
||||
{0x11580, 0x115B5}, {0x115B8, 0x115DD}, {0x11600, 0x11644},
|
||||
{0x11650, 0x11659}, {0x11660, 0x1166C}, {0x11680, 0x116B9},
|
||||
{0x116C0, 0x116C9}, {0x11700, 0x1171A}, {0x1171D, 0x1172B},
|
||||
{0x11730, 0x11746}, {0x11800, 0x1183B}, {0x118A0, 0x118F2},
|
||||
{0x118FF, 0x11906}, {0x11909, 0x11909}, {0x1190C, 0x11913},
|
||||
{0x11915, 0x11916}, {0x11918, 0x11935}, {0x11937, 0x11938},
|
||||
{0x1193B, 0x11946}, {0x11950, 0x11959}, {0x119A0, 0x119A7},
|
||||
{0x119AA, 0x119D7}, {0x119DA, 0x119E4}, {0x11A00, 0x11A47},
|
||||
{0x11A50, 0x11AA2}, {0x11AB0, 0x11AF8}, {0x11B00, 0x11B09},
|
||||
{0x11C00, 0x11C08}, {0x11C0A, 0x11C36}, {0x11C38, 0x11C45},
|
||||
{0x11C50, 0x11C6C}, {0x11C70, 0x11C8F}, {0x11C92, 0x11CA7},
|
||||
{0x11CA9, 0x11CB6}, {0x11D00, 0x11D06}, {0x11D08, 0x11D09},
|
||||
{0x11D0B, 0x11D36}, {0x11D3A, 0x11D3A}, {0x11D3C, 0x11D3D},
|
||||
{0x11D3F, 0x11D47}, {0x11D50, 0x11D59}, {0x11D60, 0x11D65},
|
||||
{0x11D67, 0x11D68}, {0x11D6A, 0x11D8E}, {0x11D90, 0x11D91},
|
||||
{0x11D93, 0x11D98}, {0x11DA0, 0x11DA9}, {0x11EE0, 0x11EF8},
|
||||
{0x11F00, 0x11F10}, {0x11F12, 0x11F3A}, {0x11F3E, 0x11F59},
|
||||
{0x11FB0, 0x11FB0}, {0x11FC0, 0x11FF1}, {0x11FFF, 0x12399},
|
||||
{0x12400, 0x1246E}, {0x12470, 0x12474}, {0x12480, 0x12543},
|
||||
{0x12F90, 0x12FF2}, {0x13000, 0x13455}, {0x14400, 0x14646},
|
||||
{0x10EB0, 0x10EB1}, {0x10EC2, 0x10EC7}, {0x10ED0, 0x10ED8},
|
||||
{0x10EFA, 0x10F27}, {0x10F30, 0x10F59}, {0x10F70, 0x10F89},
|
||||
{0x10FB0, 0x10FCB}, {0x10FE0, 0x10FF6}, {0x11000, 0x1104D},
|
||||
{0x11052, 0x11075}, {0x1107F, 0x110C2}, {0x110CD, 0x110CD},
|
||||
{0x110D0, 0x110E8}, {0x110F0, 0x110F9}, {0x11100, 0x11134},
|
||||
{0x11136, 0x11147}, {0x11150, 0x11176}, {0x11180, 0x111DF},
|
||||
{0x111E1, 0x111F4}, {0x11200, 0x11211}, {0x11213, 0x11241},
|
||||
{0x11280, 0x11286}, {0x11288, 0x11288}, {0x1128A, 0x1128D},
|
||||
{0x1128F, 0x1129D}, {0x1129F, 0x112A9}, {0x112B0, 0x112EA},
|
||||
{0x112F0, 0x112F9}, {0x11300, 0x11303}, {0x11305, 0x1130C},
|
||||
{0x1130F, 0x11310}, {0x11313, 0x11328}, {0x1132A, 0x11330},
|
||||
{0x11332, 0x11333}, {0x11335, 0x11339}, {0x1133B, 0x11344},
|
||||
{0x11347, 0x11348}, {0x1134B, 0x1134D}, {0x11350, 0x11350},
|
||||
{0x11357, 0x11357}, {0x1135D, 0x11363}, {0x11366, 0x1136C},
|
||||
{0x11370, 0x11374}, {0x11380, 0x11389}, {0x1138B, 0x1138B},
|
||||
{0x1138E, 0x1138E}, {0x11390, 0x113B5}, {0x113B7, 0x113C0},
|
||||
{0x113C2, 0x113C2}, {0x113C5, 0x113C5}, {0x113C7, 0x113CA},
|
||||
{0x113CC, 0x113D5}, {0x113D7, 0x113D8}, {0x113E1, 0x113E2},
|
||||
{0x11400, 0x1145B}, {0x1145D, 0x11461}, {0x11480, 0x114C7},
|
||||
{0x114D0, 0x114D9}, {0x11580, 0x115B5}, {0x115B8, 0x115DD},
|
||||
{0x11600, 0x11644}, {0x11650, 0x11659}, {0x11660, 0x1166C},
|
||||
{0x11680, 0x116B9}, {0x116C0, 0x116C9}, {0x116D0, 0x116E3},
|
||||
{0x11700, 0x1171A}, {0x1171D, 0x1172B}, {0x11730, 0x11746},
|
||||
{0x11800, 0x1183B}, {0x118A0, 0x118F2}, {0x118FF, 0x11906},
|
||||
{0x11909, 0x11909}, {0x1190C, 0x11913}, {0x11915, 0x11916},
|
||||
{0x11918, 0x11935}, {0x11937, 0x11938}, {0x1193B, 0x11946},
|
||||
{0x11950, 0x11959}, {0x119A0, 0x119A7}, {0x119AA, 0x119D7},
|
||||
{0x119DA, 0x119E4}, {0x11A00, 0x11A47}, {0x11A50, 0x11AA2},
|
||||
{0x11AB0, 0x11AF8}, {0x11B00, 0x11B09}, {0x11B60, 0x11B67},
|
||||
{0x11BC0, 0x11BE1}, {0x11BF0, 0x11BF9}, {0x11C00, 0x11C08},
|
||||
{0x11C0A, 0x11C36}, {0x11C38, 0x11C45}, {0x11C50, 0x11C6C},
|
||||
{0x11C70, 0x11C8F}, {0x11C92, 0x11CA7}, {0x11CA9, 0x11CB6},
|
||||
{0x11D00, 0x11D06}, {0x11D08, 0x11D09}, {0x11D0B, 0x11D36},
|
||||
{0x11D3A, 0x11D3A}, {0x11D3C, 0x11D3D}, {0x11D3F, 0x11D47},
|
||||
{0x11D50, 0x11D59}, {0x11D60, 0x11D65}, {0x11D67, 0x11D68},
|
||||
{0x11D6A, 0x11D8E}, {0x11D90, 0x11D91}, {0x11D93, 0x11D98},
|
||||
{0x11DA0, 0x11DA9}, {0x11DB0, 0x11DDB}, {0x11DE0, 0x11DE9},
|
||||
{0x11EE0, 0x11EF8}, {0x11F00, 0x11F10}, {0x11F12, 0x11F3A},
|
||||
{0x11F3E, 0x11F5A}, {0x11FB0, 0x11FB0}, {0x11FC0, 0x11FF1},
|
||||
{0x11FFF, 0x12399}, {0x12400, 0x1246E}, {0x12470, 0x12474},
|
||||
{0x12480, 0x12543}, {0x12F90, 0x12FF2}, {0x13000, 0x13455},
|
||||
{0x13460, 0x143FA}, {0x14400, 0x14646}, {0x16100, 0x16139},
|
||||
{0x16800, 0x16A38}, {0x16A40, 0x16A5E}, {0x16A60, 0x16A69},
|
||||
{0x16A6E, 0x16ABE}, {0x16AC0, 0x16AC9}, {0x16AD0, 0x16AED},
|
||||
{0x16AF0, 0x16AF5}, {0x16B00, 0x16B45}, {0x16B50, 0x16B59},
|
||||
{0x16B5B, 0x16B61}, {0x16B63, 0x16B77}, {0x16B7D, 0x16B8F},
|
||||
{0x16E40, 0x16E9A}, {0x16F00, 0x16F4A}, {0x16F4F, 0x16F87},
|
||||
{0x16D40, 0x16D79}, {0x16E40, 0x16E9A}, {0x16EA0, 0x16EB8},
|
||||
{0x16EBB, 0x16ED3}, {0x16F00, 0x16F4A}, {0x16F4F, 0x16F87},
|
||||
{0x16F8F, 0x16F9F}, {0x1BC00, 0x1BC6A}, {0x1BC70, 0x1BC7C},
|
||||
{0x1BC80, 0x1BC88}, {0x1BC90, 0x1BC99}, {0x1BC9C, 0x1BCA3},
|
||||
{0x1CF00, 0x1CF2D}, {0x1CF30, 0x1CF46}, {0x1CF50, 0x1CFC3},
|
||||
{0x1D000, 0x1D0F5}, {0x1D100, 0x1D126}, {0x1D129, 0x1D1EA},
|
||||
{0x1D200, 0x1D245}, {0x1D2C0, 0x1D2D3}, {0x1D2E0, 0x1D2F3},
|
||||
{0x1D300, 0x1D356}, {0x1D360, 0x1D378}, {0x1D400, 0x1D454},
|
||||
{0x1CC00, 0x1CCFC}, {0x1CD00, 0x1CEB3}, {0x1CEBA, 0x1CED0},
|
||||
{0x1CEE0, 0x1CEF0}, {0x1CF00, 0x1CF2D}, {0x1CF30, 0x1CF46},
|
||||
{0x1CF50, 0x1CFC3}, {0x1D000, 0x1D0F5}, {0x1D100, 0x1D126},
|
||||
{0x1D129, 0x1D1EA}, {0x1D200, 0x1D245}, {0x1D2C0, 0x1D2D3},
|
||||
{0x1D2E0, 0x1D2F3}, {0x1D377, 0x1D378}, {0x1D400, 0x1D454},
|
||||
{0x1D456, 0x1D49C}, {0x1D49E, 0x1D49F}, {0x1D4A2, 0x1D4A2},
|
||||
{0x1D4A5, 0x1D4A6}, {0x1D4A9, 0x1D4AC}, {0x1D4AE, 0x1D4B9},
|
||||
{0x1D4BB, 0x1D4BB}, {0x1D4BD, 0x1D4C3}, {0x1D4C5, 0x1D505},
|
||||
@@ -385,66 +394,94 @@ var neutral = table{
|
||||
{0x1E08F, 0x1E08F}, {0x1E100, 0x1E12C}, {0x1E130, 0x1E13D},
|
||||
{0x1E140, 0x1E149}, {0x1E14E, 0x1E14F}, {0x1E290, 0x1E2AE},
|
||||
{0x1E2C0, 0x1E2F9}, {0x1E2FF, 0x1E2FF}, {0x1E4D0, 0x1E4F9},
|
||||
{0x1E7E0, 0x1E7E6}, {0x1E7E8, 0x1E7EB}, {0x1E7ED, 0x1E7EE},
|
||||
{0x1E7F0, 0x1E7FE}, {0x1E800, 0x1E8C4}, {0x1E8C7, 0x1E8D6},
|
||||
{0x1E900, 0x1E94B}, {0x1E950, 0x1E959}, {0x1E95E, 0x1E95F},
|
||||
{0x1EC71, 0x1ECB4}, {0x1ED01, 0x1ED3D}, {0x1EE00, 0x1EE03},
|
||||
{0x1EE05, 0x1EE1F}, {0x1EE21, 0x1EE22}, {0x1EE24, 0x1EE24},
|
||||
{0x1EE27, 0x1EE27}, {0x1EE29, 0x1EE32}, {0x1EE34, 0x1EE37},
|
||||
{0x1EE39, 0x1EE39}, {0x1EE3B, 0x1EE3B}, {0x1EE42, 0x1EE42},
|
||||
{0x1EE47, 0x1EE47}, {0x1EE49, 0x1EE49}, {0x1EE4B, 0x1EE4B},
|
||||
{0x1EE4D, 0x1EE4F}, {0x1EE51, 0x1EE52}, {0x1EE54, 0x1EE54},
|
||||
{0x1EE57, 0x1EE57}, {0x1EE59, 0x1EE59}, {0x1EE5B, 0x1EE5B},
|
||||
{0x1EE5D, 0x1EE5D}, {0x1EE5F, 0x1EE5F}, {0x1EE61, 0x1EE62},
|
||||
{0x1EE64, 0x1EE64}, {0x1EE67, 0x1EE6A}, {0x1EE6C, 0x1EE72},
|
||||
{0x1EE74, 0x1EE77}, {0x1EE79, 0x1EE7C}, {0x1EE7E, 0x1EE7E},
|
||||
{0x1EE80, 0x1EE89}, {0x1EE8B, 0x1EE9B}, {0x1EEA1, 0x1EEA3},
|
||||
{0x1EEA5, 0x1EEA9}, {0x1EEAB, 0x1EEBB}, {0x1EEF0, 0x1EEF1},
|
||||
{0x1F000, 0x1F003}, {0x1F005, 0x1F02B}, {0x1F030, 0x1F093},
|
||||
{0x1F0A0, 0x1F0AE}, {0x1F0B1, 0x1F0BF}, {0x1F0C1, 0x1F0CE},
|
||||
{0x1F0D1, 0x1F0F5}, {0x1F10B, 0x1F10F}, {0x1F12E, 0x1F12F},
|
||||
{0x1F16A, 0x1F16F}, {0x1F1AD, 0x1F1AD}, {0x1F1E6, 0x1F1FF},
|
||||
{0x1F321, 0x1F32C}, {0x1F336, 0x1F336}, {0x1F37D, 0x1F37D},
|
||||
{0x1F394, 0x1F39F}, {0x1F3CB, 0x1F3CE}, {0x1F3D4, 0x1F3DF},
|
||||
{0x1F3F1, 0x1F3F3}, {0x1F3F5, 0x1F3F7}, {0x1F43F, 0x1F43F},
|
||||
{0x1F441, 0x1F441}, {0x1F4FD, 0x1F4FE}, {0x1F53E, 0x1F54A},
|
||||
{0x1F54F, 0x1F54F}, {0x1F568, 0x1F579}, {0x1F57B, 0x1F594},
|
||||
{0x1F597, 0x1F5A3}, {0x1F5A5, 0x1F5FA}, {0x1F650, 0x1F67F},
|
||||
{0x1F6C6, 0x1F6CB}, {0x1F6CD, 0x1F6CF}, {0x1F6D3, 0x1F6D4},
|
||||
{0x1F6E0, 0x1F6EA}, {0x1F6F0, 0x1F6F3}, {0x1F700, 0x1F776},
|
||||
{0x1F77B, 0x1F7D9}, {0x1F800, 0x1F80B}, {0x1F810, 0x1F847},
|
||||
{0x1F850, 0x1F859}, {0x1F860, 0x1F887}, {0x1F890, 0x1F8AD},
|
||||
{0x1F8B0, 0x1F8B1}, {0x1F900, 0x1F90B}, {0x1F93B, 0x1F93B},
|
||||
{0x1F946, 0x1F946}, {0x1FA00, 0x1FA53}, {0x1FA60, 0x1FA6D},
|
||||
{0x1FB00, 0x1FB92}, {0x1FB94, 0x1FBCA}, {0x1FBF0, 0x1FBF9},
|
||||
{0xE0001, 0xE0001}, {0xE0020, 0xE007F},
|
||||
{0x1E5D0, 0x1E5FA}, {0x1E5FF, 0x1E5FF}, {0x1E6C0, 0x1E6DE},
|
||||
{0x1E6E0, 0x1E6F5}, {0x1E6FE, 0x1E6FF}, {0x1E7E0, 0x1E7E6},
|
||||
{0x1E7E8, 0x1E7EB}, {0x1E7ED, 0x1E7EE}, {0x1E7F0, 0x1E7FE},
|
||||
{0x1E800, 0x1E8C4}, {0x1E8C7, 0x1E8D6}, {0x1E900, 0x1E94B},
|
||||
{0x1E950, 0x1E959}, {0x1E95E, 0x1E95F}, {0x1EC71, 0x1ECB4},
|
||||
{0x1ED01, 0x1ED3D}, {0x1EE00, 0x1EE03}, {0x1EE05, 0x1EE1F},
|
||||
{0x1EE21, 0x1EE22}, {0x1EE24, 0x1EE24}, {0x1EE27, 0x1EE27},
|
||||
{0x1EE29, 0x1EE32}, {0x1EE34, 0x1EE37}, {0x1EE39, 0x1EE39},
|
||||
{0x1EE3B, 0x1EE3B}, {0x1EE42, 0x1EE42}, {0x1EE47, 0x1EE47},
|
||||
{0x1EE49, 0x1EE49}, {0x1EE4B, 0x1EE4B}, {0x1EE4D, 0x1EE4F},
|
||||
{0x1EE51, 0x1EE52}, {0x1EE54, 0x1EE54}, {0x1EE57, 0x1EE57},
|
||||
{0x1EE59, 0x1EE59}, {0x1EE5B, 0x1EE5B}, {0x1EE5D, 0x1EE5D},
|
||||
{0x1EE5F, 0x1EE5F}, {0x1EE61, 0x1EE62}, {0x1EE64, 0x1EE64},
|
||||
{0x1EE67, 0x1EE6A}, {0x1EE6C, 0x1EE72}, {0x1EE74, 0x1EE77},
|
||||
{0x1EE79, 0x1EE7C}, {0x1EE7E, 0x1EE7E}, {0x1EE80, 0x1EE89},
|
||||
{0x1EE8B, 0x1EE9B}, {0x1EEA1, 0x1EEA3}, {0x1EEA5, 0x1EEA9},
|
||||
{0x1EEAB, 0x1EEBB}, {0x1EEF0, 0x1EEF1}, {0x1F000, 0x1F003},
|
||||
{0x1F005, 0x1F02B}, {0x1F030, 0x1F093}, {0x1F0A0, 0x1F0AE},
|
||||
{0x1F0B1, 0x1F0BF}, {0x1F0C1, 0x1F0CE}, {0x1F0D1, 0x1F0F5},
|
||||
{0x1F10B, 0x1F10F}, {0x1F12E, 0x1F12F}, {0x1F16A, 0x1F16F},
|
||||
{0x1F1AD, 0x1F1AD}, {0x1F1E6, 0x1F1FF}, {0x1F321, 0x1F32C},
|
||||
{0x1F336, 0x1F336}, {0x1F37D, 0x1F37D}, {0x1F394, 0x1F39F},
|
||||
{0x1F3CB, 0x1F3CE}, {0x1F3D4, 0x1F3DF}, {0x1F3F1, 0x1F3F3},
|
||||
{0x1F3F5, 0x1F3F7}, {0x1F43F, 0x1F43F}, {0x1F441, 0x1F441},
|
||||
{0x1F4FD, 0x1F4FE}, {0x1F53E, 0x1F54A}, {0x1F54F, 0x1F54F},
|
||||
{0x1F568, 0x1F579}, {0x1F57B, 0x1F594}, {0x1F597, 0x1F5A3},
|
||||
{0x1F5A5, 0x1F5FA}, {0x1F650, 0x1F67F}, {0x1F6C6, 0x1F6CB},
|
||||
{0x1F6CD, 0x1F6CF}, {0x1F6D3, 0x1F6D4}, {0x1F6E0, 0x1F6EA},
|
||||
{0x1F6F0, 0x1F6F3}, {0x1F700, 0x1F7D9}, {0x1F800, 0x1F80B},
|
||||
{0x1F810, 0x1F847}, {0x1F850, 0x1F859}, {0x1F860, 0x1F887},
|
||||
{0x1F890, 0x1F8AD}, {0x1F8B0, 0x1F8BB}, {0x1F8C0, 0x1F8C1},
|
||||
{0x1F8D0, 0x1F8D8}, {0x1F900, 0x1F90B}, {0x1F93B, 0x1F93B},
|
||||
{0x1F946, 0x1F946}, {0x1FA00, 0x1FA57}, {0x1FA60, 0x1FA6D},
|
||||
{0x1FB00, 0x1FB92}, {0x1FB94, 0x1FBFA}, {0xE0001, 0xE0001},
|
||||
{0xE0020, 0xE007F},
|
||||
}
|
||||
|
||||
var emoji = table{
|
||||
{0x203C, 0x203C}, {0x2049, 0x2049}, {0x2122, 0x2122},
|
||||
{0x2139, 0x2139}, {0x2194, 0x2199}, {0x21A9, 0x21AA},
|
||||
{0x231A, 0x231B}, {0x2328, 0x2328}, {0x2388, 0x2388},
|
||||
{0x23CF, 0x23CF}, {0x23E9, 0x23F3}, {0x23F8, 0x23FA},
|
||||
{0x24C2, 0x24C2}, {0x25AA, 0x25AB}, {0x25B6, 0x25B6},
|
||||
{0x25C0, 0x25C0}, {0x25FB, 0x25FE}, {0x2600, 0x2605},
|
||||
{0x2607, 0x2612}, {0x2614, 0x2685}, {0x2690, 0x2705},
|
||||
{0x2708, 0x2712}, {0x2714, 0x2714}, {0x2716, 0x2716},
|
||||
{0x271D, 0x271D}, {0x2721, 0x2721}, {0x2728, 0x2728},
|
||||
{0x2733, 0x2734}, {0x2744, 0x2744}, {0x2747, 0x2747},
|
||||
{0x274C, 0x274C}, {0x274E, 0x274E}, {0x2753, 0x2755},
|
||||
{0x2757, 0x2757}, {0x2763, 0x2767}, {0x2795, 0x2797},
|
||||
{0x27A1, 0x27A1}, {0x27B0, 0x27B0}, {0x27BF, 0x27BF},
|
||||
{0x2934, 0x2935}, {0x2B05, 0x2B07}, {0x2B1B, 0x2B1C},
|
||||
{0x2B50, 0x2B50}, {0x2B55, 0x2B55}, {0x3030, 0x3030},
|
||||
{0x303D, 0x303D}, {0x3297, 0x3297}, {0x3299, 0x3299},
|
||||
{0x1F000, 0x1F0FF}, {0x1F10D, 0x1F10F}, {0x1F12F, 0x1F12F},
|
||||
{0x1F16C, 0x1F171}, {0x1F17E, 0x1F17F}, {0x1F18E, 0x1F18E},
|
||||
{0x1F191, 0x1F19A}, {0x1F1AD, 0x1F1E5}, {0x1F201, 0x1F20F},
|
||||
{0x1F21A, 0x1F21A}, {0x1F22F, 0x1F22F}, {0x1F232, 0x1F23A},
|
||||
{0x1F23C, 0x1F23F}, {0x1F249, 0x1F3FA}, {0x1F400, 0x1F53D},
|
||||
{0x1F546, 0x1F64F}, {0x1F680, 0x1F6FF}, {0x1F774, 0x1F77F},
|
||||
{0x1F7D5, 0x1F7FF}, {0x1F80C, 0x1F80F}, {0x1F848, 0x1F84F},
|
||||
{0x1F85A, 0x1F85F}, {0x1F888, 0x1F88F}, {0x1F8AE, 0x1F8FF},
|
||||
{0x1F90C, 0x1F93A}, {0x1F93C, 0x1F945}, {0x1F947, 0x1FAFF},
|
||||
{0x231A, 0x231B}, {0x2328, 0x2328}, {0x23CF, 0x23CF},
|
||||
{0x23E9, 0x23F3}, {0x23F8, 0x23FA}, {0x24C2, 0x24C2},
|
||||
{0x25AA, 0x25AB}, {0x25B6, 0x25B6}, {0x25C0, 0x25C0},
|
||||
{0x25FB, 0x25FE}, {0x2600, 0x2604}, {0x260E, 0x260E},
|
||||
{0x2611, 0x2611}, {0x2614, 0x2615}, {0x2618, 0x2618},
|
||||
{0x261D, 0x261D}, {0x2620, 0x2620}, {0x2622, 0x2623},
|
||||
{0x2626, 0x2626}, {0x262A, 0x262A}, {0x262E, 0x262F},
|
||||
{0x2638, 0x263A}, {0x2640, 0x2640}, {0x2642, 0x2642},
|
||||
{0x2648, 0x2653}, {0x265F, 0x2660}, {0x2663, 0x2663},
|
||||
{0x2665, 0x2666}, {0x2668, 0x2668}, {0x267B, 0x267B},
|
||||
{0x267E, 0x267F}, {0x2692, 0x2697}, {0x2699, 0x2699},
|
||||
{0x269B, 0x269C}, {0x26A0, 0x26A1}, {0x26A7, 0x26A7},
|
||||
{0x26AA, 0x26AB}, {0x26B0, 0x26B1}, {0x26BD, 0x26BE},
|
||||
{0x26C4, 0x26C5}, {0x26C8, 0x26C8}, {0x26CE, 0x26CF},
|
||||
{0x26D1, 0x26D1}, {0x26D3, 0x26D4}, {0x26E9, 0x26EA},
|
||||
{0x26F0, 0x26F5}, {0x26F7, 0x26FA}, {0x26FD, 0x26FD},
|
||||
{0x2702, 0x2702}, {0x2705, 0x2705}, {0x2708, 0x270D},
|
||||
{0x270F, 0x270F}, {0x2712, 0x2712}, {0x2714, 0x2714},
|
||||
{0x2716, 0x2716}, {0x271D, 0x271D}, {0x2721, 0x2721},
|
||||
{0x2728, 0x2728}, {0x2733, 0x2734}, {0x2744, 0x2744},
|
||||
{0x2747, 0x2747}, {0x274C, 0x274C}, {0x274E, 0x274E},
|
||||
{0x2753, 0x2755}, {0x2757, 0x2757}, {0x2763, 0x2764},
|
||||
{0x2795, 0x2797}, {0x27A1, 0x27A1}, {0x27B0, 0x27B0},
|
||||
{0x27BF, 0x27BF}, {0x2934, 0x2935}, {0x2B05, 0x2B07},
|
||||
{0x2B1B, 0x2B1C}, {0x2B50, 0x2B50}, {0x2B55, 0x2B55},
|
||||
{0x3030, 0x3030}, {0x303D, 0x303D}, {0x3297, 0x3297},
|
||||
{0x3299, 0x3299}, {0x1F004, 0x1F004}, {0x1F02C, 0x1F02F},
|
||||
{0x1F094, 0x1F09F}, {0x1F0AF, 0x1F0B0}, {0x1F0C0, 0x1F0C0},
|
||||
{0x1F0CF, 0x1F0D0}, {0x1F0F6, 0x1F0FF}, {0x1F170, 0x1F171},
|
||||
{0x1F17E, 0x1F17F}, {0x1F18E, 0x1F18E}, {0x1F191, 0x1F19A},
|
||||
{0x1F1AE, 0x1F1E5}, {0x1F201, 0x1F20F}, {0x1F21A, 0x1F21A},
|
||||
{0x1F22F, 0x1F22F}, {0x1F232, 0x1F23A}, {0x1F23C, 0x1F23F},
|
||||
{0x1F249, 0x1F25F}, {0x1F266, 0x1F321}, {0x1F324, 0x1F393},
|
||||
{0x1F396, 0x1F397}, {0x1F399, 0x1F39B}, {0x1F39E, 0x1F3F0},
|
||||
{0x1F3F3, 0x1F3F5}, {0x1F3F7, 0x1F3FA}, {0x1F400, 0x1F4FD},
|
||||
{0x1F4FF, 0x1F53D}, {0x1F549, 0x1F54E}, {0x1F550, 0x1F567},
|
||||
{0x1F56F, 0x1F570}, {0x1F573, 0x1F57A}, {0x1F587, 0x1F587},
|
||||
{0x1F58A, 0x1F58D}, {0x1F590, 0x1F590}, {0x1F595, 0x1F596},
|
||||
{0x1F5A4, 0x1F5A5}, {0x1F5A8, 0x1F5A8}, {0x1F5B1, 0x1F5B2},
|
||||
{0x1F5BC, 0x1F5BC}, {0x1F5C2, 0x1F5C4}, {0x1F5D1, 0x1F5D3},
|
||||
{0x1F5DC, 0x1F5DE}, {0x1F5E1, 0x1F5E1}, {0x1F5E3, 0x1F5E3},
|
||||
{0x1F5E8, 0x1F5E8}, {0x1F5EF, 0x1F5EF}, {0x1F5F3, 0x1F5F3},
|
||||
{0x1F5FA, 0x1F64F}, {0x1F680, 0x1F6C5}, {0x1F6CB, 0x1F6D2},
|
||||
{0x1F6D5, 0x1F6E5}, {0x1F6E9, 0x1F6E9}, {0x1F6EB, 0x1F6F0},
|
||||
{0x1F6F3, 0x1F6FF}, {0x1F7DA, 0x1F7FF}, {0x1F80C, 0x1F80F},
|
||||
{0x1F848, 0x1F84F}, {0x1F85A, 0x1F85F}, {0x1F888, 0x1F88F},
|
||||
{0x1F8AE, 0x1F8AF}, {0x1F8BC, 0x1F8BF}, {0x1F8C2, 0x1F8CF},
|
||||
{0x1F8D9, 0x1F8FF}, {0x1F90C, 0x1F93A}, {0x1F93C, 0x1F945},
|
||||
{0x1F947, 0x1F9FF}, {0x1FA58, 0x1FA5F}, {0x1FA6E, 0x1FAFF},
|
||||
{0x1FC00, 0x1FFFD},
|
||||
}
|
||||
|
||||
+5
@@ -73,6 +73,11 @@ type ImageProperties struct {
|
||||
// Required: true
|
||||
Platform ocispec.Platform `json:"Platform"`
|
||||
|
||||
// Identity holds information about the identity and origin of the image.
|
||||
// For image list responses, this can duplicate Build/Pull fields across
|
||||
// image manifests, because those parts of identity are image-level metadata.
|
||||
Identity *Identity `json:"Identity,omitempty"`
|
||||
|
||||
Size struct {
|
||||
// Unpacked is the size (in bytes) of the locally unpacked
|
||||
// (uncompressed) image content that's directly usable by the containers
|
||||
|
||||
+3
@@ -8,5 +8,8 @@ type Error struct {
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
if e == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
return e.Message
|
||||
}
|
||||
|
||||
+22
@@ -1,6 +1,7 @@
|
||||
package swarm
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"net/netip"
|
||||
|
||||
"github.com/moby/moby/api/types/network"
|
||||
@@ -41,6 +42,27 @@ type PortConfig struct {
|
||||
PublishMode PortConfigPublishMode `json:",omitempty"`
|
||||
}
|
||||
|
||||
// Compare returns the lexical ordering of p and other, and can be used
|
||||
// with [slices.SortFunc].
|
||||
//
|
||||
// The comparison is performed in the following priority order:
|
||||
// 1. PublishedPort (host port)
|
||||
// 2. TargetPort (container port)
|
||||
// 3. Protocol
|
||||
// 4. PublishMode
|
||||
func (p PortConfig) Compare(other PortConfig) int {
|
||||
if n := cmp.Compare(p.PublishedPort, other.PublishedPort); n != 0 {
|
||||
return n
|
||||
}
|
||||
if n := cmp.Compare(p.TargetPort, other.TargetPort); n != 0 {
|
||||
return n
|
||||
}
|
||||
if n := cmp.Compare(p.Protocol, other.Protocol); n != 0 {
|
||||
return n
|
||||
}
|
||||
return cmp.Compare(p.PublishMode, other.PublishMode)
|
||||
}
|
||||
|
||||
// PortConfigPublishMode represents the mode in which the port is to
|
||||
// be published.
|
||||
type PortConfigPublishMode string
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user