mirror of
https://gitea.com/Lydanne/buildx.git
synced 2025-08-15 00:05:57 +08:00
Compare commits
50 Commits
v0.22.0-rc
...
v0.23
Author | SHA1 | Date | |
---|---|---|---|
![]() |
28c90eadc4 | ||
![]() |
fddd21c129 | ||
![]() |
8d173d192c | ||
![]() |
f58f0221e2 | ||
![]() |
9696b50d1e | ||
![]() |
15495efa86 | ||
![]() |
bde47313d4 | ||
![]() |
d69301d57b | ||
![]() |
ee77cdb175 | ||
![]() |
8fb1157b5f | ||
![]() |
a34cdff84e | ||
![]() |
77139daa4b | ||
![]() |
10e3892a63 | ||
![]() |
d80ece5bb3 | ||
![]() |
1f44971fc9 | ||
![]() |
a91db7ccc9 | ||
![]() |
98c3abb756 | ||
![]() |
3b824a0e39 | ||
![]() |
b0156cd631 | ||
![]() |
29614f9734 | ||
![]() |
f1b895196c | ||
![]() |
900502b139 | ||
![]() |
49bd7e4edc | ||
![]() |
8f9c25e8b0 | ||
![]() |
7659798f80 | ||
![]() |
7b8bf9f801 | ||
![]() |
8efc528b84 | ||
![]() |
8593e0397b | ||
![]() |
0c0e8eefdf | ||
![]() |
e114dd09a5 | ||
![]() |
d25e260d2e | ||
![]() |
86e4e77ac1 | ||
![]() |
534d9fc276 | ||
![]() |
e0c67bfc79 | ||
![]() |
53e576b306 | ||
![]() |
d3aef6642c | ||
![]() |
824cef1b92 | ||
![]() |
a8b0fa8965 | ||
![]() |
45dfb84361 | ||
![]() |
13ef01196d | ||
![]() |
646df6d4a0 | ||
![]() |
d46c1d8141 | ||
![]() |
c682742de0 | ||
![]() |
391acba718 | ||
![]() |
db4b96e62c | ||
![]() |
882ef0db91 | ||
![]() |
967fc2a696 | ||
![]() |
212d598ab1 | ||
![]() |
bf95aa3dfa | ||
![]() |
a41c9fa649 |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -54,7 +54,7 @@ jobs:
|
||||
- master
|
||||
- latest
|
||||
- buildx-stable-1
|
||||
- v0.20.1
|
||||
- v0.20.2
|
||||
- v0.19.0
|
||||
- v0.18.2
|
||||
worker:
|
||||
|
@@ -1,9 +1,6 @@
|
||||
run:
|
||||
timeout: 30m
|
||||
modules-download-mode: vendor
|
||||
# default uses Go version from the go.mod file, fallback on the env var
|
||||
# `GOVERSION`, fallback on 1.17: https://golangci-lint.run/usage/configuration/#run-configuration
|
||||
go: "1.23"
|
||||
|
||||
linters:
|
||||
enable:
|
||||
|
@@ -11,7 +11,7 @@ ARG DOCKER_VERSION_ALT_26=26.1.3
|
||||
ARG DOCKER_CLI_VERSION=${DOCKER_VERSION}
|
||||
ARG GOTESTSUM_VERSION=v1.12.0
|
||||
ARG REGISTRY_VERSION=2.8.3
|
||||
ARG BUILDKIT_VERSION=v0.20.1
|
||||
ARG BUILDKIT_VERSION=v0.20.2
|
||||
ARG UNDOCK_VERSION=0.9.0
|
||||
|
||||
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
|
||||
|
@@ -21,7 +21,7 @@
|
||||
- [Verify essential information](#verify-essential-information)
|
||||
- [Classify the issue](#classify-the-issue)
|
||||
- [Prioritization guidelines for `kind/bug`](#prioritization-guidelines-for-kindbug)
|
||||
- [Issue lifecyle](#issue-lifecyle)
|
||||
- [Issue lifecycle](#issue-lifecycle)
|
||||
- [Examples](#examples)
|
||||
- [Submitting a bug](#submitting-a-bug)
|
||||
- [Pull request review process](#pull-request-review-process)
|
||||
@@ -308,7 +308,7 @@ Examples:
|
||||
- Bugs in non-default configurations
|
||||
- Most enhancements
|
||||
|
||||
## Issue lifecyle
|
||||
## Issue lifecycle
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
|
41
bake/bake.go
41
bake/bake.go
@@ -3,7 +3,9 @@ package bake
|
||||
import (
|
||||
"context"
|
||||
"encoding"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"maps"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
@@ -732,6 +734,41 @@ type Target struct {
|
||||
linked bool
|
||||
}
|
||||
|
||||
func (t *Target) MarshalJSON() ([]byte, error) {
|
||||
tgt := *t
|
||||
esc := func(s string) string {
|
||||
return strings.ReplaceAll(strings.ReplaceAll(s, "${", "$${"), "%{", "%%{")
|
||||
}
|
||||
|
||||
tgt.Annotations = slices.Clone(t.Annotations)
|
||||
for i, v := range tgt.Annotations {
|
||||
tgt.Annotations[i] = esc(v)
|
||||
}
|
||||
|
||||
if tgt.DockerfileInline != nil {
|
||||
escaped := esc(*tgt.DockerfileInline)
|
||||
tgt.DockerfileInline = &escaped
|
||||
}
|
||||
|
||||
tgt.Labels = maps.Clone(t.Labels)
|
||||
for k, v := range t.Labels {
|
||||
if v != nil {
|
||||
escaped := esc(*v)
|
||||
tgt.Labels[k] = &escaped
|
||||
}
|
||||
}
|
||||
|
||||
tgt.Args = maps.Clone(t.Args)
|
||||
for k, v := range t.Args {
|
||||
if v != nil {
|
||||
escaped := esc(*v)
|
||||
tgt.Args[k] = &escaped
|
||||
}
|
||||
}
|
||||
|
||||
return json.Marshal(tgt)
|
||||
}
|
||||
|
||||
var (
|
||||
_ hclparser.WithEvalContexts = &Target{}
|
||||
_ hclparser.WithGetName = &Target{}
|
||||
@@ -1104,9 +1141,7 @@ func (t *Target) GetEvalContexts(ectx *hcl.EvalContext, block *hcl.Block, loadDe
|
||||
e2 := ectx.NewChild()
|
||||
e2.Variables = make(map[string]cty.Value)
|
||||
if e != ectx {
|
||||
for k, v := range e.Variables {
|
||||
e2.Variables[k] = v
|
||||
}
|
||||
maps.Copy(e2.Variables, e.Variables)
|
||||
}
|
||||
e2.Variables[k] = v
|
||||
ectxs2 = append(ectxs2, e2)
|
||||
|
@@ -2142,6 +2142,73 @@ target "app" {
|
||||
})
|
||||
}
|
||||
|
||||
func TestVariableValidationConditionNull(t *testing.T) {
|
||||
fp := File{
|
||||
Name: "docker-bake.hcl",
|
||||
Data: []byte(`
|
||||
variable "PORT" {
|
||||
default = 3000
|
||||
validation {}
|
||||
}
|
||||
target "app" {
|
||||
args = {
|
||||
PORT = PORT
|
||||
}
|
||||
}
|
||||
`),
|
||||
}
|
||||
|
||||
_, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, nil, nil, &EntitlementConf{})
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "Condition expression must return either true or false, not null")
|
||||
}
|
||||
|
||||
func TestVariableValidationConditionUnknownValue(t *testing.T) {
|
||||
fp := File{
|
||||
Name: "docker-bake.hcl",
|
||||
Data: []byte(`
|
||||
variable "PORT" {
|
||||
default = 3000
|
||||
validation {
|
||||
condition = "foo"
|
||||
}
|
||||
}
|
||||
target "app" {
|
||||
args = {
|
||||
PORT = PORT
|
||||
}
|
||||
}
|
||||
`),
|
||||
}
|
||||
|
||||
_, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, nil, nil, &EntitlementConf{})
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "Invalid condition result value: a bool is required")
|
||||
}
|
||||
|
||||
func TestVariableValidationInvalidErrorMessage(t *testing.T) {
|
||||
fp := File{
|
||||
Name: "docker-bake.hcl",
|
||||
Data: []byte(`
|
||||
variable "FOO" {
|
||||
default = 0
|
||||
validation {
|
||||
condition = FOO > 5
|
||||
}
|
||||
}
|
||||
target "app" {
|
||||
args = {
|
||||
FOO = FOO
|
||||
}
|
||||
}
|
||||
`),
|
||||
}
|
||||
|
||||
_, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, nil, nil, &EntitlementConf{})
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "This check failed, but has an invalid error message")
|
||||
}
|
||||
|
||||
// https://github.com/docker/buildx/issues/2822
|
||||
func TestVariableEmpty(t *testing.T) {
|
||||
fp := File{
|
||||
|
@@ -92,6 +92,9 @@ func ParseCompose(cfgs []composetypes.ConfigFile, envs map[string]string) (*Conf
|
||||
if s.Build.AdditionalContexts != nil {
|
||||
additionalContexts = map[string]string{}
|
||||
for k, v := range s.Build.AdditionalContexts {
|
||||
if strings.HasPrefix(v, "service:") {
|
||||
v = strings.Replace(v, "service:", "target:", 1)
|
||||
}
|
||||
additionalContexts[k] = v
|
||||
}
|
||||
}
|
||||
@@ -174,6 +177,7 @@ func ParseCompose(cfgs []composetypes.ConfigFile, envs map[string]string) (*Conf
|
||||
CacheFrom: cacheFrom,
|
||||
CacheTo: cacheTo,
|
||||
NetworkMode: networkModeP,
|
||||
Platforms: s.Build.Platforms,
|
||||
SSH: ssh,
|
||||
Secrets: secrets,
|
||||
ShmSize: shmSize,
|
||||
@@ -214,7 +218,7 @@ func validateComposeFile(dt []byte, fn string) (bool, error) {
|
||||
}
|
||||
|
||||
func validateCompose(dt []byte, envs map[string]string) error {
|
||||
_, err := loader.Load(composetypes.ConfigDetails{
|
||||
_, err := loader.LoadWithContext(context.Background(), composetypes.ConfigDetails{
|
||||
ConfigFiles: []composetypes.ConfigFile{
|
||||
{
|
||||
Content: dt,
|
||||
|
@@ -463,6 +463,21 @@ services:
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestPlatforms(t *testing.T) {
|
||||
dt := []byte(`
|
||||
services:
|
||||
foo:
|
||||
build:
|
||||
context: .
|
||||
platforms:
|
||||
- linux/amd64
|
||||
- linux/arm64
|
||||
`)
|
||||
c, err := ParseCompose([]composetypes.ConfigFile{{Content: dt}}, nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []string{"linux/amd64", "linux/arm64"}, c.Targets[0].Platforms)
|
||||
}
|
||||
|
||||
func newBool(val bool) *bool {
|
||||
b := val
|
||||
return &b
|
||||
@@ -798,6 +813,37 @@ services:
|
||||
})
|
||||
}
|
||||
|
||||
func TestServiceContext(t *testing.T) {
|
||||
dt := []byte(`
|
||||
services:
|
||||
base:
|
||||
build:
|
||||
dockerfile: baseapp.Dockerfile
|
||||
command: ./entrypoint.sh
|
||||
webapp:
|
||||
build:
|
||||
context: ./dir
|
||||
additional_contexts:
|
||||
base: service:base
|
||||
`)
|
||||
|
||||
c, err := ParseCompose([]composetypes.ConfigFile{{Content: dt}}, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, 1, len(c.Groups))
|
||||
require.Equal(t, "default", c.Groups[0].Name)
|
||||
sort.Strings(c.Groups[0].Targets)
|
||||
require.Equal(t, []string{"base", "webapp"}, c.Groups[0].Targets)
|
||||
|
||||
require.Equal(t, 2, len(c.Targets))
|
||||
sort.Slice(c.Targets, func(i, j int) bool {
|
||||
return c.Targets[i].Name < c.Targets[j].Name
|
||||
})
|
||||
|
||||
require.Equal(t, "webapp", c.Targets[1].Name)
|
||||
require.Equal(t, map[string]string{"base": "target:base"}, c.Targets[1].Contexts)
|
||||
}
|
||||
|
||||
// chdir changes the current working directory to the named directory,
|
||||
// and then restore the original working directory at the end of the test.
|
||||
func chdir(t *testing.T, dir string) {
|
||||
|
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/convert"
|
||||
)
|
||||
|
||||
type Opt struct {
|
||||
@@ -41,7 +42,7 @@ type variableValidation struct {
|
||||
type functionDef struct {
|
||||
Name string `json:"-" hcl:"name,label"`
|
||||
Params *hcl.Attribute `json:"params,omitempty" hcl:"params"`
|
||||
Variadic *hcl.Attribute `json:"variadic_param,omitempty" hcl:"variadic_params"`
|
||||
Variadic *hcl.Attribute `json:"variadic_params,omitempty" hcl:"variadic_params"`
|
||||
Result *hcl.Attribute `json:"result,omitempty" hcl:"result"`
|
||||
}
|
||||
|
||||
@@ -555,27 +556,57 @@ func (p *parser) resolveBlockNames(block *hcl.Block) ([]string, error) {
|
||||
func (p *parser) validateVariables(vars map[string]*variable, ectx *hcl.EvalContext) hcl.Diagnostics {
|
||||
var diags hcl.Diagnostics
|
||||
for _, v := range vars {
|
||||
for _, validation := range v.Validations {
|
||||
condition, condDiags := validation.Condition.Value(ectx)
|
||||
for _, rule := range v.Validations {
|
||||
resultVal, condDiags := rule.Condition.Value(ectx)
|
||||
if condDiags.HasErrors() {
|
||||
diags = append(diags, condDiags...)
|
||||
continue
|
||||
}
|
||||
if !condition.True() {
|
||||
message, msgDiags := validation.ErrorMessage.Value(ectx)
|
||||
|
||||
if resultVal.IsNull() {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid condition result",
|
||||
Detail: "Condition expression must return either true or false, not null.",
|
||||
Subject: rule.Condition.Range().Ptr(),
|
||||
Expression: rule.Condition,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
var err error
|
||||
resultVal, err = convert.Convert(resultVal, cty.Bool)
|
||||
if err != nil {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid condition result",
|
||||
Detail: fmt.Sprintf("Invalid condition result value: %s", err),
|
||||
Subject: rule.Condition.Range().Ptr(),
|
||||
Expression: rule.Condition,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
if !resultVal.True() {
|
||||
message, msgDiags := rule.ErrorMessage.Value(ectx)
|
||||
if msgDiags.HasErrors() {
|
||||
diags = append(diags, msgDiags...)
|
||||
continue
|
||||
}
|
||||
errorMessage := "This check failed, but has an invalid error message."
|
||||
if !message.IsNull() {
|
||||
errorMessage = message.AsString()
|
||||
}
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Validation failed",
|
||||
Detail: message.AsString(),
|
||||
Subject: validation.Condition.Range().Ptr(),
|
||||
Detail: errorMessage,
|
||||
Subject: rule.Condition.Range().Ptr(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return diags
|
||||
}
|
||||
|
||||
|
@@ -8,6 +8,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"maps"
|
||||
"os"
|
||||
"slices"
|
||||
"strconv"
|
||||
@@ -431,9 +432,7 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[
|
||||
FrontendInputs: frontendInputs,
|
||||
FrontendOpt: make(map[string]string),
|
||||
}
|
||||
for k, v := range so.FrontendAttrs {
|
||||
req.FrontendOpt[k] = v
|
||||
}
|
||||
maps.Copy(req.FrontendOpt, so.FrontendAttrs)
|
||||
so.Frontend = ""
|
||||
so.FrontendInputs = nil
|
||||
|
||||
|
@@ -2,6 +2,7 @@ package build
|
||||
|
||||
import (
|
||||
"context"
|
||||
"maps"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
@@ -127,9 +128,7 @@ func getGitAttributes(ctx context.Context, contextPath, dockerfilePath string) (
|
||||
if so.FrontendAttrs == nil {
|
||||
so.FrontendAttrs = make(map[string]string)
|
||||
}
|
||||
for k, v := range res {
|
||||
so.FrontendAttrs[k] = v
|
||||
}
|
||||
maps.Copy(so.FrontendAttrs, res)
|
||||
|
||||
if !setGitInfo || root == "" {
|
||||
return
|
||||
|
@@ -9,6 +9,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/buildx/util/gitutil"
|
||||
"github.com/docker/buildx/util/gitutil/gittestutil"
|
||||
"github.com/moby/buildkit/client"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -16,18 +17,18 @@ import (
|
||||
)
|
||||
|
||||
func setupTest(tb testing.TB) {
|
||||
gitutil.Mktmp(tb)
|
||||
gittestutil.Mktmp(tb)
|
||||
|
||||
c, err := gitutil.New()
|
||||
require.NoError(tb, err)
|
||||
gitutil.GitInit(c, tb)
|
||||
gittestutil.GitInit(c, tb)
|
||||
|
||||
df := []byte("FROM alpine:latest\n")
|
||||
require.NoError(tb, os.WriteFile("Dockerfile", df, 0644))
|
||||
|
||||
gitutil.GitAdd(c, tb, "Dockerfile")
|
||||
gitutil.GitCommit(c, tb, "initial commit")
|
||||
gitutil.GitSetRemote(c, tb, "origin", "git@github.com:docker/buildx.git")
|
||||
gittestutil.GitAdd(c, tb, "Dockerfile")
|
||||
gittestutil.GitCommit(c, tb, "initial commit")
|
||||
gittestutil.GitSetRemote(c, tb, "origin", "git@github.com:docker/buildx.git")
|
||||
}
|
||||
|
||||
func TestGetGitAttributesNotGitRepo(t *testing.T) {
|
||||
@@ -188,19 +189,19 @@ func TestLocalDirs(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLocalDirsSub(t *testing.T) {
|
||||
gitutil.Mktmp(t)
|
||||
gittestutil.Mktmp(t)
|
||||
|
||||
c, err := gitutil.New()
|
||||
require.NoError(t, err)
|
||||
gitutil.GitInit(c, t)
|
||||
gittestutil.GitInit(c, t)
|
||||
|
||||
df := []byte("FROM alpine:latest\n")
|
||||
require.NoError(t, os.MkdirAll("app", 0755))
|
||||
require.NoError(t, os.WriteFile("app/Dockerfile", df, 0644))
|
||||
|
||||
gitutil.GitAdd(c, t, "app/Dockerfile")
|
||||
gitutil.GitCommit(c, t, "initial commit")
|
||||
gitutil.GitSetRemote(c, t, "origin", "git@github.com:docker/buildx.git")
|
||||
gittestutil.GitAdd(c, t, "app/Dockerfile")
|
||||
gittestutil.GitCommit(c, t, "initial commit")
|
||||
gittestutil.GitSetRemote(c, t, "origin", "git@github.com:docker/buildx.git")
|
||||
|
||||
so := &client.SolveOpt{
|
||||
FrontendAttrs: map[string]string{},
|
||||
|
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"maps"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
@@ -40,9 +41,7 @@ func setRecordProvenance(ctx context.Context, c *client.Client, sr *client.Solve
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for k, v := range res {
|
||||
sr.ExporterResponse[k] = v
|
||||
}
|
||||
maps.Copy(sr.ExporterResponse, res)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
@@ -11,11 +11,10 @@ import (
|
||||
"github.com/docker/buildx/util/desktop"
|
||||
"github.com/docker/buildx/version"
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli-plugins/manager"
|
||||
"github.com/docker/cli/cli-plugins/metadata"
|
||||
"github.com/docker/cli/cli-plugins/plugin"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/debug"
|
||||
cliflags "github.com/docker/cli/cli/flags"
|
||||
"github.com/moby/buildkit/solver/errdefs"
|
||||
"github.com/moby/buildkit/util/stack"
|
||||
"github.com/pkg/errors"
|
||||
@@ -37,11 +36,7 @@ func init() {
|
||||
}
|
||||
|
||||
func runStandalone(cmd *command.DockerCli) error {
|
||||
if err := cmd.Initialize(cliflags.NewClientOptions()); err != nil {
|
||||
return err
|
||||
}
|
||||
defer flushMetrics(cmd)
|
||||
|
||||
executable := os.Args[0]
|
||||
rootCmd := commands.NewRootCmd(filepath.Base(executable), false, cmd)
|
||||
return rootCmd.Execute()
|
||||
@@ -64,7 +59,7 @@ func flushMetrics(cmd *command.DockerCli) {
|
||||
|
||||
func runPlugin(cmd *command.DockerCli) error {
|
||||
rootCmd := commands.NewRootCmd("buildx", true, cmd)
|
||||
return plugin.RunPlugin(cmd, rootCmd, manager.Metadata{
|
||||
return plugin.RunPlugin(cmd, rootCmd, metadata.Metadata{
|
||||
SchemaVersion: "0.1.0",
|
||||
Vendor: "Docker Inc.",
|
||||
Version: version.Version,
|
||||
|
@@ -424,6 +424,14 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba
|
||||
fmt.Fprintln(dockerCli.Out(), string(dt))
|
||||
}
|
||||
|
||||
for _, name := range names {
|
||||
if sp, ok := resp[name]; ok {
|
||||
if v, ok := sp.ExporterResponse["frontend.result.inlinemessage"]; ok {
|
||||
fmt.Fprintf(dockerCli.Out(), "\n# %s\n%s\n", name, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if exitCode != 0 {
|
||||
os.Exit(exitCode)
|
||||
}
|
||||
|
@@ -404,6 +404,10 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions)
|
||||
os.Exit(exitcode)
|
||||
}
|
||||
}
|
||||
if v, ok := resp.ExporterResponse["frontend.result.inlinemessage"]; ok {
|
||||
fmt.Fprintf(dockerCli.Out(), "\n%s\n", v)
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
160
commands/history/export.go
Normal file
160
commands/history/export.go
Normal file
@@ -0,0 +1,160 @@
|
||||
package history
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"slices"
|
||||
|
||||
"github.com/containerd/console"
|
||||
"github.com/containerd/platforms"
|
||||
"github.com/docker/buildx/builder"
|
||||
"github.com/docker/buildx/localstate"
|
||||
"github.com/docker/buildx/util/cobrautil/completion"
|
||||
"github.com/docker/buildx/util/confutil"
|
||||
"github.com/docker/buildx/util/desktop/bundle"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/moby/buildkit/client"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type exportOptions struct {
|
||||
builder string
|
||||
refs []string
|
||||
output string
|
||||
all bool
|
||||
}
|
||||
|
||||
func runExport(ctx context.Context, dockerCli command.Cli, opts exportOptions) error {
|
||||
b, err := builder.New(dockerCli, builder.WithName(opts.builder))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nodes, err := b.LoadNodes(ctx, builder.WithData())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, node := range nodes {
|
||||
if node.Err != nil {
|
||||
return node.Err
|
||||
}
|
||||
}
|
||||
|
||||
if len(opts.refs) == 0 {
|
||||
opts.refs = []string{""}
|
||||
}
|
||||
|
||||
var res []historyRecord
|
||||
for _, ref := range opts.refs {
|
||||
recs, err := queryRecords(ctx, ref, nodes, &queryOptions{
|
||||
CompletedOnly: true,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(recs) == 0 {
|
||||
if ref == "" {
|
||||
return errors.New("no records found")
|
||||
}
|
||||
return errors.Errorf("no record found for ref %q", ref)
|
||||
}
|
||||
|
||||
if ref == "" {
|
||||
slices.SortFunc(recs, func(a, b historyRecord) int {
|
||||
return b.CreatedAt.AsTime().Compare(a.CreatedAt.AsTime())
|
||||
})
|
||||
}
|
||||
|
||||
if opts.all {
|
||||
res = append(res, recs...)
|
||||
break
|
||||
} else {
|
||||
res = append(res, recs[0])
|
||||
}
|
||||
}
|
||||
|
||||
ls, err := localstate.New(confutil.NewConfig(dockerCli))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
visited := map[*builder.Node]struct{}{}
|
||||
var clients []*client.Client
|
||||
for _, rec := range res {
|
||||
if _, ok := visited[rec.node]; ok {
|
||||
continue
|
||||
}
|
||||
c, err := rec.node.Driver.Client(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
clients = append(clients, c)
|
||||
}
|
||||
|
||||
toExport := make([]*bundle.Record, 0, len(res))
|
||||
for _, rec := range res {
|
||||
var defaultPlatform string
|
||||
if p := rec.node.Platforms; len(p) > 0 {
|
||||
defaultPlatform = platforms.FormatAll(platforms.Normalize(p[0]))
|
||||
}
|
||||
|
||||
var stg *localstate.StateGroup
|
||||
st, _ := ls.ReadRef(rec.node.Builder, rec.node.Name, rec.Ref)
|
||||
if st != nil && st.GroupRef != "" {
|
||||
stg, err = ls.ReadGroup(st.GroupRef)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
toExport = append(toExport, &bundle.Record{
|
||||
BuildHistoryRecord: rec.BuildHistoryRecord,
|
||||
DefaultPlatform: defaultPlatform,
|
||||
LocalState: st,
|
||||
StateGroup: stg,
|
||||
})
|
||||
}
|
||||
|
||||
var w io.Writer = os.Stdout
|
||||
if opts.output != "" {
|
||||
f, err := os.Create(opts.output)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to create output file %q", opts.output)
|
||||
}
|
||||
defer f.Close()
|
||||
w = f
|
||||
} else {
|
||||
if _, err := console.ConsoleFromFile(os.Stdout); err == nil {
|
||||
return errors.Errorf("refusing to write to console, use --output to specify a file")
|
||||
}
|
||||
}
|
||||
|
||||
return bundle.Export(ctx, clients, w, toExport)
|
||||
}
|
||||
|
||||
func exportCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
|
||||
var options exportOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "export [OPTIONS] [REF]",
|
||||
Short: "Export a build into Docker Desktop bundle",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if options.all && len(args) > 0 {
|
||||
return errors.New("cannot specify refs when using --all")
|
||||
}
|
||||
options.refs = args
|
||||
options.builder = *rootOpts.Builder
|
||||
return runExport(cmd.Context(), dockerCli, options)
|
||||
},
|
||||
ValidArgsFunction: completion.Disable,
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.StringVarP(&options.output, "output", "o", "", "Output file path")
|
||||
flags.BoolVar(&options.all, "all", false, "Export all records for the builder")
|
||||
|
||||
return cmd
|
||||
}
|
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
@@ -14,10 +15,12 @@ import (
|
||||
"github.com/docker/buildx/util/cobrautil/completion"
|
||||
"github.com/docker/buildx/util/confutil"
|
||||
"github.com/docker/buildx/util/desktop"
|
||||
"github.com/docker/buildx/util/gitutil"
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/formatter"
|
||||
"github.com/docker/go-units"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -38,6 +41,9 @@ type lsOptions struct {
|
||||
builder string
|
||||
format string
|
||||
noTrunc bool
|
||||
|
||||
filters []string
|
||||
local bool
|
||||
}
|
||||
|
||||
func runLs(ctx context.Context, dockerCli command.Cli, opts lsOptions) error {
|
||||
@@ -56,7 +62,29 @@ func runLs(ctx context.Context, dockerCli command.Cli, opts lsOptions) error {
|
||||
}
|
||||
}
|
||||
|
||||
out, err := queryRecords(ctx, "", nodes, nil)
|
||||
queryOptions := &queryOptions{}
|
||||
|
||||
if opts.local {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
gitc, err := gitutil.New(gitutil.WithContext(ctx), gitutil.WithWorkingDir(wd))
|
||||
if err != nil {
|
||||
if st, err1 := os.Stat(path.Join(wd, ".git")); err1 == nil && st.IsDir() {
|
||||
return errors.Wrap(err, "git was not found in the system")
|
||||
}
|
||||
return errors.Wrapf(err, "could not find git repository for local filter")
|
||||
}
|
||||
remote, err := gitc.RemoteURL()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not get remote URL for local filter")
|
||||
}
|
||||
queryOptions.Filters = append(queryOptions.Filters, fmt.Sprintf("repository=%s", remote))
|
||||
}
|
||||
queryOptions.Filters = append(queryOptions.Filters, opts.filters...)
|
||||
|
||||
out, err := queryRecords(ctx, "", nodes, queryOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -92,6 +120,8 @@ func lsCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
|
||||
flags := cmd.Flags()
|
||||
flags.StringVar(&options.format, "format", formatter.TableFormatKey, "Format the output")
|
||||
flags.BoolVar(&options.noTrunc, "no-trunc", false, "Don't truncate output")
|
||||
flags.StringArrayVar(&options.filters, "filter", nil, `Provide filter values (e.g., "status=error")`)
|
||||
flags.BoolVar(&options.local, "local", false, "List records for current repository only")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
@@ -26,6 +26,7 @@ func RootCmd(rootcmd *cobra.Command, dockerCli command.Cli, opts RootOptions) *c
|
||||
openCmd(dockerCli, opts),
|
||||
traceCmd(dockerCli, opts),
|
||||
importCmd(dockerCli, opts),
|
||||
exportCmd(dockerCli, opts),
|
||||
)
|
||||
|
||||
return cmd
|
||||
|
@@ -1,7 +1,9 @@
|
||||
package history
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/csv"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
@@ -15,10 +17,13 @@ import (
|
||||
"github.com/docker/buildx/builder"
|
||||
"github.com/docker/buildx/localstate"
|
||||
controlapi "github.com/moby/buildkit/api/services/control"
|
||||
"github.com/moby/buildkit/util/gitutil"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
const recordsLimit = 50
|
||||
|
||||
func buildName(fattrs map[string]string, ls *localstate.State) string {
|
||||
var res string
|
||||
|
||||
@@ -110,6 +115,7 @@ type historyRecord struct {
|
||||
|
||||
type queryOptions struct {
|
||||
CompletedOnly bool
|
||||
Filters []string
|
||||
}
|
||||
|
||||
func queryRecords(ctx context.Context, ref string, nodes []builder.Node, opts *queryOptions) ([]historyRecord, error) {
|
||||
@@ -126,6 +132,11 @@ func queryRecords(ctx context.Context, ref string, nodes []builder.Node, opts *q
|
||||
ref = ""
|
||||
}
|
||||
|
||||
var filters []string
|
||||
if opts != nil {
|
||||
filters = opts.Filters
|
||||
}
|
||||
|
||||
eg, ctx := errgroup.WithContext(ctx)
|
||||
for _, node := range nodes {
|
||||
node := node
|
||||
@@ -138,9 +149,25 @@ func queryRecords(ctx context.Context, ref string, nodes []builder.Node, opts *q
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var matchers []matchFunc
|
||||
if len(filters) > 0 {
|
||||
filters, matchers, err = dockerFiltersToBuildkit(filters)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sb := bytes.NewBuffer(nil)
|
||||
w := csv.NewWriter(sb)
|
||||
w.Write(filters)
|
||||
w.Flush()
|
||||
filters = []string{strings.TrimSuffix(sb.String(), "\n")}
|
||||
}
|
||||
|
||||
serv, err := c.ControlClient().ListenBuildHistory(ctx, &controlapi.BuildHistoryRequest{
|
||||
EarlyExit: true,
|
||||
Ref: ref,
|
||||
Limit: recordsLimit,
|
||||
Filter: filters,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -158,6 +185,7 @@ func queryRecords(ctx context.Context, ref string, nodes []builder.Node, opts *q
|
||||
ts = &t
|
||||
}
|
||||
defer serv.CloseSend()
|
||||
loop0:
|
||||
for {
|
||||
he, err := serv.Recv()
|
||||
if err != nil {
|
||||
@@ -173,6 +201,13 @@ func queryRecords(ctx context.Context, ref string, nodes []builder.Node, opts *q
|
||||
continue
|
||||
}
|
||||
|
||||
// for older buildkit that don't support filters apply local filters
|
||||
for _, matcher := range matchers {
|
||||
if !matcher(he.Record) {
|
||||
continue loop0
|
||||
}
|
||||
}
|
||||
|
||||
records = append(records, historyRecord{
|
||||
BuildHistoryRecord: he.Record,
|
||||
currentTimestamp: ts,
|
||||
@@ -219,3 +254,150 @@ func formatDuration(d time.Duration) string {
|
||||
}
|
||||
return fmt.Sprintf("%dm %2ds", int(d.Minutes()), int(d.Seconds())%60)
|
||||
}
|
||||
|
||||
type matchFunc func(*controlapi.BuildHistoryRecord) bool
|
||||
|
||||
func dockerFiltersToBuildkit(in []string) ([]string, []matchFunc, error) {
|
||||
out := []string{}
|
||||
matchers := []matchFunc{}
|
||||
for _, f := range in {
|
||||
key, value, sep, found := cutAny(f, "!=", "=", "<=", "<", ">=", ">")
|
||||
if !found {
|
||||
return nil, nil, errors.Errorf("invalid filter %q", f)
|
||||
}
|
||||
switch key {
|
||||
case "ref", "repository", "status":
|
||||
if sep != "=" && sep != "!=" {
|
||||
return nil, nil, errors.Errorf("invalid separator for %q, expected = or !=", f)
|
||||
}
|
||||
matchers = append(matchers, valueFiler(key, value, sep))
|
||||
if sep == "=" {
|
||||
if key == "status" {
|
||||
sep = "=="
|
||||
} else {
|
||||
sep = "~="
|
||||
}
|
||||
}
|
||||
case "startedAt", "completedAt", "duration":
|
||||
if sep == "=" || sep == "!=" {
|
||||
return nil, nil, errors.Errorf("invalid separator for %q, expected <=, <, >= or >", f)
|
||||
}
|
||||
matcher, err := timeBasedFilter(key, value, sep)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
matchers = append(matchers, matcher)
|
||||
default:
|
||||
return nil, nil, errors.Errorf("unsupported filter %q", f)
|
||||
}
|
||||
out = append(out, key+sep+value)
|
||||
}
|
||||
return out, matchers, nil
|
||||
}
|
||||
|
||||
func valueFiler(key, value, sep string) matchFunc {
|
||||
return func(rec *controlapi.BuildHistoryRecord) bool {
|
||||
var recValue string
|
||||
switch key {
|
||||
case "ref":
|
||||
recValue = rec.Ref
|
||||
case "repository":
|
||||
v, ok := rec.FrontendAttrs["vcs:source"]
|
||||
if ok {
|
||||
recValue = v
|
||||
} else {
|
||||
if context, ok := rec.FrontendAttrs["context"]; ok {
|
||||
if ref, err := gitutil.ParseGitRef(context); err == nil {
|
||||
recValue = ref.Remote
|
||||
}
|
||||
}
|
||||
}
|
||||
case "status":
|
||||
if rec.CompletedAt != nil {
|
||||
if rec.Error != nil {
|
||||
if strings.Contains(rec.Error.Message, "context canceled") {
|
||||
recValue = "canceled"
|
||||
} else {
|
||||
recValue = "error"
|
||||
}
|
||||
} else {
|
||||
recValue = "completed"
|
||||
}
|
||||
} else {
|
||||
recValue = "running"
|
||||
}
|
||||
}
|
||||
switch sep {
|
||||
case "=":
|
||||
if key == "status" {
|
||||
return recValue == value
|
||||
}
|
||||
return strings.Contains(recValue, value)
|
||||
case "!=":
|
||||
return recValue != value
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func timeBasedFilter(key, value, sep string) (matchFunc, error) {
|
||||
var cmp int64
|
||||
switch key {
|
||||
case "startedAt", "completedAt":
|
||||
v, err := time.ParseDuration(value)
|
||||
if err == nil {
|
||||
tm := time.Now().Add(-v)
|
||||
cmp = tm.Unix()
|
||||
} else {
|
||||
tm, err := time.Parse(time.RFC3339, value)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("invalid time %s", value)
|
||||
}
|
||||
cmp = tm.Unix()
|
||||
}
|
||||
case "duration":
|
||||
v, err := time.ParseDuration(value)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("invalid duration %s", value)
|
||||
}
|
||||
cmp = int64(v)
|
||||
default:
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return func(rec *controlapi.BuildHistoryRecord) bool {
|
||||
var val int64
|
||||
switch key {
|
||||
case "startedAt":
|
||||
val = rec.CreatedAt.AsTime().Unix()
|
||||
case "completedAt":
|
||||
if rec.CompletedAt != nil {
|
||||
val = rec.CompletedAt.AsTime().Unix()
|
||||
}
|
||||
case "duration":
|
||||
if rec.CompletedAt != nil {
|
||||
val = int64(rec.CompletedAt.AsTime().Sub(rec.CreatedAt.AsTime()))
|
||||
}
|
||||
}
|
||||
switch sep {
|
||||
case ">=":
|
||||
return val >= cmp
|
||||
case "<=":
|
||||
return val <= cmp
|
||||
case ">":
|
||||
return val > cmp
|
||||
default:
|
||||
return val < cmp
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
|
||||
func cutAny(s string, seps ...string) (before, after, sep string, found bool) {
|
||||
for _, sep := range seps {
|
||||
if idx := strings.Index(s, sep); idx != -1 {
|
||||
return s[:idx], s[idx+len(sep):], sep, true
|
||||
}
|
||||
}
|
||||
return s, "", "", false
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"maps"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -409,9 +410,7 @@ func truncPlatforms(pfs []string, max int) truncatedPlatforms {
|
||||
left[ppf] = append(left[ppf], pf)
|
||||
}
|
||||
}
|
||||
for k, v := range left {
|
||||
res[k] = v
|
||||
}
|
||||
maps.Copy(res, left)
|
||||
return truncatedPlatforms{
|
||||
res: res,
|
||||
input: pfs,
|
||||
|
@@ -16,13 +16,14 @@ import (
|
||||
"github.com/docker/cli/cli-plugins/plugin"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/debug"
|
||||
cliflags "github.com/docker/cli/cli/flags"
|
||||
"github.com/moby/buildkit/util/appcontext"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func NewRootCmd(name string, isPlugin bool, dockerCli command.Cli) *cobra.Command {
|
||||
func NewRootCmd(name string, isPlugin bool, dockerCli *command.DockerCli) *cobra.Command {
|
||||
var opt rootOptions
|
||||
cmd := &cobra.Command{
|
||||
Short: "Docker Buildx",
|
||||
@@ -40,7 +41,17 @@ func NewRootCmd(name string, isPlugin bool, dockerCli command.Cli) *cobra.Comman
|
||||
}
|
||||
cmd.SetContext(appcontext.Context())
|
||||
if !isPlugin {
|
||||
return nil
|
||||
// InstallFlags and SetDefaultOptions are necessary to match
|
||||
// the plugin mode behavior to handle env vars such as
|
||||
// DOCKER_TLS, DOCKER_TLS_VERIFY, ... and we also need to use a
|
||||
// new flagset to avoid conflict with the global debug flag
|
||||
// that we already handle in the root command otherwise it
|
||||
// would panic.
|
||||
nflags := pflag.NewFlagSet(cmd.DisplayName(), pflag.ContinueOnError)
|
||||
options := cliflags.NewClientOptions()
|
||||
options.InstallFlags(nflags)
|
||||
options.SetDefaultOptions(nflags)
|
||||
return dockerCli.Initialize(options)
|
||||
}
|
||||
return plugin.PersistentPreRunE(cmd, args)
|
||||
},
|
||||
|
@@ -1,6 +1,10 @@
|
||||
package pb
|
||||
|
||||
import "github.com/moby/buildkit/client"
|
||||
import (
|
||||
"maps"
|
||||
|
||||
"github.com/moby/buildkit/client"
|
||||
)
|
||||
|
||||
func CreateCaches(entries []*CacheOptionsEntry) []client.CacheOptionsEntry {
|
||||
var outs []client.CacheOptionsEntry
|
||||
@@ -12,9 +16,7 @@ func CreateCaches(entries []*CacheOptionsEntry) []client.CacheOptionsEntry {
|
||||
Type: entry.Type,
|
||||
Attrs: map[string]string{},
|
||||
}
|
||||
for k, v := range entry.Attrs {
|
||||
out.Attrs[k] = v
|
||||
}
|
||||
maps.Copy(out.Attrs, entry.Attrs)
|
||||
outs = append(outs, out)
|
||||
}
|
||||
return outs
|
||||
|
@@ -2,6 +2,7 @@ package pb
|
||||
|
||||
import (
|
||||
"io"
|
||||
"maps"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
@@ -26,9 +27,7 @@ func CreateExports(entries []*ExportEntry) ([]client.ExportEntry, []string, erro
|
||||
Type: entry.Type,
|
||||
Attrs: map[string]string{},
|
||||
}
|
||||
for k, v := range entry.Attrs {
|
||||
out.Attrs[k] = v
|
||||
}
|
||||
maps.Copy(out.Attrs, entry.Attrs)
|
||||
|
||||
supportFile := false
|
||||
supportDir := false
|
||||
|
@@ -944,7 +944,7 @@ $ docker buildx build --secret [type=file,]id=<ID>[,src=<FILEPATH>] .
|
||||
###### `type=file` usage
|
||||
|
||||
In the following example, `type=file` is automatically detected because no
|
||||
environment variable mathing `aws` (the ID) is set.
|
||||
environment variable matching `aws` (the ID) is set.
|
||||
|
||||
```console
|
||||
$ docker buildx build --secret id=aws,src=$HOME/.aws/credentials .
|
||||
|
@@ -7,6 +7,7 @@ Commands to work on build records
|
||||
|
||||
| Name | Description |
|
||||
|:---------------------------------------|:-----------------------------------------------|
|
||||
| [`export`](buildx_history_export.md) | Export a build into Docker Desktop bundle |
|
||||
| [`import`](buildx_history_import.md) | Import a build into Docker Desktop |
|
||||
| [`inspect`](buildx_history_inspect.md) | Inspect a build |
|
||||
| [`logs`](buildx_history_logs.md) | Print the logs of a build |
|
||||
|
17
docs/reference/buildx_history_export.md
Normal file
17
docs/reference/buildx_history_export.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# docker buildx history export
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
Export a build into Docker Desktop bundle
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:-----------------|:---------|:--------|:-----------------------------------------|
|
||||
| `--all` | `bool` | | Export all records for the builder |
|
||||
| `--builder` | `string` | | Override the configured builder instance |
|
||||
| `-D`, `--debug` | `bool` | | Enable debug logging |
|
||||
| `-o`, `--output` | `string` | | Output file path |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
@@ -5,12 +5,14 @@ List build records
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:----------------|:---------|:--------|:-----------------------------------------|
|
||||
| `--builder` | `string` | | Override the configured builder instance |
|
||||
| `-D`, `--debug` | `bool` | | Enable debug logging |
|
||||
| `--format` | `string` | `table` | Format the output |
|
||||
| `--no-trunc` | `bool` | | Don't truncate output |
|
||||
| Name | Type | Default | Description |
|
||||
|:----------------|:--------------|:--------|:---------------------------------------------|
|
||||
| `--builder` | `string` | | Override the configured builder instance |
|
||||
| `-D`, `--debug` | `bool` | | Enable debug logging |
|
||||
| `--filter` | `stringArray` | | Provide filter values (e.g., `status=error`) |
|
||||
| `--format` | `string` | `table` | Format the output |
|
||||
| `--local` | `bool` | | List records for current repository only |
|
||||
| `--no-trunc` | `bool` | | Don't truncate output |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
@@ -28,7 +28,7 @@ type Driver struct {
|
||||
*tlsOpts
|
||||
defaultLoad bool
|
||||
|
||||
// remote driver caches the client because its Bootstap/Info methods reuse it internally
|
||||
// remote driver caches the client because its Bootstrap/Info methods reuse it internally
|
||||
clientOnce sync.Once
|
||||
client *client.Client
|
||||
err error
|
||||
|
57
go.mod
57
go.mod
@@ -1,14 +1,14 @@
|
||||
module github.com/docker/buildx
|
||||
|
||||
go 1.22.0
|
||||
go 1.23.0
|
||||
|
||||
require (
|
||||
github.com/Masterminds/semver/v3 v3.2.1
|
||||
github.com/Microsoft/go-winio v0.6.2
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.27
|
||||
github.com/compose-spec/compose-go/v2 v2.4.8
|
||||
github.com/compose-spec/compose-go/v2 v2.6.0
|
||||
github.com/containerd/console v1.0.4
|
||||
github.com/containerd/containerd/v2 v2.0.3
|
||||
github.com/containerd/containerd/v2 v2.0.4
|
||||
github.com/containerd/continuity v0.4.5
|
||||
github.com/containerd/errdefs v1.0.0
|
||||
github.com/containerd/log v0.1.0
|
||||
@@ -17,9 +17,9 @@ require (
|
||||
github.com/creack/pty v1.1.24
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/distribution/reference v0.6.0
|
||||
github.com/docker/cli v28.0.1+incompatible
|
||||
github.com/docker/cli v28.0.4+incompatible
|
||||
github.com/docker/cli-docs-tool v0.9.0
|
||||
github.com/docker/docker v28.0.1+incompatible
|
||||
github.com/docker/docker v28.0.4+incompatible
|
||||
github.com/docker/go-units v0.5.0
|
||||
github.com/gofrs/flock v0.12.1
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||
@@ -29,35 +29,35 @@ require (
|
||||
github.com/hashicorp/hcl/v2 v2.23.0
|
||||
github.com/in-toto/in-toto-golang v0.5.0
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2
|
||||
github.com/moby/buildkit v0.20.1
|
||||
github.com/moby/buildkit v0.21.0
|
||||
github.com/moby/sys/mountinfo v0.7.2
|
||||
github.com/moby/sys/signal v0.7.1
|
||||
github.com/morikuni/aec v1.0.0
|
||||
github.com/opencontainers/go-digest v1.0.0
|
||||
github.com/opencontainers/image-spec v1.1.0
|
||||
github.com/opencontainers/image-spec v1.1.1
|
||||
github.com/pelletier/go-toml v1.9.5
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10
|
||||
github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/cobra v1.8.1
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/spf13/pflag v1.0.6
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/tonistiigi/fsutil v0.0.0-20250113203817-b14e27f4135a
|
||||
github.com/tonistiigi/fsutil v0.0.0-20250410151801-5b74a7ad7583
|
||||
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4
|
||||
github.com/tonistiigi/jaeger-ui-rest v0.0.0-20250211190051-7d4944a45bb6
|
||||
github.com/tonistiigi/jaeger-ui-rest v0.0.0-20250408171107-3dd17559e117
|
||||
github.com/zclconf/go-cty v1.16.0
|
||||
go.opentelemetry.io/otel v1.31.0
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.31.0
|
||||
go.opentelemetry.io/otel/metric v1.31.0
|
||||
go.opentelemetry.io/otel/sdk v1.31.0
|
||||
go.opentelemetry.io/otel/trace v1.31.0
|
||||
golang.org/x/mod v0.22.0
|
||||
golang.org/x/sync v0.10.0
|
||||
golang.org/x/sys v0.29.0
|
||||
golang.org/x/term v0.27.0
|
||||
golang.org/x/text v0.21.0
|
||||
golang.org/x/mod v0.24.0
|
||||
golang.org/x/sync v0.13.0
|
||||
golang.org/x/sys v0.32.0
|
||||
golang.org/x/term v0.31.0
|
||||
golang.org/x/text v0.24.0
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38
|
||||
google.golang.org/grpc v1.69.4
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1
|
||||
@@ -71,7 +71,6 @@ require (
|
||||
require (
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
|
||||
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d // indirect
|
||||
github.com/agext/levenshtein v1.2.3 // indirect
|
||||
github.com/apparentlymart/go-cidr v1.0.1 // indirect
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
|
||||
@@ -95,10 +94,10 @@ require (
|
||||
github.com/containerd/ttrpc v1.2.7 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
|
||||
github.com/docker/distribution v2.8.3+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.8.2 // indirect
|
||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect
|
||||
github.com/docker/docker-credential-helpers v0.9.3 // indirect
|
||||
github.com/docker/go-connections v0.5.0 // indirect
|
||||
github.com/docker/go-metrics v0.0.1 // indirect
|
||||
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fvbommel/sortorder v1.0.1 // indirect
|
||||
@@ -112,7 +111,7 @@ require (
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/gnostic-models v0.6.8 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
@@ -123,19 +122,17 @@ require (
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.17.11 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/mattn/go-shellwords v1.0.12 // indirect
|
||||
github.com/miekg/pkcs11 v1.1.1 // indirect
|
||||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/locker v1.0.1 // indirect
|
||||
github.com/moby/patternmatcher v0.6.0 // indirect
|
||||
github.com/moby/spdystream v0.4.0 // indirect
|
||||
github.com/moby/sys/sequential v0.6.0 // indirect
|
||||
github.com/moby/sys/user v0.3.0 // indirect
|
||||
github.com/moby/sys/user v0.4.0 // indirect
|
||||
github.com/moby/sys/userns v0.1.0 // indirect
|
||||
github.com/moby/term v0.5.2 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
@@ -152,13 +149,14 @@ require (
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.4.0 // indirect
|
||||
github.com/shibumi/go-pathspec v1.3.0 // indirect
|
||||
github.com/theupdateframework/notary v0.7.0 // indirect
|
||||
github.com/tonistiigi/dchapes-mode v0.0.0-20241001053921-ca0759fec205 // indirect
|
||||
github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 // indirect
|
||||
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect
|
||||
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 // indirect
|
||||
@@ -169,12 +167,11 @@ require (
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.31.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
|
||||
golang.org/x/crypto v0.31.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect
|
||||
golang.org/x/net v0.33.0 // indirect
|
||||
golang.org/x/crypto v0.37.0 // indirect
|
||||
golang.org/x/net v0.39.0 // indirect
|
||||
golang.org/x/oauth2 v0.23.0 // indirect
|
||||
golang.org/x/time v0.6.0 // indirect
|
||||
golang.org/x/tools v0.27.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
golang.org/x/tools v0.32.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241021214115-324edc3d5d38 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
|
122
go.sum
122
go.sum
@@ -4,7 +4,6 @@ github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2 h1:dIS
|
||||
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2/go.mod h1:gCLVsLfv1egrcZu+GoJATN5ts75F2s62ih/457eWzOw=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
|
||||
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||
@@ -13,8 +12,6 @@ github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA
|
||||
github.com/Microsoft/hcsshim v0.12.9 h1:2zJy5KA+l0loz1HzEGqyNnjd3fyZA31ZBCGKacp6lLg=
|
||||
github.com/Microsoft/hcsshim v0.12.9/go.mod h1:fJ0gkFAna6ukt0bLdKB8djt4XIJhF/vEPuoIWYVvZ8Y=
|
||||
github.com/Shopify/logrus-bugsnag v0.0.0-20170309145241-6dbc35f2c30d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
|
||||
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs=
|
||||
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
|
||||
github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo=
|
||||
github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
@@ -63,30 +60,26 @@ github.com/bitly/go-hostpool v0.1.0/go.mod h1:4gOCgp6+NZnVqlKyZ/iBZFTAJKembaVENU
|
||||
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
|
||||
github.com/bmatcuk/doublestar v1.1.5/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE=
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
||||
github.com/bugsnag/bugsnag-go v1.0.5-0.20150529004307-13fd6b8acda0 h1:s7+5BfS4WFJoVF9pnB8kBk03S7pZXRdKamnV0FOl5Sc=
|
||||
github.com/bugsnag/bugsnag-go v1.0.5-0.20150529004307-13fd6b8acda0/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
|
||||
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ=
|
||||
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
|
||||
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o=
|
||||
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004 h1:lkAMpLVBDaj17e85keuznYcH5rqI438v41pKcBl4ZxQ=
|
||||
github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
|
||||
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE=
|
||||
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=
|
||||
github.com/compose-spec/compose-go/v2 v2.4.8 h1:7Myl8wDRl/4mRz77S+eyDJymGGEHu0diQdGSSeyq90A=
|
||||
github.com/compose-spec/compose-go/v2 v2.4.8/go.mod h1:lFN0DrMxIncJGYAXTfWuajfwj5haBJqrBkarHcnjJKc=
|
||||
github.com/compose-spec/compose-go/v2 v2.6.0 h1:/+oBD2ixSENOeN/TlJqWZmUak0xM8A7J08w/z661Wd4=
|
||||
github.com/compose-spec/compose-go/v2 v2.6.0/go.mod h1:vPlkN0i+0LjLf9rv52lodNMUTJF5YHVfHVGLLIP67NA=
|
||||
github.com/containerd/cgroups/v3 v3.0.5 h1:44na7Ud+VwyE7LIoJ8JTNQOa549a8543BmzaJHo6Bzo=
|
||||
github.com/containerd/cgroups/v3 v3.0.5/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins=
|
||||
github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro=
|
||||
github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
|
||||
github.com/containerd/containerd/api v1.8.0 h1:hVTNJKR8fMc/2Tiw60ZRijntNMd1U+JVMyTRdsD2bS0=
|
||||
github.com/containerd/containerd/api v1.8.0/go.mod h1:dFv4lt6S20wTu/hMcP4350RL87qPWLVa/OHOwmmdnYc=
|
||||
github.com/containerd/containerd/v2 v2.0.3 h1:zBKgwgZsuu+LPCMzCLgA4sC4MiZzZ59ZT31XkmiISQM=
|
||||
github.com/containerd/containerd/v2 v2.0.3/go.mod h1:5j9QUUaV/cy9ZeAx4S+8n9ffpf+iYnEj4jiExgcbuLY=
|
||||
github.com/containerd/containerd/v2 v2.0.4 h1:+r7yJMwhTfMm3CDyiBjMBQO8a9CTBxL2Bg/JtqtIwB8=
|
||||
github.com/containerd/containerd/v2 v2.0.4/go.mod h1:5j9QUUaV/cy9ZeAx4S+8n9ffpf+iYnEj4jiExgcbuLY=
|
||||
github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4=
|
||||
github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
|
||||
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
|
||||
@@ -110,7 +103,6 @@ github.com/containerd/ttrpc v1.2.7 h1:qIrroQvuOL9HQ1X6KHe2ohc7p+HP/0VE6XPU7elJRq
|
||||
github.com/containerd/ttrpc v1.2.7/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o=
|
||||
github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40=
|
||||
github.com/containerd/typeurl/v2 v2.2.3/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
@@ -122,17 +114,17 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/docker/cli v28.0.1+incompatible h1:g0h5NQNda3/CxIsaZfH4Tyf6vpxFth7PYl3hgCPOKzs=
|
||||
github.com/docker/cli v28.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/cli v28.0.4+incompatible h1:pBJSJeNd9QeIWPjRcV91RVJihd/TXB77q1ef64XEu4A=
|
||||
github.com/docker/cli v28.0.4+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/cli-docs-tool v0.9.0 h1:CVwQbE+ZziwlPqrJ7LRyUF6GvCA+6gj7MTCsayaK9t0=
|
||||
github.com/docker/cli-docs-tool v0.9.0/go.mod h1:ClrwlNW+UioiRyH9GiAOe1o3J/TsY3Tr1ipoypjAUtc=
|
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
|
||||
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v28.0.1+incompatible h1:FCHjSRdXhNRFjlHMTv4jUNlIBbTeRjrWfeFuJp7jpo0=
|
||||
github.com/docker/docker v28.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo=
|
||||
github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
|
||||
github.com/docker/docker v28.0.4+incompatible h1:JNNkBctYKurkw6FrHfKqY0nKIDf5nrbxjVBtS+cdcok=
|
||||
github.com/docker/docker v28.0.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8=
|
||||
github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo=
|
||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
|
||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c/go.mod h1:CADgU4DSXK5QUlFslkQu2yW2TKzFZcXq/leZfM0UH5Q=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
@@ -171,7 +163,6 @@ github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En
|
||||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
|
||||
github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/go-sql-driver/mysql v1.3.0 h1:pgwjLi/dvffoP9aabwkT3AKpXQM93QARkjFhDDqC1UE=
|
||||
github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
@@ -196,15 +187,14 @@ github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/certificate-transparency-go v1.0.10-0.20180222191210-5ab67e519c93 h1:jc2UWq7CbdszqeH6qu1ougXMIUBfSy8Pbh/anURYbGI=
|
||||
github.com/google/certificate-transparency-go v1.0.10-0.20180222191210-5ab67e519c93/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
|
||||
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
|
||||
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
@@ -222,7 +212,6 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I=
|
||||
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=
|
||||
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
@@ -243,9 +232,7 @@ github.com/in-toto/in-toto-golang v0.5.0/go.mod h1:/Rq0IZHLV7Ku5gielPT4wPHJfH1Gd
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jinzhu/gorm v0.0.0-20170222002820-5409931a1bb8 h1:CZkYfurY6KGhVtlalI4QwQ6T0Cu6iuY3e0x5RLu96WE=
|
||||
github.com/jinzhu/gorm v0.0.0-20170222002820-5409931a1bb8/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo=
|
||||
github.com/jinzhu/inflection v0.0.0-20170102125226-1c35d901db3d h1:jRQLvyVGL+iVtDElaEIDdKwpPqUIZJfzkNLV34htpEc=
|
||||
github.com/jinzhu/inflection v0.0.0-20170102125226-1c35d901db3d/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
@@ -258,8 +245,8 @@ github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVE
|
||||
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.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
@@ -274,7 +261,6 @@ 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/lib/pq v0.0.0-20150723085316-0dad96c0b94f/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/magiconair/properties v1.5.3 h1:C8fxWnhYyME3n0klPOhVM7PtYUB3eV1W3DeFmN3j53Y=
|
||||
github.com/magiconair/properties v1.5.3/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
@@ -293,10 +279,8 @@ github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZX
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20150613213606-2caf8efc9366/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/moby/buildkit v0.20.1 h1:sT0ZXhhNo5rVbMcYfgttma3TdUHfO5JjFA0UAL8p9fY=
|
||||
github.com/moby/buildkit v0.20.1/go.mod h1:Rq9nB/fJImdk6QeM0niKtOHJqwKeYMrK847hTTDVuA4=
|
||||
github.com/moby/buildkit v0.21.0 h1:+z4vVqgt0spLrOSxi4DLedRbIh2gbNVlZ5q4rsnNp60=
|
||||
github.com/moby/buildkit v0.21.0/go.mod h1:mBq0D44uCyz2PdX8T/qym5LBbkBO3GGv0wqgX9ABYYw=
|
||||
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/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
|
||||
@@ -311,8 +295,8 @@ github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7z
|
||||
github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
|
||||
github.com/moby/sys/signal v0.7.1 h1:PrQxdvxcGijdo6UXXo/lU/TvHUWyPhj7UOpSo8tuvk0=
|
||||
github.com/moby/sys/signal v0.7.1/go.mod h1:Se1VGehYokAkrSQwL4tDzHvETwUZlnY7S5XtQ50mQp8=
|
||||
github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo=
|
||||
github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
|
||||
github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs=
|
||||
github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
|
||||
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
|
||||
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
|
||||
github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
|
||||
@@ -345,13 +329,12 @@ github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
||||
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
|
||||
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
|
||||
github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk=
|
||||
github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/selinux v1.11.1 h1:nHFvthhM0qY8/m+vfhJylliSshm8G1jJ2jDMcgULaH8=
|
||||
github.com/opencontainers/selinux v1.11.1/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
|
||||
github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
@@ -406,17 +389,14 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/spdx/tools-golang v0.5.3 h1:ialnHeEYUC4+hkm5vJm4qz2x+oEJbS0mAMFrNXdQraY=
|
||||
github.com/spdx/tools-golang v0.5.3/go.mod h1:/ETOahiAo96Ob0/RAIBmFZw6XN0yTnyr/uFZm2NTMhI=
|
||||
github.com/spf13/cast v0.0.0-20150508191742-4d07383ffe94 h1:JmfC365KywYwHB946TTiQWEb8kqPY+pybPLoGE9GgVk=
|
||||
github.com/spf13/cast v0.0.0-20150508191742-4d07383ffe94/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
|
||||
github.com/spf13/cobra v0.0.1/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||
github.com/spf13/jwalterweatherman v0.0.0-20141219030609-3d60171a6431 h1:XTHrT015sxHyJ5FnQ0AeemSspZWaDq7DoTRW0EVsDCE=
|
||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||
github.com/spf13/jwalterweatherman v0.0.0-20141219030609-3d60171a6431/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.0/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v0.0.0-20150530192845-be5ff3e4840c h1:2EejZtjFjKJGk71ANb+wtFK5EjUzUkEM3R0xnp559xg=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v0.0.0-20150530192845-be5ff3e4840c/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@@ -435,14 +415,14 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/theupdateframework/notary v0.7.0 h1:QyagRZ7wlSpjT5N2qQAh/pN+DVqgekv4DzbAiAiEL3c=
|
||||
github.com/theupdateframework/notary v0.7.0/go.mod h1:c9DRxcmhHmVLDay4/2fUYdISnHqbFDGRSlXPO0AhYWw=
|
||||
github.com/tonistiigi/dchapes-mode v0.0.0-20241001053921-ca0759fec205 h1:eUk79E1w8yMtXeHSzjKorxuC8qJOnyXQnLaJehxpJaI=
|
||||
github.com/tonistiigi/dchapes-mode v0.0.0-20241001053921-ca0759fec205/go.mod h1:3Iuxbr0P7D3zUzBMAZB+ois3h/et0shEz0qApgHYGpY=
|
||||
github.com/tonistiigi/fsutil v0.0.0-20250113203817-b14e27f4135a h1:EfGw4G0x/8qXWgtcZ6KVaPS+wpWOQMaypczzP8ojkMY=
|
||||
github.com/tonistiigi/fsutil v0.0.0-20250113203817-b14e27f4135a/go.mod h1:Dl/9oEjK7IqnjAm21Okx/XIxUCFJzvh+XdVHUlBwXTw=
|
||||
github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 h1:r0p7fK56l8WPequOaR3i9LBqfPtEdXIQbUTzT55iqT4=
|
||||
github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323/go.mod h1:3Iuxbr0P7D3zUzBMAZB+ois3h/et0shEz0qApgHYGpY=
|
||||
github.com/tonistiigi/fsutil v0.0.0-20250410151801-5b74a7ad7583 h1:mK+ZskNt7SG4dxfKIi27C7qHAQzyjAVt1iyTf0hmsNc=
|
||||
github.com/tonistiigi/fsutil v0.0.0-20250410151801-5b74a7ad7583/go.mod h1:BKdcez7BiVtBvIcef90ZPc6ebqIWr4JWD7+EvLm6J98=
|
||||
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4 h1:7I5c2Ig/5FgqkYOh/N87NzoyI9U15qUPXhDD8uCupv8=
|
||||
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4/go.mod h1:278M4p8WsNh3n4a1eqiFcV2FGk7wE5fwUpUom9mK9lE=
|
||||
github.com/tonistiigi/jaeger-ui-rest v0.0.0-20250211190051-7d4944a45bb6 h1:RT/a0RvdX84iwtOrUK45+wjcNpaG+hS7n7XFYqj4axg=
|
||||
github.com/tonistiigi/jaeger-ui-rest v0.0.0-20250211190051-7d4944a45bb6/go.mod h1:3Ez1Paeg+0Ghu3KwpEGC1HgZ4CHDlg+Ez/5Baeomk54=
|
||||
github.com/tonistiigi/jaeger-ui-rest v0.0.0-20250408171107-3dd17559e117 h1:XFwyh2JZwR5aiKLXHX2C1n0v5F11dCJpyGL1W/Cpl3U=
|
||||
github.com/tonistiigi/jaeger-ui-rest v0.0.0-20250408171107-3dd17559e117/go.mod h1:3Ez1Paeg+0Ghu3KwpEGC1HgZ4CHDlg+Ez/5Baeomk54=
|
||||
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea h1:SXhTLE6pb6eld/v/cCndK0AMpt1wiVFb/YYmqB3/QG0=
|
||||
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea/go.mod h1:WPnis/6cRcDZSUvVmezrxJPkiO87ThFYsoUiMwWNDJk=
|
||||
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab h1:H6aJ0yKQ0gF49Qb2z5hI1UHxSQt4JMyxebFR15KnApw=
|
||||
@@ -459,6 +439,8 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
|
||||
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/zclconf/go-cty v1.4.0/go.mod h1:nHzOclRkoj++EU9ZjSrZvRG0BXIWt8c7loYc0qXAFGQ=
|
||||
@@ -508,14 +490,12 @@ golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20200422194213-44a606286825/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo=
|
||||
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak=
|
||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
@@ -523,8 +503,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.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
|
||||
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -533,8 +513,8 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
||||
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -547,24 +527,24 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
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.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
|
||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
|
||||
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
|
||||
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o=
|
||||
golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q=
|
||||
golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU=
|
||||
golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -583,7 +563,6 @@ google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8i
|
||||
google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/cenkalti/backoff.v2 v2.2.1 h1:eJ9UAg01/HIHG987TwxvnzK2MgxXq97YY6rYDpY9aII=
|
||||
gopkg.in/cenkalti/backoff.v2 v2.2.1/go.mod h1:S0QdOvT2AlerfSBkp0O+dk+bbIMaNbEmVk876gPCthU=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
@@ -594,7 +573,6 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy
|
||||
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/rethinkdb/rethinkdb-go.v6 v6.2.1 h1:d4KQkxAaAiRY2h5Zqis161Pv91A37uZyJOx73duwUwM=
|
||||
gopkg.in/rethinkdb/rethinkdb-go.v6 v6.2.1/go.mod h1:WbjuEoo1oadwzQ4apSDU+JTvmllEHtsNHS6y7vFc7iw=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
@@ -2,6 +2,7 @@ package store
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"maps"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
@@ -93,9 +94,7 @@ func (ng *NodeGroup) Update(name, endpoint string, platforms []string, endpoints
|
||||
needsRestart = true
|
||||
}
|
||||
if buildkitdConfigFile != "" {
|
||||
for k, v := range files {
|
||||
n.Files[k] = v
|
||||
}
|
||||
maps.Copy(n.Files, files)
|
||||
needsRestart = true
|
||||
}
|
||||
if needsRestart {
|
||||
@@ -147,9 +146,7 @@ func (n *Node) Copy() *Node {
|
||||
buildkitdFlags := []string{}
|
||||
copy(buildkitdFlags, n.BuildkitdFlags)
|
||||
driverOpts := map[string]string{}
|
||||
for k, v := range n.DriverOpts {
|
||||
driverOpts[k] = v
|
||||
}
|
||||
maps.Copy(driverOpts, n.DriverOpts)
|
||||
files := map[string][]byte{}
|
||||
for k, v := range n.Files {
|
||||
vv := []byte{}
|
||||
|
156
tests/bake.go
156
tests/bake.go
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/containerd/continuity/fs/fstest"
|
||||
"github.com/docker/buildx/bake"
|
||||
"github.com/docker/buildx/util/gitutil"
|
||||
"github.com/docker/buildx/util/gitutil/gittestutil"
|
||||
"github.com/moby/buildkit/client"
|
||||
"github.com/moby/buildkit/frontend/subrequests/lint"
|
||||
"github.com/moby/buildkit/identity"
|
||||
@@ -39,6 +40,7 @@ var bakeTests = []func(t *testing.T, sb integration.Sandbox){
|
||||
testBakePrint,
|
||||
testBakePrintSensitive,
|
||||
testBakePrintOverrideEmpty,
|
||||
testBakePrintKeepEscaped,
|
||||
testBakeLocal,
|
||||
testBakeLocalMulti,
|
||||
testBakeRemote,
|
||||
@@ -328,6 +330,66 @@ target "default" {
|
||||
}`, stdout.String())
|
||||
}
|
||||
|
||||
func testBakePrintKeepEscaped(t *testing.T, sb integration.Sandbox) {
|
||||
bakefile := []byte(`
|
||||
target "default" {
|
||||
dockerfile-inline = <<EOT
|
||||
ARG VERSION=latest
|
||||
FROM alpine:$${VERSION}
|
||||
EOT
|
||||
args = {
|
||||
VERSION = "3.21"
|
||||
}
|
||||
annotations = [
|
||||
"org.opencontainers.image.authors=$${user}"
|
||||
]
|
||||
labels = {
|
||||
foo = "hello %%{bar}"
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
dir := tmpdir(t, fstest.CreateFile("docker-bake.hcl", bakefile, 0600))
|
||||
cmd := buildxCmd(sb, withDir(dir), withArgs("bake", "--print"))
|
||||
stdout := bytes.Buffer{}
|
||||
stderr := bytes.Buffer{}
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = &stderr
|
||||
require.NoError(t, cmd.Run(), stdout.String(), stderr.String())
|
||||
|
||||
require.JSONEq(t, `{
|
||||
"group": {
|
||||
"default": {
|
||||
"targets": [
|
||||
"default"
|
||||
]
|
||||
}
|
||||
},
|
||||
"target": {
|
||||
"default": {
|
||||
"annotations": [
|
||||
"org.opencontainers.image.authors=$${user}"
|
||||
],
|
||||
"context": ".",
|
||||
"dockerfile": "Dockerfile",
|
||||
"dockerfile-inline": "ARG VERSION=latest\nFROM alpine:$${VERSION}\n",
|
||||
"args": {
|
||||
"VERSION": "3.21"
|
||||
},
|
||||
"labels": {
|
||||
"foo": "hello %%{bar}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}`, stdout.String())
|
||||
|
||||
// test build with definition from print output
|
||||
dir = tmpdir(t, fstest.CreateFile("docker-bake.json", stdout.Bytes(), 0600))
|
||||
cmd = buildxCmd(sb, withDir(dir), withArgs("bake"))
|
||||
out, err := cmd.CombinedOutput()
|
||||
require.NoError(t, err, string(out))
|
||||
}
|
||||
|
||||
func testBakeLocal(t *testing.T, sb integration.Sandbox) {
|
||||
dockerfile := []byte(`
|
||||
FROM scratch
|
||||
@@ -415,10 +477,10 @@ EOT
|
||||
git, err := gitutil.New(gitutil.WithWorkingDir(dir))
|
||||
require.NoError(t, err)
|
||||
|
||||
gitutil.GitInit(git, t)
|
||||
gitutil.GitAdd(git, t, "docker-bake.hcl", "foo")
|
||||
gitutil.GitCommit(git, t, "initial commit")
|
||||
addr := gitutil.GitServeHTTP(git, t)
|
||||
gittestutil.GitInit(git, t)
|
||||
gittestutil.GitAdd(git, t, "docker-bake.hcl", "foo")
|
||||
gittestutil.GitCommit(git, t, "initial commit")
|
||||
addr := gittestutil.GitServeHTTP(git, t)
|
||||
|
||||
out, err := bakeCmd(sb, withDir(dir), withArgs(addr, "--set", "*.output=type=local,dest="+dirDest))
|
||||
require.NoError(t, err, out)
|
||||
@@ -445,12 +507,12 @@ EOT
|
||||
git, err := gitutil.New(gitutil.WithWorkingDir(dir))
|
||||
require.NoError(t, err)
|
||||
|
||||
gitutil.GitInit(git, t)
|
||||
gitutil.GitAdd(git, t, "docker-bake.hcl", "foo")
|
||||
gitutil.GitCommit(git, t, "initial commit")
|
||||
gittestutil.GitInit(git, t)
|
||||
gittestutil.GitAdd(git, t, "docker-bake.hcl", "foo")
|
||||
gittestutil.GitCommit(git, t, "initial commit")
|
||||
|
||||
token := identity.NewID()
|
||||
addr := gitutil.GitServeHTTP(git, t, gitutil.WithAccessToken(token))
|
||||
addr := gittestutil.GitServeHTTP(git, t, gittestutil.WithAccessToken(token))
|
||||
|
||||
out, err := bakeCmd(sb, withDir(dir),
|
||||
withEnv("BUILDX_BAKE_GIT_AUTH_TOKEN="+token),
|
||||
@@ -492,10 +554,10 @@ EOT
|
||||
git, err := gitutil.New(gitutil.WithWorkingDir(dirSpec))
|
||||
require.NoError(t, err)
|
||||
|
||||
gitutil.GitInit(git, t)
|
||||
gitutil.GitAdd(git, t, "docker-bake.hcl", "bar")
|
||||
gitutil.GitCommit(git, t, "initial commit")
|
||||
addr := gitutil.GitServeHTTP(git, t)
|
||||
gittestutil.GitInit(git, t)
|
||||
gittestutil.GitAdd(git, t, "docker-bake.hcl", "bar")
|
||||
gittestutil.GitCommit(git, t, "initial commit")
|
||||
addr := gittestutil.GitServeHTTP(git, t)
|
||||
|
||||
out, err := bakeCmd(sb, withDir(dirSrc), withArgs(addr, "--file", "cwd://local-docker-bake.hcl", "--set", "*.output=type=local,dest="+dirDest))
|
||||
require.NoError(t, err, out)
|
||||
@@ -561,10 +623,10 @@ EOT
|
||||
git, err := gitutil.New(gitutil.WithWorkingDir(dirSpec))
|
||||
require.NoError(t, err)
|
||||
|
||||
gitutil.GitInit(git, t)
|
||||
gitutil.GitAdd(git, t, "docker-bake.hcl")
|
||||
gitutil.GitCommit(git, t, "initial commit")
|
||||
addr := gitutil.GitServeHTTP(git, t)
|
||||
gittestutil.GitInit(git, t)
|
||||
gittestutil.GitAdd(git, t, "docker-bake.hcl")
|
||||
gittestutil.GitCommit(git, t, "initial commit")
|
||||
addr := gittestutil.GitServeHTTP(git, t)
|
||||
|
||||
out, err := bakeCmd(sb, withDir(dirSrc), withArgs(addr, "--set", "*.output=type=local,dest="+dirDest))
|
||||
require.NoError(t, err, out)
|
||||
@@ -594,17 +656,17 @@ EOT
|
||||
|
||||
gitSpec, err := gitutil.New(gitutil.WithWorkingDir(dirSpec))
|
||||
require.NoError(t, err)
|
||||
gitutil.GitInit(gitSpec, t)
|
||||
gitutil.GitAdd(gitSpec, t, "docker-bake.hcl")
|
||||
gitutil.GitCommit(gitSpec, t, "initial commit")
|
||||
addrSpec := gitutil.GitServeHTTP(gitSpec, t)
|
||||
gittestutil.GitInit(gitSpec, t)
|
||||
gittestutil.GitAdd(gitSpec, t, "docker-bake.hcl")
|
||||
gittestutil.GitCommit(gitSpec, t, "initial commit")
|
||||
addrSpec := gittestutil.GitServeHTTP(gitSpec, t)
|
||||
|
||||
gitSrc, err := gitutil.New(gitutil.WithWorkingDir(dirSrc))
|
||||
require.NoError(t, err)
|
||||
gitutil.GitInit(gitSrc, t)
|
||||
gitutil.GitAdd(gitSrc, t, "foo")
|
||||
gitutil.GitCommit(gitSrc, t, "initial commit")
|
||||
addrSrc := gitutil.GitServeHTTP(gitSrc, t)
|
||||
gittestutil.GitInit(gitSrc, t)
|
||||
gittestutil.GitAdd(gitSrc, t, "foo")
|
||||
gittestutil.GitCommit(gitSrc, t, "initial commit")
|
||||
addrSrc := gittestutil.GitServeHTTP(gitSrc, t)
|
||||
|
||||
out, err := bakeCmd(sb, withDir("/tmp"), withArgs(addrSpec, addrSrc, "--set", "*.output=type=local,dest="+dirDest))
|
||||
require.NoError(t, err, out)
|
||||
@@ -635,10 +697,10 @@ COPY super-cool.txt /
|
||||
|
||||
git, err := gitutil.New(gitutil.WithWorkingDir(dir))
|
||||
require.NoError(t, err)
|
||||
gitutil.GitInit(git, t)
|
||||
gitutil.GitAdd(git, t, "docker-bake.hcl", "bar")
|
||||
gitutil.GitCommit(git, t, "initial commit")
|
||||
addr := gitutil.GitServeHTTP(git, t)
|
||||
gittestutil.GitInit(git, t)
|
||||
gittestutil.GitAdd(git, t, "docker-bake.hcl", "bar")
|
||||
gittestutil.GitCommit(git, t, "initial commit")
|
||||
addr := gittestutil.GitServeHTTP(git, t)
|
||||
|
||||
out, err := bakeCmd(sb, withDir("/tmp"), withArgs(addr, "--set", "*.output=type=local,dest="+dirDest))
|
||||
require.NoError(t, err, out)
|
||||
@@ -676,10 +738,10 @@ EOT
|
||||
git, err := gitutil.New(gitutil.WithWorkingDir(dirSpec))
|
||||
require.NoError(t, err)
|
||||
|
||||
gitutil.GitInit(git, t)
|
||||
gitutil.GitAdd(git, t, "docker-bake.hcl")
|
||||
gitutil.GitCommit(git, t, "initial commit")
|
||||
addr := gitutil.GitServeHTTP(git, t)
|
||||
gittestutil.GitInit(git, t)
|
||||
gittestutil.GitAdd(git, t, "docker-bake.hcl")
|
||||
gittestutil.GitCommit(git, t, "initial commit")
|
||||
addr := gittestutil.GitServeHTTP(git, t)
|
||||
|
||||
out, err := bakeCmd(
|
||||
sb,
|
||||
@@ -724,10 +786,10 @@ EOT
|
||||
git, err := gitutil.New(gitutil.WithWorkingDir(dirSpec))
|
||||
require.NoError(t, err)
|
||||
|
||||
gitutil.GitInit(git, t)
|
||||
gitutil.GitAdd(git, t, "docker-bake.hcl")
|
||||
gitutil.GitCommit(git, t, "initial commit")
|
||||
addr := gitutil.GitServeHTTP(git, t)
|
||||
gittestutil.GitInit(git, t)
|
||||
gittestutil.GitAdd(git, t, "docker-bake.hcl")
|
||||
gittestutil.GitCommit(git, t, "initial commit")
|
||||
addr := gittestutil.GitServeHTTP(git, t)
|
||||
|
||||
out, err := bakeCmd(
|
||||
sb,
|
||||
@@ -780,13 +842,13 @@ COPY foo /foo
|
||||
git, err := gitutil.New(gitutil.WithWorkingDir(dirSpec))
|
||||
require.NoError(t, err)
|
||||
|
||||
gitutil.GitInit(git, t)
|
||||
gitutil.GitAdd(git, t, "docker-bake.hcl")
|
||||
gitutil.GitAdd(git, t, "Dockerfile")
|
||||
gitutil.GitAdd(git, t, "foo")
|
||||
gitutil.GitAdd(git, t, "bar")
|
||||
gitutil.GitCommit(git, t, "initial commit")
|
||||
addr := gitutil.GitServeHTTP(git, t)
|
||||
gittestutil.GitInit(git, t)
|
||||
gittestutil.GitAdd(git, t, "docker-bake.hcl")
|
||||
gittestutil.GitAdd(git, t, "Dockerfile")
|
||||
gittestutil.GitAdd(git, t, "foo")
|
||||
gittestutil.GitAdd(git, t, "bar")
|
||||
gittestutil.GitCommit(git, t, "initial commit")
|
||||
addr := gittestutil.GitServeHTTP(git, t)
|
||||
|
||||
out, err := bakeCmd(
|
||||
sb,
|
||||
@@ -832,10 +894,10 @@ COPY foo /foo
|
||||
git, err := gitutil.New(gitutil.WithWorkingDir(dirSpec))
|
||||
require.NoError(t, err)
|
||||
|
||||
gitutil.GitInit(git, t)
|
||||
gitutil.GitAdd(git, t, "docker-bake.hcl")
|
||||
gitutil.GitCommit(git, t, "initial commit")
|
||||
addr := gitutil.GitServeHTTP(git, t)
|
||||
gittestutil.GitInit(git, t)
|
||||
gittestutil.GitAdd(git, t, "docker-bake.hcl")
|
||||
gittestutil.GitCommit(git, t, "initial commit")
|
||||
addr := gittestutil.GitServeHTTP(git, t)
|
||||
|
||||
out, err := bakeCmd(
|
||||
sb,
|
||||
|
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/docker/buildx/localstate"
|
||||
"github.com/docker/buildx/util/confutil"
|
||||
"github.com/docker/buildx/util/gitutil"
|
||||
"github.com/docker/buildx/util/gitutil/gittestutil"
|
||||
"github.com/moby/buildkit/client"
|
||||
"github.com/moby/buildkit/frontend/subrequests/lint"
|
||||
"github.com/moby/buildkit/frontend/subrequests/outline"
|
||||
@@ -126,10 +127,10 @@ COPY foo /foo
|
||||
git, err := gitutil.New(gitutil.WithWorkingDir(dir))
|
||||
require.NoError(t, err)
|
||||
|
||||
gitutil.GitInit(git, t)
|
||||
gitutil.GitAdd(git, t, "Dockerfile", "foo")
|
||||
gitutil.GitCommit(git, t, "initial commit")
|
||||
addr := gitutil.GitServeHTTP(git, t)
|
||||
gittestutil.GitInit(git, t)
|
||||
gittestutil.GitAdd(git, t, "Dockerfile", "foo")
|
||||
gittestutil.GitCommit(git, t, "initial commit")
|
||||
addr := gittestutil.GitServeHTTP(git, t)
|
||||
|
||||
out, err := buildCmd(sb, withDir(dir), withArgs("--output=type=local,dest="+dirDest, addr))
|
||||
require.NoError(t, err, out)
|
||||
@@ -238,10 +239,10 @@ COPY foo /foo
|
||||
git, err := gitutil.New(gitutil.WithWorkingDir(dir))
|
||||
require.NoError(t, err)
|
||||
|
||||
gitutil.GitInit(git, t)
|
||||
gitutil.GitAdd(git, t, "build.Dockerfile", "foo")
|
||||
gitutil.GitCommit(git, t, "initial commit")
|
||||
addr := gitutil.GitServeHTTP(git, t)
|
||||
gittestutil.GitInit(git, t)
|
||||
gittestutil.GitAdd(git, t, "build.Dockerfile", "foo")
|
||||
gittestutil.GitCommit(git, t, "initial commit")
|
||||
addr := gittestutil.GitServeHTTP(git, t)
|
||||
|
||||
out, err := buildCmd(sb, withDir(dir), withArgs(
|
||||
"-f", "build.Dockerfile",
|
||||
|
@@ -202,7 +202,7 @@ func ParseAttests(in []string) ([]*controllerapi.Attest, error) {
|
||||
func ConvertAttests(in []*Attest) ([]*controllerapi.Attest, error) {
|
||||
out := make([]*controllerapi.Attest, 0, len(in))
|
||||
|
||||
// Check for dupplicate attestations while we convert them
|
||||
// Check for duplicate attestations while we convert them
|
||||
// to the controller API.
|
||||
found := map[string]struct{}{}
|
||||
for _, attest := range in {
|
||||
|
78
util/desktop/bundle/content.go
Normal file
78
util/desktop/bundle/content.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package bundle
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/containerd/containerd/v2/core/content"
|
||||
cerrdefs "github.com/containerd/errdefs"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
type nsFallbackStore struct {
|
||||
main content.Store
|
||||
fb content.Store
|
||||
}
|
||||
|
||||
var _ content.Store = &nsFallbackStore{}
|
||||
|
||||
func (c *nsFallbackStore) Info(ctx context.Context, dgst digest.Digest) (content.Info, error) {
|
||||
info, err := c.main.Info(ctx, dgst)
|
||||
if err != nil {
|
||||
if cerrdefs.IsNotFound(err) {
|
||||
return c.fb.Info(ctx, dgst)
|
||||
}
|
||||
}
|
||||
return info, err
|
||||
}
|
||||
|
||||
func (c *nsFallbackStore) Update(ctx context.Context, info content.Info, fieldpaths ...string) (content.Info, error) {
|
||||
return c.main.Update(ctx, info, fieldpaths...)
|
||||
}
|
||||
|
||||
func (c *nsFallbackStore) Walk(ctx context.Context, fn content.WalkFunc, filters ...string) error {
|
||||
seen := make(map[digest.Digest]struct{})
|
||||
err := c.main.Walk(ctx, func(i content.Info) error {
|
||||
seen[i.Digest] = struct{}{}
|
||||
return fn(i)
|
||||
}, filters...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.fb.Walk(ctx, func(i content.Info) error {
|
||||
if _, ok := seen[i.Digest]; ok {
|
||||
return nil
|
||||
}
|
||||
return fn(i)
|
||||
}, filters...)
|
||||
}
|
||||
|
||||
func (c *nsFallbackStore) Delete(ctx context.Context, dgst digest.Digest) error {
|
||||
return c.main.Delete(ctx, dgst)
|
||||
}
|
||||
|
||||
func (c *nsFallbackStore) Status(ctx context.Context, ref string) (content.Status, error) {
|
||||
return c.main.Status(ctx, ref)
|
||||
}
|
||||
|
||||
func (c *nsFallbackStore) ListStatuses(ctx context.Context, filters ...string) ([]content.Status, error) {
|
||||
return c.main.ListStatuses(ctx, filters...)
|
||||
}
|
||||
|
||||
func (c *nsFallbackStore) Abort(ctx context.Context, ref string) error {
|
||||
return c.main.Abort(ctx, ref)
|
||||
}
|
||||
|
||||
func (c *nsFallbackStore) ReaderAt(ctx context.Context, desc ocispecs.Descriptor) (content.ReaderAt, error) {
|
||||
ra, err := c.main.ReaderAt(ctx, desc)
|
||||
if err != nil {
|
||||
if cerrdefs.IsNotFound(err) {
|
||||
return c.fb.ReaderAt(ctx, desc)
|
||||
}
|
||||
}
|
||||
return ra, err
|
||||
}
|
||||
|
||||
func (c *nsFallbackStore) Writer(ctx context.Context, opts ...content.WriterOpt) (content.Writer, error) {
|
||||
return c.main.Writer(ctx, opts...)
|
||||
}
|
282
util/desktop/bundle/export.go
Normal file
282
util/desktop/bundle/export.go
Normal file
@@ -0,0 +1,282 @@
|
||||
package bundle
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
|
||||
"github.com/containerd/containerd/v2/core/content"
|
||||
"github.com/containerd/containerd/v2/core/content/proxy"
|
||||
imgarchive "github.com/containerd/containerd/v2/core/images/archive"
|
||||
"github.com/docker/buildx/localstate"
|
||||
controlapi "github.com/moby/buildkit/api/services/control"
|
||||
"github.com/moby/buildkit/client"
|
||||
"github.com/moby/buildkit/util/contentutil"
|
||||
"github.com/opencontainers/go-digest"
|
||||
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
HistoryRecordMediaTypeV0 = "application/vnd.buildkit.historyrecord.v0"
|
||||
RefDescriptorMediaType = "vnd.export-build.descriptor.mediatype"
|
||||
)
|
||||
|
||||
type Record struct {
|
||||
*controlapi.BuildHistoryRecord
|
||||
|
||||
DefaultPlatform string
|
||||
LocalState *localstate.State `json:"localState,omitempty"`
|
||||
StateGroup *localstate.StateGroup `json:"stateGroup,omitempty"`
|
||||
}
|
||||
|
||||
func Export(ctx context.Context, c []*client.Client, w io.Writer, records []*Record) error {
|
||||
var store content.Store
|
||||
for _, c := range c {
|
||||
s := proxy.NewContentStore(c.ContentClient())
|
||||
if store == nil {
|
||||
store = s
|
||||
break
|
||||
}
|
||||
store = &nsFallbackStore{
|
||||
main: store,
|
||||
fb: s,
|
||||
}
|
||||
}
|
||||
if store == nil {
|
||||
return errors.New("no buildkit client found")
|
||||
}
|
||||
|
||||
mp := contentutil.NewMultiProvider(store)
|
||||
|
||||
desc, err := export(ctx, mp, records)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to export")
|
||||
}
|
||||
|
||||
gz := gzip.NewWriter(w)
|
||||
defer gz.Close()
|
||||
|
||||
if err := imgarchive.Export(ctx, mp, gz, imgarchive.WithManifest(desc), imgarchive.WithSkipDockerManifest()); err != nil {
|
||||
return errors.Wrap(err, "failed to create dockerbuild archive")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func export(ctx context.Context, mp *contentutil.MultiProvider, records []*Record) (ocispecs.Descriptor, error) {
|
||||
if len(records) == 1 {
|
||||
desc, err := exportRecord(ctx, mp, records[0])
|
||||
if err != nil {
|
||||
return ocispecs.Descriptor{}, errors.Wrap(err, "failed to export record")
|
||||
}
|
||||
return desc, nil
|
||||
}
|
||||
|
||||
var idx ocispecs.Index
|
||||
idx.MediaType = ocispecs.MediaTypeImageIndex
|
||||
idx.SchemaVersion = 2
|
||||
|
||||
for _, r := range records {
|
||||
desc, err := exportRecord(ctx, mp, r)
|
||||
if err != nil {
|
||||
return ocispecs.Descriptor{}, errors.Wrap(err, "failed to export record")
|
||||
}
|
||||
if desc.Annotations == nil {
|
||||
desc.Annotations = make(map[string]string)
|
||||
}
|
||||
desc.Annotations["vnd.buildkit.history.reference"] = r.Ref
|
||||
idx.Manifests = append(idx.Manifests, desc)
|
||||
}
|
||||
|
||||
desc, err := writeJSON(ctx, mp, idx.MediaType, idx)
|
||||
if err != nil {
|
||||
return ocispecs.Descriptor{}, errors.Wrap(err, "failed to write index")
|
||||
}
|
||||
|
||||
return desc, nil
|
||||
}
|
||||
|
||||
func writeJSON(ctx context.Context, mp *contentutil.MultiProvider, mt string, data any) (ocispecs.Descriptor, error) {
|
||||
dt, err := json.MarshalIndent(data, "", " ")
|
||||
if err != nil {
|
||||
return ocispecs.Descriptor{}, errors.Wrap(err, "failed to marshal data")
|
||||
}
|
||||
|
||||
desc := ocispecs.Descriptor{
|
||||
MediaType: mt,
|
||||
Size: int64(len(dt)),
|
||||
Digest: digest.FromBytes(dt),
|
||||
}
|
||||
|
||||
buf := contentutil.NewBuffer()
|
||||
if err := content.WriteBlob(ctx, buf, "blob-"+desc.Digest.String(), bytes.NewReader(dt), desc); err != nil {
|
||||
return ocispecs.Descriptor{}, errors.Wrap(err, "failed to write blob")
|
||||
}
|
||||
|
||||
mp.Add(desc.Digest, buf)
|
||||
return desc, nil
|
||||
}
|
||||
|
||||
func sanitizeCacheImports(v string) (string, error) {
|
||||
type cacheImport struct {
|
||||
Type string `json:"Type"`
|
||||
Attrs map[string]string `json:"Attrs"`
|
||||
}
|
||||
var arr []cacheImport
|
||||
if err := json.Unmarshal([]byte(v), &arr); err != nil {
|
||||
return "", errors.Wrap(err, "failed to unmarshal cache imports")
|
||||
}
|
||||
for i := range arr {
|
||||
m := map[string]string{}
|
||||
for k, v := range arr[i].Attrs {
|
||||
if k == "scope" || k == "ref" {
|
||||
m[k] = v
|
||||
}
|
||||
}
|
||||
arr[i].Attrs = m
|
||||
}
|
||||
dt, err := json.Marshal(arr)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed to marshal cache imports")
|
||||
}
|
||||
return string(dt), nil
|
||||
}
|
||||
|
||||
func sanitizeRecord(rec *controlapi.BuildHistoryRecord) {
|
||||
for k, v := range rec.FrontendAttrs {
|
||||
if k == "cache-imports" {
|
||||
v, err := sanitizeCacheImports(v)
|
||||
if err != nil {
|
||||
rec.FrontendAttrs[k] = ""
|
||||
} else {
|
||||
rec.FrontendAttrs[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func exportRecord(ctx context.Context, mp *contentutil.MultiProvider, record *Record) (ocispecs.Descriptor, error) {
|
||||
var mfst ocispecs.Manifest
|
||||
mfst.MediaType = ocispecs.MediaTypeImageManifest
|
||||
mfst.SchemaVersion = 2
|
||||
|
||||
sanitizeRecord(record.BuildHistoryRecord)
|
||||
|
||||
visited := map[string]struct{}{}
|
||||
|
||||
if trace := record.Trace; trace != nil {
|
||||
desc, err := loadDescriptor(ctx, mp, trace, visited)
|
||||
if err != nil {
|
||||
return ocispecs.Descriptor{}, errors.Wrap(err, "failed to load trace descriptor")
|
||||
}
|
||||
desc, err = sanitizeTrace(ctx, mp, *desc)
|
||||
if err != nil {
|
||||
return ocispecs.Descriptor{}, errors.Wrap(err, "failed to sanitize trace")
|
||||
}
|
||||
record.Trace.Digest = desc.Digest.String()
|
||||
record.Trace.Size = desc.Size
|
||||
mfst.Layers = append(mfst.Layers, *desc)
|
||||
}
|
||||
|
||||
config, err := writeJSON(ctx, mp, HistoryRecordMediaTypeV0, record)
|
||||
if err != nil {
|
||||
return ocispecs.Descriptor{}, errors.Wrap(err, "failed to write history record")
|
||||
}
|
||||
|
||||
mfst.Config = config
|
||||
|
||||
if logs := record.Logs; logs != nil {
|
||||
desc, err := loadDescriptor(ctx, mp, logs, visited)
|
||||
if err != nil {
|
||||
return ocispecs.Descriptor{}, errors.Wrap(err, "failed to load logs descriptor")
|
||||
}
|
||||
mfst.Layers = append(mfst.Layers, *desc)
|
||||
}
|
||||
|
||||
if res := record.Result; res != nil {
|
||||
results, err := loadResult(ctx, mp, res, visited)
|
||||
if err != nil {
|
||||
return ocispecs.Descriptor{}, errors.Wrap(err, "failed to load result")
|
||||
}
|
||||
mfst.Layers = append(mfst.Layers, results...)
|
||||
}
|
||||
|
||||
if exterr := record.ExternalError; exterr != nil {
|
||||
desc, err := loadDescriptor(ctx, mp, exterr, visited)
|
||||
if err != nil {
|
||||
return ocispecs.Descriptor{}, errors.Wrap(err, "failed to load external error descriptor")
|
||||
}
|
||||
mfst.Layers = append(mfst.Layers, *desc)
|
||||
}
|
||||
|
||||
for _, res := range record.Results {
|
||||
results, err := loadResult(ctx, mp, res, visited)
|
||||
if err != nil {
|
||||
return ocispecs.Descriptor{}, errors.Wrap(err, "failed to load result")
|
||||
}
|
||||
mfst.Layers = append(mfst.Layers, results...)
|
||||
}
|
||||
|
||||
desc, err := writeJSON(ctx, mp, mfst.MediaType, mfst)
|
||||
if err != nil {
|
||||
return ocispecs.Descriptor{}, errors.Wrap(err, "failed to write manifest")
|
||||
}
|
||||
|
||||
return desc, nil
|
||||
}
|
||||
|
||||
func loadResult(ctx context.Context, ip content.InfoProvider, in *controlapi.BuildResultInfo, visited map[string]struct{}) ([]ocispecs.Descriptor, error) {
|
||||
var out []ocispecs.Descriptor
|
||||
for _, attest := range in.Attestations {
|
||||
desc, err := loadDescriptor(ctx, ip, attest, visited)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to load attestation descriptor")
|
||||
}
|
||||
if desc != nil {
|
||||
out = append(out, *desc)
|
||||
}
|
||||
}
|
||||
for _, r := range in.Results {
|
||||
desc, err := loadDescriptor(ctx, ip, r, visited)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to load result descriptor")
|
||||
}
|
||||
if desc != nil {
|
||||
if desc.Annotations == nil {
|
||||
desc.Annotations = make(map[string]string)
|
||||
}
|
||||
// Override media type to avoid containerd to walk children. Also
|
||||
// keep original media type in annotations.
|
||||
desc.Annotations[RefDescriptorMediaType] = desc.MediaType
|
||||
desc.MediaType = "application/json"
|
||||
out = append(out, *desc)
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func loadDescriptor(ctx context.Context, ip content.InfoProvider, in *controlapi.Descriptor, visited map[string]struct{}) (*ocispecs.Descriptor, error) {
|
||||
if _, ok := visited[in.Digest]; ok {
|
||||
return nil, nil
|
||||
}
|
||||
visited[in.Digest] = struct{}{}
|
||||
|
||||
dgst, err := digest.Parse(in.Digest)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse digest")
|
||||
}
|
||||
|
||||
if _, err := ip.Info(ctx, dgst); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get info")
|
||||
}
|
||||
|
||||
return &ocispecs.Descriptor{
|
||||
MediaType: in.MediaType,
|
||||
Digest: dgst,
|
||||
Size: in.Size,
|
||||
Annotations: in.Annotations,
|
||||
}, nil
|
||||
}
|
84
util/desktop/bundle/trace.go
Normal file
84
util/desktop/bundle/trace.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package bundle
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/containerd/containerd/v2/core/content"
|
||||
"github.com/docker/buildx/util/otelutil"
|
||||
"github.com/moby/buildkit/util/contentutil"
|
||||
"github.com/opencontainers/go-digest"
|
||||
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
)
|
||||
|
||||
var (
|
||||
reOnce sync.Once
|
||||
reAttrs, reGhs, reGhpat *regexp.Regexp
|
||||
)
|
||||
|
||||
func sanitizeCommand(value string) string {
|
||||
reOnce.Do(func() {
|
||||
sensitiveKeys := []string{"ghtoken", "token", "access_key_id", "secret_access_key", "session_token"}
|
||||
reAttrs = regexp.MustCompile(`(?i)(` + strings.Join(sensitiveKeys, "|") + `)=[^ ,]+`)
|
||||
reGhs = regexp.MustCompile(`(?:ghu|ghs)_[A-Za-z0-9]{36}`)
|
||||
reGhpat = regexp.MustCompile(`github_pat_\w{82}`)
|
||||
})
|
||||
|
||||
value = reAttrs.ReplaceAllString(value, "${1}=xxxxx")
|
||||
// reGhs/reGhpat is just double proofing. Not really needed.
|
||||
value = reGhs.ReplaceAllString(value, "xxxxx")
|
||||
value = reGhpat.ReplaceAllString(value, "xxxxx")
|
||||
return value
|
||||
}
|
||||
|
||||
func sanitizeTrace(ctx context.Context, mp *contentutil.MultiProvider, desc ocispecs.Descriptor) (*ocispecs.Descriptor, error) {
|
||||
ra, err := mp.ReaderAt(ctx, desc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer ra.Close()
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
dec := json.NewDecoder(io.NewSectionReader(ra, 0, ra.Size()))
|
||||
enc := json.NewEncoder(buf)
|
||||
enc.SetIndent("", " ")
|
||||
for {
|
||||
var obj otelutil.Span
|
||||
if err := dec.Decode(&obj); err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i, att := range obj.Attributes {
|
||||
v := att.Value
|
||||
if v.Type() == attribute.STRING {
|
||||
obj.Attributes[i].Value = attribute.StringValue(sanitizeCommand(v.AsString()))
|
||||
}
|
||||
}
|
||||
|
||||
if err := enc.Encode(obj); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
buffer := contentutil.NewBuffer()
|
||||
newDesc := ocispecs.Descriptor{
|
||||
MediaType: desc.MediaType,
|
||||
Size: int64(buf.Len()),
|
||||
Digest: digest.FromBytes(buf.Bytes()),
|
||||
}
|
||||
if err := content.WriteBlob(ctx, buffer, "trace-sanitized", bytes.NewReader(buf.Bytes()), newDesc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mp.Add(newDesc.Digest, buffer)
|
||||
|
||||
return &newDesc, nil
|
||||
}
|
45
util/gitutil/credentials_test.go
Normal file
45
util/gitutil/credentials_test.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package gitutil
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestStripCredentials(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
url string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "non-blank Password",
|
||||
url: "https://user:password@host.tld/this:that",
|
||||
want: "https://host.tld/this:that",
|
||||
},
|
||||
{
|
||||
name: "blank Password",
|
||||
url: "https://user@host.tld/this:that",
|
||||
want: "https://host.tld/this:that",
|
||||
},
|
||||
{
|
||||
name: "blank Username",
|
||||
url: "https://:password@host.tld/this:that",
|
||||
want: "https://host.tld/this:that",
|
||||
},
|
||||
{
|
||||
name: "blank Username, blank Password",
|
||||
url: "https://host.tld/this:that",
|
||||
want: "https://host.tld/this:that",
|
||||
},
|
||||
{
|
||||
name: "invalid URL",
|
||||
url: "1https://foo.com",
|
||||
want: "1https://foo.com",
|
||||
},
|
||||
}
|
||||
for _, tt := range cases {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if g, w := stripCredentials(tt.url), tt.want; g != w {
|
||||
t.Fatalf("got: %q\nwant: %q", g, w)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@@ -1,14 +1,15 @@
|
||||
package gitutil
|
||||
package gittestutil
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/buildx/util/gitutil"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func GitInit(c *Git, tb testing.TB) {
|
||||
func GitInit(c *gitutil.Git, tb testing.TB) {
|
||||
tb.Helper()
|
||||
out, err := fakeGit(c, "init")
|
||||
require.NoError(tb, err)
|
||||
@@ -18,41 +19,41 @@ func GitInit(c *Git, tb testing.TB) {
|
||||
_, _ = fakeGit(c, "branch", "-D", "master")
|
||||
}
|
||||
|
||||
func GitCommit(c *Git, tb testing.TB, msg string) {
|
||||
func GitCommit(c *gitutil.Git, tb testing.TB, msg string) {
|
||||
tb.Helper()
|
||||
out, err := fakeGit(c, "commit", "--allow-empty", "-m", msg)
|
||||
require.NoError(tb, err)
|
||||
require.Contains(tb, out, "main", msg)
|
||||
}
|
||||
|
||||
func GitTag(c *Git, tb testing.TB, tag string) {
|
||||
func GitTag(c *gitutil.Git, tb testing.TB, tag string) {
|
||||
tb.Helper()
|
||||
out, err := fakeGit(c, "tag", tag)
|
||||
require.NoError(tb, err)
|
||||
require.Empty(tb, out)
|
||||
}
|
||||
|
||||
func GitCheckoutBranch(c *Git, tb testing.TB, name string) {
|
||||
func GitCheckoutBranch(c *gitutil.Git, tb testing.TB, name string) {
|
||||
tb.Helper()
|
||||
out, err := fakeGit(c, "checkout", "-b", name)
|
||||
require.NoError(tb, err)
|
||||
require.Empty(tb, out)
|
||||
}
|
||||
|
||||
func GitAdd(c *Git, tb testing.TB, files ...string) {
|
||||
func GitAdd(c *gitutil.Git, tb testing.TB, files ...string) {
|
||||
tb.Helper()
|
||||
args := append([]string{"add"}, files...)
|
||||
_, err := fakeGit(c, args...)
|
||||
require.NoError(tb, err)
|
||||
}
|
||||
|
||||
func GitSetRemote(c *Git, tb testing.TB, name string, url string) {
|
||||
func GitSetRemote(c *gitutil.Git, tb testing.TB, name string, url string) {
|
||||
tb.Helper()
|
||||
_, err := fakeGit(c, "remote", "add", name, url)
|
||||
require.NoError(tb, err)
|
||||
}
|
||||
|
||||
func GitSetMainUpstream(c *Git, tb testing.TB, remote, target string) {
|
||||
func GitSetMainUpstream(c *gitutil.Git, tb testing.TB, remote, target string) {
|
||||
tb.Helper()
|
||||
_, err := fakeGit(c, "fetch", "--depth", "1", remote, target)
|
||||
require.NoError(tb, err)
|
||||
@@ -73,7 +74,7 @@ func Mktmp(tb testing.TB) string {
|
||||
return folder
|
||||
}
|
||||
|
||||
func fakeGit(c *Git, args ...string) (string, error) {
|
||||
func fakeGit(c *gitutil.Git, args ...string) (string, error) {
|
||||
allArgs := []string{
|
||||
"-c", "user.name=buildx",
|
||||
"-c", "user.email=buildx@docker.com",
|
||||
@@ -82,7 +83,7 @@ func fakeGit(c *Git, args ...string) (string, error) {
|
||||
"-c", "log.showSignature=false",
|
||||
}
|
||||
allArgs = append(allArgs, args...)
|
||||
return c.clean(c.run(allArgs...))
|
||||
return c.Run(allArgs...)
|
||||
}
|
||||
|
||||
func IsAmbiguousArgument(err error) bool {
|
@@ -1,4 +1,4 @@
|
||||
package gitutil
|
||||
package gittestutil
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/buildx/util/gitutil"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@@ -23,7 +24,7 @@ func WithAccessToken(token string) GitServeOpt {
|
||||
}
|
||||
}
|
||||
|
||||
func GitServeHTTP(c *Git, t testing.TB, opts ...GitServeOpt) (url string) {
|
||||
func GitServeHTTP(c *gitutil.Git, t testing.TB, opts ...GitServeOpt) (url string) {
|
||||
t.Helper()
|
||||
gitUpdateServerInfo(c, t)
|
||||
ctx, cancel := context.WithCancelCause(context.TODO())
|
||||
@@ -91,7 +92,7 @@ func GitServeHTTP(c *Git, t testing.TB, opts ...GitServeOpt) (url string) {
|
||||
return fmt.Sprintf("http://%s/%s", addr, name)
|
||||
}
|
||||
|
||||
func gitUpdateServerInfo(c *Git, tb testing.TB) {
|
||||
func gitUpdateServerInfo(c *gitutil.Git, tb testing.TB) {
|
||||
tb.Helper()
|
||||
_, err := fakeGit(c, "update-server-info")
|
||||
require.NoError(tb, err)
|
@@ -57,17 +57,17 @@ func New(opts ...Option) (*Git, error) {
|
||||
}
|
||||
|
||||
func (c *Git) IsInsideWorkTree() bool {
|
||||
out, err := c.clean(c.run("rev-parse", "--is-inside-work-tree"))
|
||||
out, err := c.Run("rev-parse", "--is-inside-work-tree")
|
||||
return out == "true" && err == nil
|
||||
}
|
||||
|
||||
func (c *Git) IsDirty() bool {
|
||||
out, err := c.run("status", "--porcelain", "--ignored")
|
||||
out, err := c.Run("status", "--porcelain", "--ignored")
|
||||
return strings.TrimSpace(out) != "" || err != nil
|
||||
}
|
||||
|
||||
func (c *Git) RootDir() (string, error) {
|
||||
root, err := c.clean(c.run("rev-parse", "--show-toplevel"))
|
||||
root, err := c.Run("rev-parse", "--show-toplevel")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -127,6 +127,10 @@ func (c *Git) Tag() (string, error) {
|
||||
return tag, err
|
||||
}
|
||||
|
||||
func (c *Git) Run(args ...string) (string, error) {
|
||||
return c.clean(c.run(args...))
|
||||
}
|
||||
|
||||
func (c *Git) run(args ...string) (string, error) {
|
||||
var extraArgs = []string{
|
||||
"-c", "log.showSignature=false",
|
||||
@@ -161,7 +165,7 @@ func (c *Git) clean(out string, err error) (string, error) {
|
||||
}
|
||||
|
||||
func (c *Git) currentRemote() (string, error) {
|
||||
symref, err := c.clean(c.run("symbolic-ref", "-q", "HEAD"))
|
||||
symref, err := c.Run("symbolic-ref", "-q", "HEAD")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -169,7 +173,7 @@ func (c *Git) currentRemote() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
// git for-each-ref --format='%(upstream:remotename)'
|
||||
remote, err := c.clean(c.run("for-each-ref", "--format=%(upstream:remotename)", symref))
|
||||
remote, err := c.Run("for-each-ref", "--format=%(upstream:remotename)", symref)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@@ -1,32 +1,34 @@
|
||||
package gitutil
|
||||
package gitutil_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/buildx/util/gitutil"
|
||||
"github.com/docker/buildx/util/gitutil/gittestutil"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGit(t *testing.T) {
|
||||
c, err := New()
|
||||
c, err := gitutil.New()
|
||||
require.NoError(t, err)
|
||||
|
||||
out, err := c.run("status")
|
||||
out, err := c.Run("status")
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, out)
|
||||
|
||||
out, err = c.clean(c.run("not-exist"))
|
||||
out, err = c.Run("not-exist")
|
||||
require.Error(t, err)
|
||||
require.Empty(t, out)
|
||||
require.Equal(t, "git: 'not-exist' is not a git command. See 'git --help'.", err.Error())
|
||||
}
|
||||
|
||||
func TestGitFullCommit(t *testing.T) {
|
||||
Mktmp(t)
|
||||
c, err := New()
|
||||
gittestutil.Mktmp(t)
|
||||
c, err := gitutil.New()
|
||||
require.NoError(t, err)
|
||||
|
||||
GitInit(c, t)
|
||||
GitCommit(c, t, "bar")
|
||||
gittestutil.GitInit(c, t)
|
||||
gittestutil.GitCommit(c, t, "bar")
|
||||
|
||||
out, err := c.FullCommit()
|
||||
require.NoError(t, err)
|
||||
@@ -34,12 +36,12 @@ func TestGitFullCommit(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGitShortCommit(t *testing.T) {
|
||||
Mktmp(t)
|
||||
c, err := New()
|
||||
gittestutil.Mktmp(t)
|
||||
c, err := gitutil.New()
|
||||
require.NoError(t, err)
|
||||
|
||||
GitInit(c, t)
|
||||
GitCommit(c, t, "bar")
|
||||
gittestutil.GitInit(c, t)
|
||||
gittestutil.GitCommit(c, t, "bar")
|
||||
|
||||
out, err := c.ShortCommit()
|
||||
require.NoError(t, err)
|
||||
@@ -47,59 +49,59 @@ func TestGitShortCommit(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGitFullCommitErr(t *testing.T) {
|
||||
Mktmp(t)
|
||||
c, err := New()
|
||||
gittestutil.Mktmp(t)
|
||||
c, err := gitutil.New()
|
||||
require.NoError(t, err)
|
||||
|
||||
GitInit(c, t)
|
||||
gittestutil.GitInit(c, t)
|
||||
|
||||
_, err = c.FullCommit()
|
||||
require.Error(t, err)
|
||||
require.True(t, IsUnknownRevision(err))
|
||||
require.False(t, IsAmbiguousArgument(err))
|
||||
require.True(t, gitutil.IsUnknownRevision(err))
|
||||
require.False(t, gittestutil.IsAmbiguousArgument(err))
|
||||
}
|
||||
|
||||
func TestGitShortCommitErr(t *testing.T) {
|
||||
Mktmp(t)
|
||||
c, err := New()
|
||||
gittestutil.Mktmp(t)
|
||||
c, err := gitutil.New()
|
||||
require.NoError(t, err)
|
||||
|
||||
GitInit(c, t)
|
||||
gittestutil.GitInit(c, t)
|
||||
|
||||
_, err = c.ShortCommit()
|
||||
require.Error(t, err)
|
||||
require.True(t, IsUnknownRevision(err))
|
||||
require.False(t, IsAmbiguousArgument(err))
|
||||
require.True(t, gitutil.IsUnknownRevision(err))
|
||||
require.False(t, gittestutil.IsAmbiguousArgument(err))
|
||||
}
|
||||
|
||||
func TestGitTagsPointsAt(t *testing.T) {
|
||||
Mktmp(t)
|
||||
c, err := New()
|
||||
gittestutil.Mktmp(t)
|
||||
c, err := gitutil.New()
|
||||
require.NoError(t, err)
|
||||
|
||||
GitInit(c, t)
|
||||
GitCommit(c, t, "bar")
|
||||
GitTag(c, t, "v0.8.0")
|
||||
GitCommit(c, t, "foo")
|
||||
GitTag(c, t, "v0.9.0")
|
||||
gittestutil.GitInit(c, t)
|
||||
gittestutil.GitCommit(c, t, "bar")
|
||||
gittestutil.GitTag(c, t, "v0.8.0")
|
||||
gittestutil.GitCommit(c, t, "foo")
|
||||
gittestutil.GitTag(c, t, "v0.9.0")
|
||||
|
||||
out, err := c.clean(c.run("tag", "--points-at", "HEAD", "--sort", "-version:creatordate"))
|
||||
out, err := c.Run("tag", "--points-at", "HEAD", "--sort", "-version:creatordate")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "v0.9.0", out)
|
||||
}
|
||||
|
||||
func TestGitDescribeTags(t *testing.T) {
|
||||
Mktmp(t)
|
||||
c, err := New()
|
||||
gittestutil.Mktmp(t)
|
||||
c, err := gitutil.New()
|
||||
require.NoError(t, err)
|
||||
|
||||
GitInit(c, t)
|
||||
GitCommit(c, t, "bar")
|
||||
GitTag(c, t, "v0.8.0")
|
||||
GitCommit(c, t, "foo")
|
||||
GitTag(c, t, "v0.9.0")
|
||||
gittestutil.GitInit(c, t)
|
||||
gittestutil.GitCommit(c, t, "bar")
|
||||
gittestutil.GitTag(c, t, "v0.8.0")
|
||||
gittestutil.GitCommit(c, t, "foo")
|
||||
gittestutil.GitTag(c, t, "v0.9.0")
|
||||
|
||||
out, err := c.clean(c.run("describe", "--tags", "--abbrev=0"))
|
||||
out, err := c.Run("describe", "--tags", "--abbrev=0")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "v0.9.0", out)
|
||||
}
|
||||
@@ -200,16 +202,16 @@ func TestGitRemoteURL(t *testing.T) {
|
||||
for _, tt := range cases {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
Mktmp(t)
|
||||
c, err := New()
|
||||
gittestutil.Mktmp(t)
|
||||
c, err := gitutil.New()
|
||||
require.NoError(t, err)
|
||||
|
||||
GitInit(c, t)
|
||||
GitCommit(c, t, "initial commit")
|
||||
gittestutil.GitInit(c, t)
|
||||
gittestutil.GitCommit(c, t, "initial commit")
|
||||
for _, r := range tt.remotes {
|
||||
GitSetRemote(c, t, r.name, r.url)
|
||||
gittestutil.GitSetRemote(c, t, r.name, r.url)
|
||||
if r.tracking != "" {
|
||||
GitSetMainUpstream(c, t, r.name, r.tracking)
|
||||
gittestutil.GitSetMainUpstream(c, t, r.name, r.tracking)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,45 +225,3 @@ func TestGitRemoteURL(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStripCredentials(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
url string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "non-blank Password",
|
||||
url: "https://user:password@host.tld/this:that",
|
||||
want: "https://host.tld/this:that",
|
||||
},
|
||||
{
|
||||
name: "blank Password",
|
||||
url: "https://user@host.tld/this:that",
|
||||
want: "https://host.tld/this:that",
|
||||
},
|
||||
{
|
||||
name: "blank Username",
|
||||
url: "https://:password@host.tld/this:that",
|
||||
want: "https://host.tld/this:that",
|
||||
},
|
||||
{
|
||||
name: "blank Username, blank Password",
|
||||
url: "https://host.tld/this:that",
|
||||
want: "https://host.tld/this:that",
|
||||
},
|
||||
{
|
||||
name: "invalid URL",
|
||||
url: "1https://foo.com",
|
||||
want: "1https://foo.com",
|
||||
},
|
||||
}
|
||||
for _, tt := range cases {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if g, w := stripCredentials(tt.url), tt.want; g != w {
|
||||
t.Fatalf("got: %q\nwant: %q", g, w)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@@ -107,9 +107,7 @@ func (r *Resolver) Combine(ctx context.Context, srcs []*Source, ann map[exptypes
|
||||
if old.Annotations == nil {
|
||||
old.Annotations = map[string]string{}
|
||||
}
|
||||
for k, v := range d.Annotations {
|
||||
old.Annotations[k] = v
|
||||
}
|
||||
maps.Copy(old.Annotations, d.Annotations)
|
||||
newDescs[idx] = old
|
||||
} else {
|
||||
m[d.Digest] = len(newDescs)
|
||||
|
@@ -6,6 +6,7 @@ import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"maps"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
@@ -126,13 +127,9 @@ func (l *loader) Load(ctx context.Context, ref string) (*result, error) {
|
||||
}
|
||||
|
||||
var a asset
|
||||
annotations := make(map[string]string, len(mfst.manifest.Annotations)+len(mfst.desc.Annotations))
|
||||
for k, v := range mfst.desc.Annotations {
|
||||
annotations[k] = v
|
||||
}
|
||||
for k, v := range mfst.manifest.Annotations {
|
||||
annotations[k] = v
|
||||
}
|
||||
annotations := map[string]string{}
|
||||
maps.Copy(annotations, mfst.desc.Annotations)
|
||||
maps.Copy(annotations, mfst.manifest.Annotations)
|
||||
|
||||
if err := l.scanConfig(ctx, fetcher, mfst.manifest.Config, &a); err != nil {
|
||||
return nil, err
|
||||
|
@@ -189,7 +189,7 @@ func TestMuxIO(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
type instruction func(m *MuxIO) (intput string, writeBackView string)
|
||||
type instruction func(m *MuxIO) (input string, writeBackView string)
|
||||
|
||||
func input(s string) instruction {
|
||||
return func(m *MuxIO) (string, string) {
|
||||
|
@@ -13,7 +13,7 @@ function "add" {
|
||||
|
||||
function "list" {
|
||||
params = []
|
||||
variadic_param = items
|
||||
variadic_params = items
|
||||
result = items
|
||||
}
|
||||
```
|
||||
|
@@ -1,79 +0,0 @@
|
||||
package userfunc
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
)
|
||||
|
||||
var funcBodySchema = &hcl.BodySchema{
|
||||
Attributes: []hcl.AttributeSchema{
|
||||
{
|
||||
Name: "params",
|
||||
Required: true,
|
||||
},
|
||||
{
|
||||
Name: "variadic_param",
|
||||
Required: false,
|
||||
},
|
||||
{
|
||||
Name: "result",
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func decodeUserFunctions(body hcl.Body, blockType string, contextFunc ContextFunc) (funcs map[string]function.Function, remain hcl.Body, diags hcl.Diagnostics) {
|
||||
schema := &hcl.BodySchema{
|
||||
Blocks: []hcl.BlockHeaderSchema{
|
||||
{
|
||||
Type: blockType,
|
||||
LabelNames: []string{"name"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
content, remain, diags := body.PartialContent(schema)
|
||||
if diags.HasErrors() {
|
||||
return nil, remain, diags
|
||||
}
|
||||
|
||||
// first call to getBaseCtx will populate context, and then the same
|
||||
// context will be used for all subsequent calls. It's assumed that
|
||||
// all functions in a given body should see an identical context.
|
||||
var baseCtx *hcl.EvalContext
|
||||
getBaseCtx := func() *hcl.EvalContext {
|
||||
if baseCtx == nil {
|
||||
if contextFunc != nil {
|
||||
baseCtx = contextFunc()
|
||||
}
|
||||
}
|
||||
// baseCtx might still be nil here, and that's okay
|
||||
return baseCtx
|
||||
}
|
||||
|
||||
funcs = make(map[string]function.Function)
|
||||
|
||||
for _, block := range content.Blocks {
|
||||
name := block.Labels[0]
|
||||
funcContent, funcDiags := block.Body.Content(funcBodySchema)
|
||||
diags = append(diags, funcDiags...)
|
||||
if funcDiags.HasErrors() {
|
||||
continue
|
||||
}
|
||||
|
||||
paramsExpr := funcContent.Attributes["params"].Expr
|
||||
resultExpr := funcContent.Attributes["result"].Expr
|
||||
var varParamExpr hcl.Expression
|
||||
if funcContent.Attributes["variadic_param"] != nil {
|
||||
varParamExpr = funcContent.Attributes["variadic_param"].Expr
|
||||
}
|
||||
f, funcDiags := NewFunction(paramsExpr, varParamExpr, resultExpr, getBaseCtx)
|
||||
if funcDiags.HasErrors() {
|
||||
diags = append(diags, funcDiags...)
|
||||
continue
|
||||
}
|
||||
funcs[name] = f
|
||||
}
|
||||
|
||||
return funcs, remain, diags
|
||||
}
|
@@ -20,28 +20,6 @@ import (
|
||||
// structure a function declaration is found, etc.
|
||||
type ContextFunc func() *hcl.EvalContext
|
||||
|
||||
// DecodeUserFunctions looks for blocks of the given type in the given body
|
||||
// and, for each one found, interprets it as a custom function definition.
|
||||
//
|
||||
// On success, the result is a mapping of function names to implementations,
|
||||
// along with a new body that represents the remaining content of the given
|
||||
// body which can be used for further processing.
|
||||
//
|
||||
// The result expression of each function is parsed during decoding but not
|
||||
// evaluated until the function is called.
|
||||
//
|
||||
// If the given ContextFunc is non-nil, it will be called to obtain the
|
||||
// context in which the function result expressions will be evaluated. If nil,
|
||||
// or if it returns nil, the result expression will have access only to
|
||||
// variables named after the declared parameters. A non-nil context turns
|
||||
// the returned functions into closures, bound to the given context.
|
||||
//
|
||||
// If the returned diagnostics set has errors then the function map and
|
||||
// remain body may be nil or incomplete.
|
||||
func DecodeUserFunctions(body hcl.Body, blockType string, context ContextFunc) (funcs map[string]function.Function, remain hcl.Body, diags hcl.Diagnostics) {
|
||||
return decodeUserFunctions(body, blockType, context)
|
||||
}
|
||||
|
||||
// NewFunction creates a new function instance from preparsed HCL expressions.
|
||||
func NewFunction(paramsExpr, varParamExpr, resultExpr hcl.Expression, getBaseCtx func() *hcl.EvalContext) (function.Function, hcl.Diagnostics) {
|
||||
var params []string
|
||||
@@ -69,7 +47,7 @@ func NewFunction(paramsExpr, varParamExpr, resultExpr hcl.Expression, getBaseCtx
|
||||
if varParam == "" {
|
||||
return function.Function{}, hcl.Diagnostics{{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid variadic_param",
|
||||
Summary: "Invalid variadic_params",
|
||||
Detail: "The variadic parameter name must be an identifier.",
|
||||
Subject: varParamExpr.Range().Ptr(),
|
||||
}}
|
||||
|
10
vendor/github.com/compose-spec/compose-go/v2/cli/options.go
generated
vendored
10
vendor/github.com/compose-spec/compose-go/v2/cli/options.go
generated
vendored
@@ -18,7 +18,6 @@ package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -30,7 +29,6 @@ import (
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/consts"
|
||||
"github.com/compose-spec/compose-go/v2/dotenv"
|
||||
"github.com/compose-spec/compose-go/v2/errdefs"
|
||||
"github.com/compose-spec/compose-go/v2/loader"
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/compose-spec/compose-go/v2/utils"
|
||||
@@ -551,14 +549,6 @@ func withListeners(options *ProjectOptions) func(*loader.Options) {
|
||||
}
|
||||
}
|
||||
|
||||
// getConfigPaths retrieves the config files for project based on project options
|
||||
func (o *ProjectOptions) getConfigPaths() ([]string, error) {
|
||||
if len(o.ConfigPaths) != 0 {
|
||||
return absolutePaths(o.ConfigPaths)
|
||||
}
|
||||
return nil, fmt.Errorf("no configuration file provided: %w", errdefs.ErrNotFound)
|
||||
}
|
||||
|
||||
func findFiles(names []string, pwd string) []string {
|
||||
candidates := []string{}
|
||||
for _, n := range names {
|
||||
|
4
vendor/github.com/compose-spec/compose-go/v2/dotenv/env.go
generated
vendored
4
vendor/github.com/compose-spec/compose-go/v2/dotenv/env.go
generated
vendored
@@ -35,7 +35,7 @@ func GetEnvFromFile(currentEnv map[string]string, filenames []string) (map[strin
|
||||
|
||||
s, err := os.Stat(dotEnvFile)
|
||||
if os.IsNotExist(err) {
|
||||
return envMap, fmt.Errorf("Couldn't find env file: %s", dotEnvFile)
|
||||
return envMap, fmt.Errorf("couldn't find env file: %s", dotEnvFile)
|
||||
}
|
||||
if err != nil {
|
||||
return envMap, err
|
||||
@@ -50,7 +50,7 @@ func GetEnvFromFile(currentEnv map[string]string, filenames []string) (map[strin
|
||||
|
||||
b, err := os.ReadFile(dotEnvFile)
|
||||
if os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("Couldn't read env file: %s", dotEnvFile)
|
||||
return nil, fmt.Errorf("couldn't read env file: %s", dotEnvFile)
|
||||
}
|
||||
if err != nil {
|
||||
return envMap, err
|
||||
|
27
vendor/github.com/compose-spec/compose-go/v2/dotenv/format.go
generated
vendored
27
vendor/github.com/compose-spec/compose-go/v2/dotenv/format.go
generated
vendored
@@ -21,18 +21,31 @@ import (
|
||||
"io"
|
||||
)
|
||||
|
||||
var formats = map[string]Parser{}
|
||||
const DotEnv = ".env"
|
||||
|
||||
type Parser func(r io.Reader, filename string, lookup func(key string) (string, bool)) (map[string]string, error)
|
||||
var formats = map[string]Parser{
|
||||
DotEnv: func(r io.Reader, filename string, vars map[string]string, lookup func(key string) (string, bool)) error {
|
||||
err := parseWithLookup(r, vars, lookup)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read %s: %w", filename, err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
type Parser func(r io.Reader, filename string, vars map[string]string, lookup func(key string) (string, bool)) error
|
||||
|
||||
func RegisterFormat(format string, p Parser) {
|
||||
formats[format] = p
|
||||
}
|
||||
|
||||
func ParseWithFormat(r io.Reader, filename string, resolve LookupFn, format string) (map[string]string, error) {
|
||||
parser, ok := formats[format]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unsupported env_file format %q", format)
|
||||
func ParseWithFormat(r io.Reader, filename string, vars map[string]string, resolve LookupFn, format string) error {
|
||||
if format == "" {
|
||||
format = DotEnv
|
||||
}
|
||||
return parser(r, filename, resolve)
|
||||
fn, ok := formats[format]
|
||||
if !ok {
|
||||
return fmt.Errorf("unsupported env_file format %q", format)
|
||||
}
|
||||
return fn(r, filename, vars, resolve)
|
||||
}
|
||||
|
13
vendor/github.com/compose-spec/compose-go/v2/dotenv/godotenv.go
generated
vendored
13
vendor/github.com/compose-spec/compose-go/v2/dotenv/godotenv.go
generated
vendored
@@ -30,7 +30,7 @@ var startsWithDigitRegex = regexp.MustCompile(`^\s*\d.*`) // Keys starting with
|
||||
// LookupFn represents a lookup function to resolve variables from
|
||||
type LookupFn func(string) (string, bool)
|
||||
|
||||
var noLookupFn = func(s string) (string, bool) {
|
||||
var noLookupFn = func(_ string) (string, bool) {
|
||||
return "", false
|
||||
}
|
||||
|
||||
@@ -41,16 +41,23 @@ func Parse(r io.Reader) (map[string]string, error) {
|
||||
|
||||
// ParseWithLookup reads an env file from io.Reader, returning a map of keys and values.
|
||||
func ParseWithLookup(r io.Reader, lookupFn LookupFn) (map[string]string, error) {
|
||||
vars := map[string]string{}
|
||||
err := parseWithLookup(r, vars, lookupFn)
|
||||
return vars, err
|
||||
}
|
||||
|
||||
// ParseWithLookup reads an env file from io.Reader, returning a map of keys and values.
|
||||
func parseWithLookup(r io.Reader, vars map[string]string, lookupFn LookupFn) error {
|
||||
data, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
// seek past the UTF-8 BOM if it exists (particularly on Windows, some
|
||||
// editors tend to add it, and it'll cause parsing to fail)
|
||||
data = bytes.TrimPrefix(data, utf8BOM)
|
||||
|
||||
return UnmarshalBytesWithLookup(data, lookupFn)
|
||||
return newParser().parse(string(data), vars, lookupFn)
|
||||
}
|
||||
|
||||
// Load will read your env file(s) and load them into ENV for this process.
|
||||
|
4
vendor/github.com/compose-spec/compose-go/v2/dotenv/parser.go
generated
vendored
4
vendor/github.com/compose-spec/compose-go/v2/dotenv/parser.go
generated
vendored
@@ -115,7 +115,7 @@ loop:
|
||||
switch rune {
|
||||
case '=', ':', '\n':
|
||||
// library also supports yaml-style value declaration
|
||||
key = string(src[0:i])
|
||||
key = src[0:i]
|
||||
offset = i + 1
|
||||
inherited = rune == '\n'
|
||||
break loop
|
||||
@@ -157,7 +157,7 @@ func (p *parser) extractVarValue(src string, envMap map[string]string, lookupFn
|
||||
// Remove inline comments on unquoted lines
|
||||
value, _, _ = strings.Cut(value, " #")
|
||||
value = strings.TrimRightFunc(value, unicode.IsSpace)
|
||||
retVal, err := expandVariables(string(value), envMap, lookupFn)
|
||||
retVal, err := expandVariables(value, envMap, lookupFn)
|
||||
return retVal, rest, err
|
||||
}
|
||||
|
||||
|
2
vendor/github.com/compose-spec/compose-go/v2/graph/cycle.go
generated
vendored
2
vendor/github.com/compose-spec/compose-go/v2/graph/cycle.go
generated
vendored
@@ -18,11 +18,11 @@ package graph
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/compose-spec/compose-go/v2/utils"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
// CheckCycle analyze project's depends_on relation and report an error on cycle detection
|
||||
|
6
vendor/github.com/compose-spec/compose-go/v2/graph/traversal.go
generated
vendored
6
vendor/github.com/compose-spec/compose-go/v2/graph/traversal.go
generated
vendored
@@ -18,9 +18,9 @@ package graph
|
||||
|
||||
import (
|
||||
"context"
|
||||
"slices"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
@@ -63,9 +63,9 @@ func newTraversal[S, T any](fn CollectorFn[S, T]) *traversal[S, T] {
|
||||
}
|
||||
|
||||
// WithMaxConcurrency configure traversal to limit concurrency walking graph nodes
|
||||
func WithMaxConcurrency(max int) func(*Options) {
|
||||
func WithMaxConcurrency(concurrency int) func(*Options) {
|
||||
return func(o *Options) {
|
||||
o.maxConcurrency = max
|
||||
o.maxConcurrency = concurrency
|
||||
}
|
||||
}
|
||||
|
||||
|
5
vendor/github.com/compose-spec/compose-go/v2/loader/extends.go
generated
vendored
5
vendor/github.com/compose-spec/compose-go/v2/loader/extends.go
generated
vendored
@@ -113,11 +113,14 @@ func applyServiceExtends(ctx context.Context, name string, services map[string]a
|
||||
source := deepClone(base).(map[string]any)
|
||||
|
||||
for _, processor := range post {
|
||||
processor.Apply(map[string]any{
|
||||
err = processor.Apply(map[string]any{
|
||||
"services": map[string]any{
|
||||
name: source,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
merged, err := override.ExtendService(source, service)
|
||||
if err != nil {
|
||||
|
2
vendor/github.com/compose-spec/compose-go/v2/loader/interpolate.go
generated
vendored
2
vendor/github.com/compose-spec/compose-go/v2/loader/interpolate.go
generated
vendored
@@ -27,7 +27,6 @@ import (
|
||||
)
|
||||
|
||||
var interpolateTypeCastMapping = map[tree.Path]interp.Cast{
|
||||
servicePath("configs", tree.PathMatchList, "mode"): toInt,
|
||||
servicePath("cpu_count"): toInt64,
|
||||
servicePath("cpu_percent"): toFloat,
|
||||
servicePath("cpu_period"): toInt64,
|
||||
@@ -53,7 +52,6 @@ var interpolateTypeCastMapping = map[tree.Path]interp.Cast{
|
||||
servicePath("privileged"): toBoolean,
|
||||
servicePath("read_only"): toBoolean,
|
||||
servicePath("scale"): toInt,
|
||||
servicePath("secrets", tree.PathMatchList, "mode"): toInt,
|
||||
servicePath("stdin_open"): toBoolean,
|
||||
servicePath("tty"): toBoolean,
|
||||
servicePath("ulimits", tree.PathMatchAll): toInt,
|
||||
|
62
vendor/github.com/compose-spec/compose-go/v2/loader/loader.go
generated
vendored
62
vendor/github.com/compose-spec/compose-go/v2/loader/loader.go
generated
vendored
@@ -26,6 +26,7 @@ import (
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -42,7 +43,6 @@ import (
|
||||
"github.com/compose-spec/compose-go/v2/validation"
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/exp/slices"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
@@ -257,15 +257,6 @@ func WithProfiles(profiles []string) func(*Options) {
|
||||
}
|
||||
}
|
||||
|
||||
// ParseYAML reads the bytes from a file, parses the bytes into a mapping
|
||||
// structure, and returns it.
|
||||
func ParseYAML(source []byte) (map[string]interface{}, error) {
|
||||
r := bytes.NewReader(source)
|
||||
decoder := yaml.NewDecoder(r)
|
||||
m, _, err := parseYAML(decoder)
|
||||
return m, err
|
||||
}
|
||||
|
||||
// PostProcessor is used to tweak compose model based on metadata extracted during yaml Unmarshal phase
|
||||
// that hardly can be implemented using go-yaml and mapstructure
|
||||
type PostProcessor interface {
|
||||
@@ -275,32 +266,6 @@ type PostProcessor interface {
|
||||
Apply(interface{}) error
|
||||
}
|
||||
|
||||
func parseYAML(decoder *yaml.Decoder) (map[string]interface{}, PostProcessor, error) {
|
||||
var cfg interface{}
|
||||
processor := ResetProcessor{target: &cfg}
|
||||
|
||||
if err := decoder.Decode(&processor); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
stringMap, ok := cfg.(map[string]interface{})
|
||||
if ok {
|
||||
converted, err := convertToStringKeysRecursive(stringMap, "")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return converted.(map[string]interface{}), &processor, nil
|
||||
}
|
||||
cfgMap, ok := cfg.(map[interface{}]interface{})
|
||||
if !ok {
|
||||
return nil, nil, errors.New("Top-level object must be a mapping")
|
||||
}
|
||||
converted, err := convertToStringKeysRecursive(cfgMap, "")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return converted.(map[string]interface{}), &processor, nil
|
||||
}
|
||||
|
||||
// LoadConfigFiles ingests config files with ResourceLoader and returns config details with paths to local copies
|
||||
func LoadConfigFiles(ctx context.Context, configFiles []string, workingDir string, options ...func(*Options)) (*types.ConfigDetails, error) {
|
||||
if len(configFiles) < 1 {
|
||||
@@ -353,12 +318,6 @@ func LoadConfigFiles(ctx context.Context, configFiles []string, workingDir strin
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// Load reads a ConfigDetails and returns a fully loaded configuration.
|
||||
// Deprecated: use LoadWithContext.
|
||||
func Load(configDetails types.ConfigDetails, options ...func(*Options)) (*types.Project, error) {
|
||||
return LoadWithContext(context.Background(), configDetails, options...)
|
||||
}
|
||||
|
||||
// LoadWithContext reads a ConfigDetails and returns a fully loaded configuration as a compose-go Project
|
||||
func LoadWithContext(ctx context.Context, configDetails types.ConfigDetails, options ...func(*Options)) (*types.Project, error) {
|
||||
opts := toOptions(&configDetails, options)
|
||||
@@ -378,7 +337,7 @@ func LoadModelWithContext(ctx context.Context, configDetails types.ConfigDetails
|
||||
// LoadModelWithContext reads a ConfigDetails and returns a fully loaded configuration as a yaml dictionary
|
||||
func loadModelWithContext(ctx context.Context, configDetails *types.ConfigDetails, opts *Options) (map[string]any, error) {
|
||||
if len(configDetails.ConfigFiles) < 1 {
|
||||
return nil, errors.New("No files specified")
|
||||
return nil, errors.New("no compose file specified")
|
||||
}
|
||||
|
||||
err := projectName(configDetails, opts)
|
||||
@@ -448,7 +407,15 @@ func loadYamlModel(ctx context.Context, config types.ConfigDetails, opts *Option
|
||||
return dict, nil
|
||||
}
|
||||
|
||||
func loadYamlFile(ctx context.Context, file types.ConfigFile, opts *Options, workingDir string, environment types.Mapping, ct *cycleTracker, dict map[string]interface{}, included []string) (map[string]interface{}, PostProcessor, error) {
|
||||
func loadYamlFile(ctx context.Context,
|
||||
file types.ConfigFile,
|
||||
opts *Options,
|
||||
workingDir string,
|
||||
environment types.Mapping,
|
||||
ct *cycleTracker,
|
||||
dict map[string]interface{},
|
||||
included []string,
|
||||
) (map[string]interface{}, PostProcessor, error) {
|
||||
ctx = context.WithValue(ctx, consts.ComposeFileKey{}, file.Filename)
|
||||
if file.Content == nil && file.Config == nil {
|
||||
content, err := os.ReadFile(file.Filename)
|
||||
@@ -465,7 +432,7 @@ func loadYamlFile(ctx context.Context, file types.ConfigFile, opts *Options, wor
|
||||
}
|
||||
cfg, ok := converted.(map[string]interface{})
|
||||
if !ok {
|
||||
return errors.New("Top-level object must be a mapping")
|
||||
return errors.New("top-level object must be a mapping")
|
||||
}
|
||||
|
||||
if opts.Interpolate != nil && !opts.SkipInterpolation {
|
||||
@@ -565,7 +532,6 @@ func load(ctx context.Context, configDetails types.ConfigDetails, opts *Options,
|
||||
return nil, fmt.Errorf("include cycle detected:\n%s\n include %s", loaded[0], strings.Join(loaded[1:], "\n include "))
|
||||
}
|
||||
}
|
||||
loaded = append(loaded, mainFile)
|
||||
|
||||
dict, err := loadYamlModel(ctx, configDetails, opts, &cycleTracker{}, nil)
|
||||
if err != nil {
|
||||
@@ -576,7 +542,7 @@ func load(ctx context.Context, configDetails types.ConfigDetails, opts *Options,
|
||||
return nil, errors.New("empty compose file")
|
||||
}
|
||||
|
||||
if opts.projectName == "" {
|
||||
if !opts.SkipValidation && opts.projectName == "" {
|
||||
return nil, errors.New("project name must not be empty")
|
||||
}
|
||||
|
||||
@@ -911,7 +877,7 @@ func formatInvalidKeyError(keyPrefix string, key interface{}) error {
|
||||
} else {
|
||||
location = fmt.Sprintf("in %s", keyPrefix)
|
||||
}
|
||||
return fmt.Errorf("Non-string key %s: %#v", location, key)
|
||||
return fmt.Errorf("non-string key %s: %#v", location, key)
|
||||
}
|
||||
|
||||
// Windows path, c:\\my\\path\\shiny, need to be changed to be compatible with
|
||||
|
3
vendor/github.com/compose-spec/compose-go/v2/loader/omitEmpty.go
generated
vendored
3
vendor/github.com/compose-spec/compose-go/v2/loader/omitEmpty.go
generated
vendored
@@ -19,7 +19,8 @@ package loader
|
||||
import "github.com/compose-spec/compose-go/v2/tree"
|
||||
|
||||
var omitempty = []tree.Path{
|
||||
"services.*.dns"}
|
||||
"services.*.dns",
|
||||
}
|
||||
|
||||
// OmitEmpty removes empty attributes which are irrelevant when unset
|
||||
func OmitEmpty(yaml map[string]any) map[string]any {
|
||||
|
24
vendor/github.com/compose-spec/compose-go/v2/loader/paths.go
generated
vendored
24
vendor/github.com/compose-spec/compose-go/v2/loader/paths.go
generated
vendored
@@ -17,9 +17,7 @@
|
||||
package loader
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
)
|
||||
@@ -40,17 +38,6 @@ func ResolveRelativePaths(project *types.Project) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func absPath(workingDir string, filePath string) string {
|
||||
if strings.HasPrefix(filePath, "~") {
|
||||
home, _ := os.UserHomeDir()
|
||||
return filepath.Join(home, filePath[1:])
|
||||
}
|
||||
if filepath.IsAbs(filePath) {
|
||||
return filePath
|
||||
}
|
||||
return filepath.Join(workingDir, filePath)
|
||||
}
|
||||
|
||||
func absComposeFiles(composeFiles []string) ([]string, error) {
|
||||
for i, composeFile := range composeFiles {
|
||||
absComposefile, err := filepath.Abs(composeFile)
|
||||
@@ -61,14 +48,3 @@ func absComposeFiles(composeFiles []string) ([]string, error) {
|
||||
}
|
||||
return composeFiles, nil
|
||||
}
|
||||
|
||||
func resolvePaths(basePath string, in types.StringList) types.StringList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
ret := make(types.StringList, len(in))
|
||||
for i := range in {
|
||||
ret[i] = absPath(basePath, in[i])
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
6
vendor/github.com/compose-spec/compose-go/v2/loader/reset.go
generated
vendored
6
vendor/github.com/compose-spec/compose-go/v2/loader/reset.go
generated
vendored
@@ -67,6 +67,8 @@ func (p *ResetProcessor) resolveReset(node *yaml.Node, path tree.Path) (*yaml.No
|
||||
p.paths = append(p.paths, path)
|
||||
return node, nil
|
||||
}
|
||||
|
||||
keys := map[string]int{}
|
||||
switch node.Kind {
|
||||
case yaml.SequenceNode:
|
||||
var nodes []*yaml.Node
|
||||
@@ -87,6 +89,10 @@ func (p *ResetProcessor) resolveReset(node *yaml.Node, path tree.Path) (*yaml.No
|
||||
for idx, v := range node.Content {
|
||||
if idx%2 == 0 {
|
||||
key = v.Value
|
||||
if line, seen := keys[key]; seen {
|
||||
return nil, fmt.Errorf("line %d: mapping key %#v already defined at line %d", v.Line, key, line)
|
||||
}
|
||||
keys[key] = v.Line
|
||||
} else {
|
||||
resolved, err := p.resolveReset(v, path.Next(key))
|
||||
if err != nil {
|
||||
|
5
vendor/github.com/compose-spec/compose-go/v2/loader/validate.go
generated
vendored
5
vendor/github.com/compose-spec/compose-go/v2/loader/validate.go
generated
vendored
@@ -27,9 +27,9 @@ import (
|
||||
)
|
||||
|
||||
// checkConsistency validate a compose model is consistent
|
||||
func checkConsistency(project *types.Project) error {
|
||||
func checkConsistency(project *types.Project) error { //nolint:gocyclo
|
||||
for name, s := range project.Services {
|
||||
if s.Build == nil && s.Image == "" {
|
||||
if s.Build == nil && s.Image == "" && s.Provider == nil {
|
||||
return fmt.Errorf("service %q has neither an image nor a build context specified: %w", s.Name, errdefs.ErrInvalid)
|
||||
}
|
||||
|
||||
@@ -171,7 +171,6 @@ func checkConsistency(project *types.Project) error {
|
||||
return fmt.Errorf("services.%s.develop.watch: target is required for non-rebuild actions: %w", s.Name, errdefs.ErrInvalid)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
18
vendor/github.com/compose-spec/compose-go/v2/override/merge.go
generated
vendored
18
vendor/github.com/compose-spec/compose-go/v2/override/merge.go
generated
vendored
@@ -19,10 +19,9 @@ package override
|
||||
import (
|
||||
"cmp"
|
||||
"fmt"
|
||||
"strings"
|
||||
"slices"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/tree"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
// Merge applies overrides to a config model
|
||||
@@ -62,6 +61,7 @@ func init() {
|
||||
mergeSpecials["services.*.extra_hosts"] = mergeExtraHosts
|
||||
mergeSpecials["services.*.healthcheck.test"] = override
|
||||
mergeSpecials["services.*.labels"] = mergeToSequence
|
||||
mergeSpecials["services.*.volumes.*.volume.labels"] = mergeToSequence
|
||||
mergeSpecials["services.*.logging"] = mergeLogging
|
||||
mergeSpecials["services.*.networks"] = mergeNetworks
|
||||
mergeSpecials["services.*.sysctls"] = mergeToSequence
|
||||
@@ -104,7 +104,7 @@ func mergeYaml(e any, o any, p tree.Path) (any, error) {
|
||||
func mergeMappings(mapping map[string]any, other map[string]any, p tree.Path) (map[string]any, error) {
|
||||
for k, v := range other {
|
||||
e, ok := mapping[k]
|
||||
if !ok || strings.HasPrefix(k, "x-") {
|
||||
if !ok {
|
||||
mapping[k] = v
|
||||
continue
|
||||
}
|
||||
@@ -227,9 +227,17 @@ func mergeUlimit(_ any, o any, p tree.Path) (any, error) {
|
||||
|
||||
func mergeIPAMConfig(c any, o any, path tree.Path) (any, error) {
|
||||
var ipamConfigs []any
|
||||
for _, original := range c.([]any) {
|
||||
configs, ok := c.([]any)
|
||||
if !ok {
|
||||
return o, fmt.Errorf("%s: unexpected type %T", path, c)
|
||||
}
|
||||
overrides, ok := o.([]any)
|
||||
if !ok {
|
||||
return o, fmt.Errorf("%s: unexpected type %T", path, c)
|
||||
}
|
||||
for _, original := range configs {
|
||||
right := convertIntoMapping(original, nil)
|
||||
for _, override := range o.([]any) {
|
||||
for _, override := range overrides {
|
||||
left := convertIntoMapping(override, nil)
|
||||
if left["subnet"] != right["subnet"] {
|
||||
// check if left is already in ipamConfigs, add it if not and continue with the next config
|
||||
|
1
vendor/github.com/compose-spec/compose-go/v2/paths/windows_path.go
generated
vendored
1
vendor/github.com/compose-spec/compose-go/v2/paths/windows_path.go
generated
vendored
@@ -44,7 +44,6 @@ func isWindowsAbs(path string) (b bool) {
|
||||
|
||||
// volumeNameLen returns length of the leading volume name on Windows.
|
||||
// It returns 0 elsewhere.
|
||||
// nolint: gocyclo
|
||||
func volumeNameLen(path string) int {
|
||||
if len(path) < 2 {
|
||||
return 0
|
||||
|
41
vendor/github.com/compose-spec/compose-go/v2/schema/compose-spec.json
generated
vendored
41
vendor/github.com/compose-spec/compose-go/v2/schema/compose-spec.json
generated
vendored
@@ -259,6 +259,20 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"provider": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {"type": "string"},
|
||||
"options": {
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^.+$": {"type": ["string", "number", "null"]}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
},
|
||||
"external_links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
|
||||
"extra_hosts": {"$ref": "#/definitions/extra_hosts"},
|
||||
"gpus": {"$ref": "#/definitions/gpus"},
|
||||
@@ -279,7 +293,6 @@
|
||||
"links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
|
||||
"logging": {
|
||||
"type": "object",
|
||||
|
||||
"properties": {
|
||||
"driver": {"type": "string"},
|
||||
"options": {
|
||||
@@ -370,9 +383,10 @@
|
||||
"pre_stop": {"type": "array", "items": {"$ref": "#/definitions/service_hook"}},
|
||||
"privileged": {"type": ["boolean", "string"]},
|
||||
"profiles": {"$ref": "#/definitions/list_of_strings"},
|
||||
"pull_policy": {"type": "string", "enum": [
|
||||
"always", "never", "if_not_present", "build", "missing"
|
||||
]},
|
||||
"pull_policy": {"type": "string",
|
||||
"pattern": "always|never|build|if_not_present|missing|refresh|daily|weekly|every_([0-9]+[wdhms])+"
|
||||
},
|
||||
"pull_refresh_after": {"type": "string"},
|
||||
"read_only": {"type": ["boolean", "string"]},
|
||||
"restart": {"type": "string"},
|
||||
"runtime": {
|
||||
@@ -404,7 +418,9 @@
|
||||
"type": "object",
|
||||
"required": ["type"],
|
||||
"properties": {
|
||||
"type": {"type": "string"},
|
||||
"type": {"type": "string",
|
||||
"enum": ["bind", "volume", "tmpfs", "cluster", "image"]
|
||||
},
|
||||
"source": {"type": "string"},
|
||||
"target": {"type": "string"},
|
||||
"read_only": {"type": ["boolean", "string"]},
|
||||
@@ -423,6 +439,7 @@
|
||||
"volume": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"labels": {"$ref": "#/definitions/list_or_dict"},
|
||||
"nocopy": {"type": ["boolean", "string"]},
|
||||
"subpath": {"type": "string"}
|
||||
},
|
||||
@@ -442,6 +459,14 @@
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
},
|
||||
"image": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"subpath": {"type": "string"}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
@@ -490,7 +515,8 @@
|
||||
"type": "object",
|
||||
"required": ["path", "action"],
|
||||
"properties": {
|
||||
"ignore": {"type": "array", "items": {"type": "string"}},
|
||||
"ignore": {"$ref": "#/definitions/string_or_list"},
|
||||
"include": {"$ref": "#/definitions/string_or_list"},
|
||||
"path": {"type": "string"},
|
||||
"action": {"type": "string", "enum": ["rebuild", "sync", "restart", "sync+restart", "sync+exec"]},
|
||||
"target": {"type": "string"},
|
||||
@@ -837,7 +863,8 @@
|
||||
"environment": {"$ref": "#/definitions/list_or_dict"}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {"^x-": {}}
|
||||
"patternProperties": {"^x-": {}},
|
||||
"required": ["command"]
|
||||
},
|
||||
|
||||
"env_file": {
|
||||
|
37
vendor/github.com/compose-spec/compose-go/v2/template/template.go
generated
vendored
37
vendor/github.com/compose-spec/compose-go/v2/template/template.go
generated
vendored
@@ -26,25 +26,28 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var delimiter = "\\$"
|
||||
var substitutionNamed = "[_a-z][_a-z0-9]*"
|
||||
var substitutionBraced = "[_a-z][_a-z0-9]*(?::?[-+?](.*))?"
|
||||
|
||||
var groupEscaped = "escaped"
|
||||
var groupNamed = "named"
|
||||
var groupBraced = "braced"
|
||||
var groupInvalid = "invalid"
|
||||
|
||||
var patternString = fmt.Sprintf(
|
||||
"%s(?i:(?P<%s>%s)|(?P<%s>%s)|{(?:(?P<%s>%s)}|(?P<%s>)))",
|
||||
delimiter,
|
||||
groupEscaped, delimiter,
|
||||
groupNamed, substitutionNamed,
|
||||
groupBraced, substitutionBraced,
|
||||
groupInvalid,
|
||||
const (
|
||||
delimiter = "\\$"
|
||||
substitutionNamed = "[_a-z][_a-z0-9]*"
|
||||
substitutionBraced = "[_a-z][_a-z0-9]*(?::?[-+?](.*))?"
|
||||
groupEscaped = "escaped"
|
||||
groupNamed = "named"
|
||||
groupBraced = "braced"
|
||||
groupInvalid = "invalid"
|
||||
)
|
||||
|
||||
var DefaultPattern = regexp.MustCompile(patternString)
|
||||
var (
|
||||
patternString = fmt.Sprintf(
|
||||
"%s(?i:(?P<%s>%s)|(?P<%s>%s)|{(?:(?P<%s>%s)}|(?P<%s>)))",
|
||||
delimiter,
|
||||
groupEscaped, delimiter,
|
||||
groupNamed, substitutionNamed,
|
||||
groupBraced, substitutionBraced,
|
||||
groupInvalid,
|
||||
)
|
||||
|
||||
DefaultPattern = regexp.MustCompile(patternString)
|
||||
)
|
||||
|
||||
// InvalidTemplateError is returned when a variable template is not in a valid
|
||||
// format
|
||||
|
2
vendor/github.com/compose-spec/compose-go/v2/transform/canonical.go
generated
vendored
2
vendor/github.com/compose-spec/compose-go/v2/transform/canonical.go
generated
vendored
@@ -44,6 +44,8 @@ func init() {
|
||||
transformers["services.*.build.ssh"] = transformSSH
|
||||
transformers["services.*.ulimits.*"] = transformUlimits
|
||||
transformers["services.*.build.ulimits.*"] = transformUlimits
|
||||
transformers["services.*.develop.watch.*.ignore"] = transformStringOrList
|
||||
transformers["services.*.develop.watch.*.include"] = transformStringOrList
|
||||
transformers["volumes.*"] = transformMaybeExternal
|
||||
transformers["networks.*"] = transformMaybeExternal
|
||||
transformers["secrets.*"] = transformMaybeExternal
|
||||
|
4
vendor/github.com/compose-spec/compose-go/v2/transform/devices.go
generated
vendored
4
vendor/github.com/compose-spec/compose-go/v2/transform/devices.go
generated
vendored
@@ -28,8 +28,8 @@ func deviceRequestDefaults(data any, p tree.Path, _ bool) (any, error) {
|
||||
return data, fmt.Errorf("%s: invalid type %T for device request", p, v)
|
||||
}
|
||||
_, hasCount := v["count"]
|
||||
_, hasIds := v["device_ids"]
|
||||
if !hasCount && !hasIds {
|
||||
_, hasIDs := v["device_ids"]
|
||||
if !hasCount && !hasIDs {
|
||||
v["count"] = "all"
|
||||
}
|
||||
return v, nil
|
||||
|
6
vendor/github.com/compose-spec/compose-go/v2/types/config.go
generated
vendored
6
vendor/github.com/compose-spec/compose-go/v2/types/config.go
generated
vendored
@@ -24,10 +24,8 @@ import (
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
// isCaseInsensitiveEnvVars is true on platforms where environment variable names are treated case-insensitively.
|
||||
isCaseInsensitiveEnvVars = (runtime.GOOS == "windows")
|
||||
)
|
||||
// isCaseInsensitiveEnvVars is true on platforms where environment variable names are treated case-insensitively.
|
||||
var isCaseInsensitiveEnvVars = (runtime.GOOS == "windows")
|
||||
|
||||
// ConfigDetails are the details about a group of ConfigFiles
|
||||
type ConfigDetails struct {
|
||||
|
455
vendor/github.com/compose-spec/compose-go/v2/types/derived.gen.go
generated
vendored
455
vendor/github.com/compose-spec/compose-go/v2/types/derived.gen.go
generated
vendored
File diff suppressed because it is too large
Load Diff
1
vendor/github.com/compose-spec/compose-go/v2/types/develop.go
generated
vendored
1
vendor/github.com/compose-spec/compose-go/v2/types/develop.go
generated
vendored
@@ -37,6 +37,7 @@ type Trigger struct {
|
||||
Action WatchAction `yaml:"action" json:"action"`
|
||||
Target string `yaml:"target,omitempty" json:"target,omitempty"`
|
||||
Exec ServiceHook `yaml:"exec,omitempty" json:"exec,omitempty"`
|
||||
Include []string `yaml:"include,omitempty" json:"include,omitempty"`
|
||||
Ignore []string `yaml:"ignore,omitempty" json:"ignore,omitempty"`
|
||||
Extensions Extensions `yaml:"#extensions,inline,omitempty" json:"-"`
|
||||
}
|
||||
|
4
vendor/github.com/compose-spec/compose-go/v2/types/duration.go
generated
vendored
4
vendor/github.com/compose-spec/compose-go/v2/types/duration.go
generated
vendored
@@ -21,6 +21,8 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/xhit/go-str2duration/v2"
|
||||
)
|
||||
|
||||
// Duration is a thin wrapper around time.Duration with improved JSON marshalling
|
||||
@@ -31,7 +33,7 @@ func (d Duration) String() string {
|
||||
}
|
||||
|
||||
func (d *Duration) DecodeMapstructure(value interface{}) error {
|
||||
v, err := time.ParseDuration(fmt.Sprint(value))
|
||||
v, err := str2duration.ParseDuration(fmt.Sprint(value))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
1
vendor/github.com/compose-spec/compose-go/v2/types/labels.go
generated
vendored
1
vendor/github.com/compose-spec/compose-go/v2/types/labels.go
generated
vendored
@@ -55,7 +55,6 @@ func (l Labels) AsList() []string {
|
||||
func (l Labels) ToMappingWithEquals() MappingWithEquals {
|
||||
mapping := MappingWithEquals{}
|
||||
for k, v := range l {
|
||||
v := v
|
||||
mapping[k] = &v
|
||||
}
|
||||
return mapping
|
||||
|
5
vendor/github.com/compose-spec/compose-go/v2/types/mapping.go
generated
vendored
5
vendor/github.com/compose-spec/compose-go/v2/types/mapping.go
generated
vendored
@@ -20,6 +20,7 @@ import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// MappingWithEquals is a mapping type that can be converted from a list of
|
||||
@@ -94,6 +95,9 @@ func (m *MappingWithEquals) DecodeMapstructure(value interface{}) error {
|
||||
mapping := make(MappingWithEquals, len(v))
|
||||
for _, s := range v {
|
||||
k, e, ok := strings.Cut(fmt.Sprint(s), "=")
|
||||
if unicode.IsSpace(rune(k[len(k)-1])) {
|
||||
return fmt.Errorf("environment variable %s is declared with a trailing space", k)
|
||||
}
|
||||
if !ok {
|
||||
mapping[k] = nil
|
||||
} else {
|
||||
@@ -157,7 +161,6 @@ func (m Mapping) Values() []string {
|
||||
func (m Mapping) ToMappingWithEquals() MappingWithEquals {
|
||||
mapping := MappingWithEquals{}
|
||||
for k, v := range m {
|
||||
v := v
|
||||
mapping[k] = &v
|
||||
}
|
||||
return mapping
|
||||
|
118
vendor/github.com/compose-spec/compose-go/v2/types/project.go
generated
vendored
118
vendor/github.com/compose-spec/compose-go/v2/types/project.go
generated
vendored
@@ -21,8 +21,10 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"maps"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"sort"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/dotenv"
|
||||
@@ -30,7 +32,6 @@ import (
|
||||
"github.com/compose-spec/compose-go/v2/utils"
|
||||
"github.com/distribution/reference"
|
||||
godigest "github.com/opencontainers/go-digest"
|
||||
"golang.org/x/exp/maps"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
@@ -120,21 +121,21 @@ func (p *Project) ServicesWithBuild() []string {
|
||||
servicesBuild := p.Services.Filter(func(s ServiceConfig) bool {
|
||||
return s.Build != nil && s.Build.Context != ""
|
||||
})
|
||||
return maps.Keys(servicesBuild)
|
||||
return slices.Collect(maps.Keys(servicesBuild))
|
||||
}
|
||||
|
||||
func (p *Project) ServicesWithExtends() []string {
|
||||
servicesExtends := p.Services.Filter(func(s ServiceConfig) bool {
|
||||
return s.Extends != nil && *s.Extends != (ExtendsConfig{})
|
||||
})
|
||||
return maps.Keys(servicesExtends)
|
||||
return slices.Collect(maps.Keys(servicesExtends))
|
||||
}
|
||||
|
||||
func (p *Project) ServicesWithDependsOn() []string {
|
||||
servicesDependsOn := p.Services.Filter(func(s ServiceConfig) bool {
|
||||
return len(s.DependsOn) > 0
|
||||
})
|
||||
return maps.Keys(servicesDependsOn)
|
||||
return slices.Collect(maps.Keys(servicesDependsOn))
|
||||
}
|
||||
|
||||
func (p *Project) ServicesWithCapabilities() ([]string, []string, []string) {
|
||||
@@ -156,9 +157,10 @@ func (p *Project) ServicesWithCapabilities() ([]string, []string, []string) {
|
||||
capabilities = append(capabilities, service.Name)
|
||||
}
|
||||
for _, c := range d.Capabilities {
|
||||
if c == "gpu" {
|
||||
switch c {
|
||||
case "gpu":
|
||||
gpu = append(gpu, service.Name)
|
||||
} else if c == "tpu" {
|
||||
case "tpu":
|
||||
tpu = append(tpu, service.Name)
|
||||
}
|
||||
}
|
||||
@@ -188,16 +190,25 @@ func (p *Project) getServicesByNames(names ...string) (Services, []string) {
|
||||
if len(names) == 0 {
|
||||
return p.Services, nil
|
||||
}
|
||||
|
||||
services := Services{}
|
||||
var servicesNotFound []string
|
||||
for _, name := range names {
|
||||
service, ok := p.Services[name]
|
||||
if !ok {
|
||||
servicesNotFound = append(servicesNotFound, name)
|
||||
continue
|
||||
matched := false
|
||||
|
||||
for serviceName, service := range p.Services {
|
||||
match, _ := filepath.Match(name, serviceName)
|
||||
if match {
|
||||
services[serviceName] = service
|
||||
matched = true
|
||||
}
|
||||
}
|
||||
|
||||
if !matched {
|
||||
servicesNotFound = append(servicesNotFound, name)
|
||||
}
|
||||
services[name] = service
|
||||
}
|
||||
|
||||
return services, servicesNotFound
|
||||
}
|
||||
|
||||
@@ -298,16 +309,25 @@ func (p *Project) withServices(names []string, fn ServiceFunc, seen map[string]b
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Project) GetDependentsForService(s ServiceConfig) []string {
|
||||
return utils.MapKeys(p.dependentsForService(s))
|
||||
func (p *Project) GetDependentsForService(s ServiceConfig, filter ...func(ServiceDependency) bool) []string {
|
||||
return utils.MapKeys(p.dependentsForService(s, filter...))
|
||||
}
|
||||
|
||||
func (p *Project) dependentsForService(s ServiceConfig) map[string]ServiceDependency {
|
||||
func (p *Project) dependentsForService(s ServiceConfig, filter ...func(ServiceDependency) bool) map[string]ServiceDependency {
|
||||
dependent := make(map[string]ServiceDependency)
|
||||
for _, service := range p.Services {
|
||||
for name, dependency := range service.DependsOn {
|
||||
if name == s.Name {
|
||||
dependent[service.Name] = dependency
|
||||
depends := true
|
||||
for _, f := range filter {
|
||||
if !f(dependency) {
|
||||
depends = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if depends {
|
||||
dependent[service.Name] = dependency
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -380,12 +400,7 @@ func (p *Project) WithServicesEnabled(names ...string) (*Project, error) {
|
||||
service := p.DisabledServices[name]
|
||||
profiles = append(profiles, service.Profiles...)
|
||||
}
|
||||
newProject, err := newProject.WithProfiles(profiles)
|
||||
if err != nil {
|
||||
return newProject, err
|
||||
}
|
||||
|
||||
return newProject.WithServicesEnvironmentResolved(true)
|
||||
return newProject.WithProfiles(profiles)
|
||||
}
|
||||
|
||||
// WithoutUnnecessaryResources drops networks/volumes/secrets/configs that are not referenced by active services
|
||||
@@ -477,7 +492,7 @@ func (p *Project) WithSelectedServices(names []string, options ...DependencyOpti
|
||||
}
|
||||
|
||||
set := utils.NewSet[string]()
|
||||
err := p.ForEachService(names, func(name string, service *ServiceConfig) error {
|
||||
err := p.ForEachService(names, func(name string, _ *ServiceConfig) error {
|
||||
set.Add(name)
|
||||
return nil
|
||||
}, options...)
|
||||
@@ -535,7 +550,7 @@ func (p *Project) WithServicesDisabled(names ...string) *Project {
|
||||
// WithImagesResolved updates services images to include digest computed by a resolver function
|
||||
// It returns a new Project instance with the changes and keep the original Project unchanged
|
||||
func (p *Project) WithImagesResolved(resolver func(named reference.Named) (godigest.Digest, error)) (*Project, error) {
|
||||
return p.WithServicesTransform(func(name string, service ServiceConfig) (ServiceConfig, error) {
|
||||
return p.WithServicesTransform(func(_ string, service ServiceConfig) (ServiceConfig, error) {
|
||||
if service.Image == "" {
|
||||
return service, nil
|
||||
}
|
||||
@@ -635,25 +650,25 @@ func (p Project) WithServicesEnvironmentResolved(discardEnvFiles bool) (*Project
|
||||
for i, service := range newProject.Services {
|
||||
service.Environment = service.Environment.Resolve(newProject.Environment.Resolve)
|
||||
|
||||
environment := MappingWithEquals{}
|
||||
// resolve variables based on other files we already parsed, + project's environment
|
||||
var resolve dotenv.LookupFn = func(s string) (string, bool) {
|
||||
v, ok := environment[s]
|
||||
if ok && v != nil {
|
||||
return *v, ok
|
||||
}
|
||||
return newProject.Environment.Resolve(s)
|
||||
}
|
||||
|
||||
environment := service.Environment.ToMapping()
|
||||
for _, envFile := range service.EnvFiles {
|
||||
vars, err := loadEnvFile(envFile, resolve)
|
||||
err := loadEnvFile(envFile, environment, func(k string) (string, bool) {
|
||||
// project.env has precedence doing interpolation
|
||||
if resolve, ok := p.Environment.Resolve(k); ok {
|
||||
return resolve, true
|
||||
}
|
||||
// then service.environment
|
||||
if s, ok := service.Environment[k]; ok {
|
||||
return *s, true
|
||||
}
|
||||
return "", false
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
environment.OverrideBy(vars.ToMappingWithEquals())
|
||||
}
|
||||
|
||||
service.Environment = environment.OverrideBy(service.Environment)
|
||||
service.Environment = environment.ToMappingWithEquals().OverrideBy(service.Environment)
|
||||
|
||||
if discardEnvFiles {
|
||||
service.EnvFiles = nil
|
||||
@@ -701,15 +716,16 @@ func (p Project) WithServicesLabelsResolved(discardLabelFiles bool) (*Project, e
|
||||
return newProject, nil
|
||||
}
|
||||
|
||||
func loadEnvFile(envFile EnvFile, resolve dotenv.LookupFn) (Mapping, error) {
|
||||
func loadEnvFile(envFile EnvFile, environment Mapping, resolve dotenv.LookupFn) error {
|
||||
if _, err := os.Stat(envFile.Path); os.IsNotExist(err) {
|
||||
if envFile.Required {
|
||||
return nil, fmt.Errorf("env file %s not found: %w", envFile.Path, err)
|
||||
return fmt.Errorf("env file %s not found: %w", envFile.Path, err)
|
||||
}
|
||||
return nil, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
return loadMappingFile(envFile.Path, envFile.Format, resolve)
|
||||
err := loadMappingFile(envFile.Path, envFile.Format, environment, resolve)
|
||||
return err
|
||||
}
|
||||
|
||||
func loadLabelFile(labelFile string, resolve dotenv.LookupFn) (Mapping, error) {
|
||||
@@ -717,26 +733,19 @@ func loadLabelFile(labelFile string, resolve dotenv.LookupFn) (Mapping, error) {
|
||||
return nil, fmt.Errorf("label file %s not found: %w", labelFile, err)
|
||||
}
|
||||
|
||||
return loadMappingFile(labelFile, "", resolve)
|
||||
labels := Mapping{}
|
||||
err := loadMappingFile(labelFile, "", labels, resolve)
|
||||
return labels, err
|
||||
}
|
||||
|
||||
func loadMappingFile(path string, format string, resolve dotenv.LookupFn) (Mapping, error) {
|
||||
func loadMappingFile(path string, format string, vars Mapping, resolve dotenv.LookupFn) error {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
defer file.Close() //nolint:errcheck
|
||||
defer file.Close()
|
||||
|
||||
var fileVars map[string]string
|
||||
if format != "" {
|
||||
fileVars, err = dotenv.ParseWithFormat(file, path, resolve, format)
|
||||
} else {
|
||||
fileVars, err = dotenv.ParseWithLookup(file, resolve)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return fileVars, nil
|
||||
return dotenv.ParseWithFormat(file, path, vars, resolve, format)
|
||||
}
|
||||
|
||||
func (p *Project) deepCopy() *Project {
|
||||
@@ -746,7 +755,6 @@ func (p *Project) deepCopy() *Project {
|
||||
n := &Project{}
|
||||
deriveDeepCopyProject(n, p)
|
||||
return n
|
||||
|
||||
}
|
||||
|
||||
// WithServicesTransform applies a transformation to project services and return a new project with transformation results
|
||||
|
102
vendor/github.com/compose-spec/compose-go/v2/types/types.go
generated
vendored
102
vendor/github.com/compose-spec/compose-go/v2/types/types.go
generated
vendored
@@ -20,9 +20,12 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/xhit/go-str2duration/v2"
|
||||
)
|
||||
|
||||
// ServiceConfig is the configuration of one service
|
||||
@@ -72,8 +75,8 @@ type ServiceConfig struct {
|
||||
// If set, overrides ENTRYPOINT from the image.
|
||||
//
|
||||
// Set to `[]` or an empty string to clear the entrypoint from the image.
|
||||
Entrypoint ShellCommand `yaml:"entrypoint,omitempty" json:"entrypoint"` // NOTE: we can NOT omitempty for JSON! see ShellCommand type for details.
|
||||
|
||||
Entrypoint ShellCommand `yaml:"entrypoint,omitempty" json:"entrypoint"` // NOTE: we can NOT omitempty for JSON! see ShellCommand type for details.
|
||||
Provider *ServiceProviderConfig `yaml:"provider,omitempty" json:"provider,omitempty"`
|
||||
Environment MappingWithEquals `yaml:"environment,omitempty" json:"environment,omitempty"`
|
||||
EnvFiles []EnvFile `yaml:"env_file,omitempty" json:"env_file,omitempty"`
|
||||
Expose StringOrNumberList `yaml:"expose,omitempty" json:"expose,omitempty"`
|
||||
@@ -139,6 +142,12 @@ type ServiceConfig struct {
|
||||
Extensions Extensions `yaml:"#extensions,inline,omitempty" json:"-"`
|
||||
}
|
||||
|
||||
type ServiceProviderConfig struct {
|
||||
Type string `yaml:"type,omitempty" json:"driver,omitempty"`
|
||||
Options Options `yaml:"options,omitempty" json:"options,omitempty"`
|
||||
Extensions Extensions `yaml:"#extensions,inline,omitempty" json:"-"`
|
||||
}
|
||||
|
||||
// MarshalYAML makes ServiceConfig implement yaml.Marshaller
|
||||
func (s ServiceConfig) MarshalYAML() (interface{}, error) {
|
||||
type t ServiceConfig
|
||||
@@ -215,6 +224,8 @@ const (
|
||||
PullPolicyMissing = "missing"
|
||||
// PullPolicyBuild force building images
|
||||
PullPolicyBuild = "build"
|
||||
// PullPolicyRefresh checks if image needs to be updated
|
||||
PullPolicyRefresh = "refresh"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -268,6 +279,27 @@ func (s ServiceConfig) GetDependents(p *Project) []string {
|
||||
return dependent
|
||||
}
|
||||
|
||||
func (s ServiceConfig) GetPullPolicy() (string, time.Duration, error) {
|
||||
switch s.PullPolicy {
|
||||
case PullPolicyAlways, PullPolicyNever, PullPolicyIfNotPresent, PullPolicyMissing, PullPolicyBuild:
|
||||
return s.PullPolicy, 0, nil
|
||||
case "daily":
|
||||
return PullPolicyRefresh, 24 * time.Hour, nil
|
||||
case "weekly":
|
||||
return PullPolicyRefresh, 7 * 24 * time.Hour, nil
|
||||
default:
|
||||
if strings.HasPrefix(s.PullPolicy, "every_") {
|
||||
delay := s.PullPolicy[6:]
|
||||
duration, err := str2duration.ParseDuration(delay)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
return PullPolicyRefresh, duration, nil
|
||||
}
|
||||
return PullPolicyMissing, 0, nil
|
||||
}
|
||||
}
|
||||
|
||||
// BuildConfig is a type for build
|
||||
type BuildConfig struct {
|
||||
Context string `yaml:"context,omitempty" json:"context,omitempty"`
|
||||
@@ -479,16 +511,13 @@ func ParsePortConfig(value string) ([]ServicePortConfig, error) {
|
||||
|
||||
for _, key := range keys {
|
||||
port := nat.Port(key)
|
||||
converted, err := convertPortToPortConfig(port, portBindings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
converted := convertPortToPortConfig(port, portBindings)
|
||||
portConfigs = append(portConfigs, converted...)
|
||||
}
|
||||
return portConfigs, nil
|
||||
}
|
||||
|
||||
func convertPortToPortConfig(port nat.Port, portBindings map[nat.Port][]nat.PortBinding) ([]ServicePortConfig, error) {
|
||||
func convertPortToPortConfig(port nat.Port, portBindings map[nat.Port][]nat.PortBinding) []ServicePortConfig {
|
||||
var portConfigs []ServicePortConfig
|
||||
for _, binding := range portBindings[port] {
|
||||
portConfigs = append(portConfigs, ServicePortConfig{
|
||||
@@ -499,7 +528,7 @@ func convertPortToPortConfig(port nat.Port, portBindings map[nat.Port][]nat.Port
|
||||
Mode: "ingress",
|
||||
})
|
||||
}
|
||||
return portConfigs, nil
|
||||
return portConfigs
|
||||
}
|
||||
|
||||
// ServiceVolumeConfig are references to a volume used by a service
|
||||
@@ -512,6 +541,7 @@ type ServiceVolumeConfig struct {
|
||||
Bind *ServiceVolumeBind `yaml:"bind,omitempty" json:"bind,omitempty"`
|
||||
Volume *ServiceVolumeVolume `yaml:"volume,omitempty" json:"volume,omitempty"`
|
||||
Tmpfs *ServiceVolumeTmpfs `yaml:"tmpfs,omitempty" json:"tmpfs,omitempty"`
|
||||
Image *ServiceVolumeImage `yaml:"image,omitempty" json:"image,omitempty"`
|
||||
|
||||
Extensions Extensions `yaml:"#extensions,inline,omitempty" json:"-"`
|
||||
}
|
||||
@@ -546,6 +576,8 @@ const (
|
||||
VolumeTypeNamedPipe = "npipe"
|
||||
// VolumeTypeCluster is the type for mounting container storage interface (CSI) volumes
|
||||
VolumeTypeCluster = "cluster"
|
||||
// VolumeTypeImage is the tpe for mounting an image
|
||||
VolumeTypeImage = "image"
|
||||
|
||||
// SElinuxShared share the volume content
|
||||
SElinuxShared = "z"
|
||||
@@ -589,8 +621,9 @@ const (
|
||||
|
||||
// ServiceVolumeVolume are options for a service volume of type volume
|
||||
type ServiceVolumeVolume struct {
|
||||
NoCopy bool `yaml:"nocopy,omitempty" json:"nocopy,omitempty"`
|
||||
Subpath string `yaml:"subpath,omitempty" json:"subpath,omitempty"`
|
||||
Labels Mapping `yaml:"labels,omitempty" json:"labels,omitempty"`
|
||||
NoCopy bool `yaml:"nocopy,omitempty" json:"nocopy,omitempty"`
|
||||
Subpath string `yaml:"subpath,omitempty" json:"subpath,omitempty"`
|
||||
|
||||
Extensions Extensions `yaml:"#extensions,inline,omitempty" json:"-"`
|
||||
}
|
||||
@@ -604,17 +637,56 @@ type ServiceVolumeTmpfs struct {
|
||||
Extensions Extensions `yaml:"#extensions,inline,omitempty" json:"-"`
|
||||
}
|
||||
|
||||
type ServiceVolumeImage struct {
|
||||
SubPath string `yaml:"subpath,omitempty" json:"subpath,omitempty"`
|
||||
Extensions Extensions `yaml:"#extensions,inline,omitempty" json:"-"`
|
||||
}
|
||||
|
||||
type FileMode int64
|
||||
|
||||
// FileReferenceConfig for a reference to a swarm file object
|
||||
type FileReferenceConfig struct {
|
||||
Source string `yaml:"source,omitempty" json:"source,omitempty"`
|
||||
Target string `yaml:"target,omitempty" json:"target,omitempty"`
|
||||
UID string `yaml:"uid,omitempty" json:"uid,omitempty"`
|
||||
GID string `yaml:"gid,omitempty" json:"gid,omitempty"`
|
||||
Mode *uint32 `yaml:"mode,omitempty" json:"mode,omitempty"`
|
||||
Source string `yaml:"source,omitempty" json:"source,omitempty"`
|
||||
Target string `yaml:"target,omitempty" json:"target,omitempty"`
|
||||
UID string `yaml:"uid,omitempty" json:"uid,omitempty"`
|
||||
GID string `yaml:"gid,omitempty" json:"gid,omitempty"`
|
||||
Mode *FileMode `yaml:"mode,omitempty" json:"mode,omitempty"`
|
||||
|
||||
Extensions Extensions `yaml:"#extensions,inline,omitempty" json:"-"`
|
||||
}
|
||||
|
||||
func (f *FileMode) DecodeMapstructure(value interface{}) error {
|
||||
switch v := value.(type) {
|
||||
case *FileMode:
|
||||
return nil
|
||||
case string:
|
||||
i, err := strconv.ParseInt(v, 8, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*f = FileMode(i)
|
||||
case int:
|
||||
*f = FileMode(v)
|
||||
default:
|
||||
return fmt.Errorf("unexpected value type %T for mode", value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalYAML makes FileMode implement yaml.Marshaller
|
||||
func (f *FileMode) MarshalYAML() (interface{}, error) {
|
||||
return f.String(), nil
|
||||
}
|
||||
|
||||
// MarshalJSON makes FileMode implement json.Marshaller
|
||||
func (f *FileMode) MarshalJSON() ([]byte, error) {
|
||||
return []byte("\"" + f.String() + "\""), nil
|
||||
}
|
||||
|
||||
func (f *FileMode) String() string {
|
||||
return fmt.Sprintf("0%o", int64(*f))
|
||||
}
|
||||
|
||||
// ServiceConfigObjConfig is the config obj configuration for a service
|
||||
type ServiceConfigObjConfig FileReferenceConfig
|
||||
|
||||
|
12
vendor/github.com/compose-spec/compose-go/v2/utils/collectionutils.go
generated
vendored
12
vendor/github.com/compose-spec/compose-go/v2/utils/collectionutils.go
generated
vendored
@@ -17,15 +17,13 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"golang.org/x/exp/constraints"
|
||||
"golang.org/x/exp/maps"
|
||||
"golang.org/x/exp/slices"
|
||||
"cmp"
|
||||
"maps"
|
||||
"slices"
|
||||
)
|
||||
|
||||
func MapKeys[T constraints.Ordered, U any](theMap map[T]U) []T {
|
||||
result := maps.Keys(theMap)
|
||||
slices.Sort(result)
|
||||
return result
|
||||
func MapKeys[T cmp.Ordered, U any](theMap map[T]U) []T {
|
||||
return slices.Sorted(maps.Keys(theMap))
|
||||
}
|
||||
|
||||
func MapsAppend[T comparable, U any](target map[T]U, source map[T]U) map[T]U {
|
||||
|
1
vendor/github.com/compose-spec/compose-go/v2/utils/pathutils.go
generated
vendored
1
vendor/github.com/compose-spec/compose-go/v2/utils/pathutils.go
generated
vendored
@@ -41,7 +41,6 @@ func ResolveSymbolicLink(path string) (string, error) {
|
||||
return path, nil
|
||||
}
|
||||
return strings.Replace(path, part, sym, 1), nil
|
||||
|
||||
}
|
||||
|
||||
// getSymbolinkLink parses all parts of the path and returns the
|
||||
|
5
vendor/github.com/compose-spec/compose-go/v2/validation/validation.go
generated
vendored
5
vendor/github.com/compose-spec/compose-go/v2/validation/validation.go
generated
vendored
@@ -65,7 +65,6 @@ func check(value any, p tree.Path) error {
|
||||
|
||||
func checkFileObject(keys ...string) checkerFunc {
|
||||
return func(value any, p tree.Path) error {
|
||||
|
||||
v := value.(map[string]any)
|
||||
count := 0
|
||||
for _, s := range keys {
|
||||
@@ -100,8 +99,8 @@ func checkPath(value any, p tree.Path) error {
|
||||
func checkDeviceRequest(value any, p tree.Path) error {
|
||||
v := value.(map[string]any)
|
||||
_, hasCount := v["count"]
|
||||
_, hasIds := v["device_ids"]
|
||||
if hasCount && hasIds {
|
||||
_, hasIDs := v["device_ids"]
|
||||
if hasCount && hasIDs {
|
||||
return fmt.Errorf(`%s: "count" and "device_ids" attributes are exclusive`, p)
|
||||
}
|
||||
return nil
|
||||
|
4
vendor/github.com/containerd/containerd/v2/core/images/image.go
generated
vendored
4
vendor/github.com/containerd/containerd/v2/core/images/image.go
generated
vendored
@@ -369,8 +369,8 @@ func Children(ctx context.Context, provider content.Provider, desc ocispec.Descr
|
||||
}
|
||||
|
||||
return append([]ocispec.Descriptor{}, index.Manifests...), nil
|
||||
} else if !IsLayerType(desc.MediaType) && !IsKnownConfig(desc.MediaType) {
|
||||
// Layers and configs are childless data types and should not be logged.
|
||||
} else if !IsLayerType(desc.MediaType) && !IsKnownConfig(desc.MediaType) && !IsAttestationType(desc.MediaType) {
|
||||
// Layers, configs, and attestations are childless data types and should not be logged.
|
||||
log.G(ctx).Debugf("encountered unknown type %v; children may not be fetched", desc.MediaType)
|
||||
}
|
||||
return nil, nil
|
||||
|
13
vendor/github.com/containerd/containerd/v2/core/images/mediatypes.go
generated
vendored
13
vendor/github.com/containerd/containerd/v2/core/images/mediatypes.go
generated
vendored
@@ -58,6 +58,9 @@ const (
|
||||
|
||||
MediaTypeImageLayerEncrypted = ocispec.MediaTypeImageLayer + "+encrypted"
|
||||
MediaTypeImageLayerGzipEncrypted = ocispec.MediaTypeImageLayerGzip + "+encrypted"
|
||||
|
||||
// In-toto attestation
|
||||
MediaTypeInToto = "application/vnd.in-toto+json"
|
||||
)
|
||||
|
||||
// DiffCompression returns the compression as defined by the layer diff media
|
||||
@@ -193,6 +196,16 @@ func IsKnownConfig(mt string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsAttestationType returns true if the media type is an attestation type
|
||||
func IsAttestationType(mt string) bool {
|
||||
switch mt {
|
||||
case MediaTypeInToto:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// ChildGCLabels returns the label for a given descriptor to reference it
|
||||
func ChildGCLabels(desc ocispec.Descriptor) []string {
|
||||
mt := desc.MediaType
|
||||
|
2
vendor/github.com/containerd/containerd/v2/core/remotes/handlers.go
generated
vendored
2
vendor/github.com/containerd/containerd/v2/core/remotes/handlers.go
generated
vendored
@@ -80,6 +80,8 @@ func MakeRefKey(ctx context.Context, desc ocispec.Descriptor) string {
|
||||
return "layer-" + key
|
||||
case images.IsKnownConfig(desc.MediaType):
|
||||
return "config-" + key
|
||||
case images.IsAttestationType(desc.MediaType):
|
||||
return "attestation-" + key
|
||||
default:
|
||||
log.G(ctx).Warnf("reference for unknown type: %s", desc.MediaType)
|
||||
return "unknown-" + key
|
||||
|
3
vendor/github.com/containerd/containerd/v2/version/version.go
generated
vendored
3
vendor/github.com/containerd/containerd/v2/version/version.go
generated
vendored
@@ -19,11 +19,12 @@ package version
|
||||
import "runtime"
|
||||
|
||||
var (
|
||||
Name = "containerd"
|
||||
// Package is filled at linking time
|
||||
Package = "github.com/containerd/containerd/v2"
|
||||
|
||||
// Version holds the complete version number. Filled in at linking time.
|
||||
Version = "2.0.3+unknown"
|
||||
Version = "2.0.4+unknown"
|
||||
|
||||
// Revision is filled with the VCS (e.g. git) revision being used to build
|
||||
// the program at linking time.
|
||||
|
18
vendor/github.com/docker/cli/cli-plugins/hooks/printer.go
generated
vendored
18
vendor/github.com/docker/cli/cli-plugins/hooks/printer.go
generated
vendored
@@ -1,18 +0,0 @@
|
||||
package hooks
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/morikuni/aec"
|
||||
)
|
||||
|
||||
func PrintNextSteps(out io.Writer, messages []string) {
|
||||
if len(messages) == 0 {
|
||||
return
|
||||
}
|
||||
_, _ = fmt.Fprintln(out, aec.Bold.Apply("\nWhat's next:"))
|
||||
for _, n := range messages {
|
||||
_, _ = fmt.Fprintln(out, " ", n)
|
||||
}
|
||||
}
|
116
vendor/github.com/docker/cli/cli-plugins/hooks/template.go
generated
vendored
116
vendor/github.com/docker/cli/cli-plugins/hooks/template.go
generated
vendored
@@ -1,116 +0,0 @@
|
||||
package hooks
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type HookType int
|
||||
|
||||
const (
|
||||
NextSteps = iota
|
||||
)
|
||||
|
||||
// HookMessage represents a plugin hook response. Plugins
|
||||
// declaring support for CLI hooks need to print a json
|
||||
// representation of this type when their hook subcommand
|
||||
// is invoked.
|
||||
type HookMessage struct {
|
||||
Type HookType
|
||||
Template string
|
||||
}
|
||||
|
||||
// TemplateReplaceSubcommandName returns a hook template string
|
||||
// that will be replaced by the CLI subcommand being executed
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// "you ran the subcommand: " + TemplateReplaceSubcommandName()
|
||||
//
|
||||
// when being executed after the command:
|
||||
// `docker run --name "my-container" alpine`
|
||||
// will result in the message:
|
||||
// `you ran the subcommand: run`
|
||||
func TemplateReplaceSubcommandName() string {
|
||||
return hookTemplateCommandName
|
||||
}
|
||||
|
||||
// TemplateReplaceFlagValue returns a hook template string
|
||||
// that will be replaced by the flags value.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// "you ran a container named: " + TemplateReplaceFlagValue("name")
|
||||
//
|
||||
// when being executed after the command:
|
||||
// `docker run --name "my-container" alpine`
|
||||
// will result in the message:
|
||||
// `you ran a container named: my-container`
|
||||
func TemplateReplaceFlagValue(flag string) string {
|
||||
return fmt.Sprintf(hookTemplateFlagValue, flag)
|
||||
}
|
||||
|
||||
// TemplateReplaceArg takes an index i and returns a hook
|
||||
// template string that the CLI will replace the template with
|
||||
// the ith argument, after processing the passed flags.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// "run this image with `docker run " + TemplateReplaceArg(0) + "`"
|
||||
//
|
||||
// when being executed after the command:
|
||||
// `docker pull alpine`
|
||||
// will result in the message:
|
||||
// "Run this image with `docker run alpine`"
|
||||
func TemplateReplaceArg(i int) string {
|
||||
return fmt.Sprintf(hookTemplateArg, strconv.Itoa(i))
|
||||
}
|
||||
|
||||
func ParseTemplate(hookTemplate string, cmd *cobra.Command) ([]string, error) {
|
||||
tmpl := template.New("").Funcs(commandFunctions)
|
||||
tmpl, err := tmpl.Parse(hookTemplate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b := bytes.Buffer{}
|
||||
err = tmpl.Execute(&b, cmd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return strings.Split(b.String(), "\n"), nil
|
||||
}
|
||||
|
||||
var ErrHookTemplateParse = errors.New("failed to parse hook template")
|
||||
|
||||
const (
|
||||
hookTemplateCommandName = "{{.Name}}"
|
||||
hookTemplateFlagValue = `{{flag . "%s"}}`
|
||||
hookTemplateArg = "{{arg . %s}}"
|
||||
)
|
||||
|
||||
var commandFunctions = template.FuncMap{
|
||||
"flag": getFlagValue,
|
||||
"arg": getArgValue,
|
||||
}
|
||||
|
||||
func getFlagValue(cmd *cobra.Command, flag string) (string, error) {
|
||||
cmdFlag := cmd.Flag(flag)
|
||||
if cmdFlag == nil {
|
||||
return "", ErrHookTemplateParse
|
||||
}
|
||||
return cmdFlag.Value.String(), nil
|
||||
}
|
||||
|
||||
func getArgValue(cmd *cobra.Command, i int) (string, error) {
|
||||
flags := cmd.Flags()
|
||||
if flags == nil {
|
||||
return "", ErrHookTemplateParse
|
||||
}
|
||||
return flags.Arg(i), nil
|
||||
}
|
21
vendor/github.com/docker/cli/cli-plugins/manager/candidate.go
generated
vendored
21
vendor/github.com/docker/cli/cli-plugins/manager/candidate.go
generated
vendored
@@ -1,21 +0,0 @@
|
||||
package manager
|
||||
|
||||
import "os/exec"
|
||||
|
||||
// Candidate represents a possible plugin candidate, for mocking purposes
|
||||
type Candidate interface {
|
||||
Path() string
|
||||
Metadata() ([]byte, error)
|
||||
}
|
||||
|
||||
type candidate struct {
|
||||
path string
|
||||
}
|
||||
|
||||
func (c *candidate) Path() string {
|
||||
return c.path
|
||||
}
|
||||
|
||||
func (c *candidate) Metadata() ([]byte, error) {
|
||||
return exec.Command(c.path, MetadataSubcommandName).Output() // #nosec G204 -- ignore "Subprocess launched with a potential tainted input or cmd arguments"
|
||||
}
|
147
vendor/github.com/docker/cli/cli-plugins/manager/cobra.go
generated
vendored
147
vendor/github.com/docker/cli/cli-plugins/manager/cobra.go
generated
vendored
@@ -1,147 +0,0 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/spf13/cobra"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
)
|
||||
|
||||
const (
|
||||
// CommandAnnotationPlugin is added to every stub command added by
|
||||
// AddPluginCommandStubs with the value "true" and so can be
|
||||
// used to distinguish plugin stubs from regular commands.
|
||||
CommandAnnotationPlugin = "com.docker.cli.plugin"
|
||||
|
||||
// CommandAnnotationPluginVendor is added to every stub command
|
||||
// added by AddPluginCommandStubs and contains the vendor of
|
||||
// that plugin.
|
||||
CommandAnnotationPluginVendor = "com.docker.cli.plugin.vendor"
|
||||
|
||||
// CommandAnnotationPluginVersion is added to every stub command
|
||||
// added by AddPluginCommandStubs and contains the version of
|
||||
// that plugin.
|
||||
CommandAnnotationPluginVersion = "com.docker.cli.plugin.version"
|
||||
|
||||
// CommandAnnotationPluginInvalid is added to any stub command
|
||||
// added by AddPluginCommandStubs for an invalid command (that
|
||||
// is, one which failed it's candidate test) and contains the
|
||||
// reason for the failure.
|
||||
CommandAnnotationPluginInvalid = "com.docker.cli.plugin-invalid"
|
||||
|
||||
// CommandAnnotationPluginCommandPath is added to overwrite the
|
||||
// command path for a plugin invocation.
|
||||
CommandAnnotationPluginCommandPath = "com.docker.cli.plugin.command_path"
|
||||
)
|
||||
|
||||
var pluginCommandStubsOnce sync.Once
|
||||
|
||||
// AddPluginCommandStubs adds a stub cobra.Commands for each valid and invalid
|
||||
// plugin. The command stubs will have several annotations added, see
|
||||
// `CommandAnnotationPlugin*`.
|
||||
func AddPluginCommandStubs(dockerCli command.Cli, rootCmd *cobra.Command) (err error) {
|
||||
pluginCommandStubsOnce.Do(func() {
|
||||
var plugins []Plugin
|
||||
plugins, err = ListPlugins(dockerCli, rootCmd)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, p := range plugins {
|
||||
vendor := p.Vendor
|
||||
if vendor == "" {
|
||||
vendor = "unknown"
|
||||
}
|
||||
annotations := map[string]string{
|
||||
CommandAnnotationPlugin: "true",
|
||||
CommandAnnotationPluginVendor: vendor,
|
||||
CommandAnnotationPluginVersion: p.Version,
|
||||
}
|
||||
if p.Err != nil {
|
||||
annotations[CommandAnnotationPluginInvalid] = p.Err.Error()
|
||||
}
|
||||
rootCmd.AddCommand(&cobra.Command{
|
||||
Use: p.Name,
|
||||
Short: p.ShortDescription,
|
||||
Run: func(_ *cobra.Command, _ []string) {},
|
||||
Annotations: annotations,
|
||||
DisableFlagParsing: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
flags := rootCmd.PersistentFlags()
|
||||
flags.SetOutput(nil)
|
||||
perr := flags.Parse(args)
|
||||
if perr != nil {
|
||||
return err
|
||||
}
|
||||
if flags.Changed("help") {
|
||||
cmd.HelpFunc()(rootCmd, args)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("docker: unknown command: docker %s\n\nRun 'docker --help' for more information", cmd.Name())
|
||||
},
|
||||
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 = append(cargs, args...)
|
||||
cargs = append(cargs, toComplete)
|
||||
os.Args = cargs
|
||||
runCommand, runErr := PluginRunCommand(dockerCli, p.Name, cmd)
|
||||
if runErr != nil {
|
||||
return nil, cobra.ShellCompDirectiveError
|
||||
}
|
||||
runErr = runCommand.Run()
|
||||
if runErr == nil {
|
||||
os.Exit(0) // plugin already rendered complete data
|
||||
}
|
||||
return nil, cobra.ShellCompDirectiveError
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
const (
|
||||
dockerCliAttributePrefix = attribute.Key("docker.cli")
|
||||
|
||||
cobraCommandPath = attribute.Key("cobra.command_path")
|
||||
)
|
||||
|
||||
func getPluginResourceAttributes(cmd *cobra.Command, plugin Plugin) attribute.Set {
|
||||
commandPath := cmd.Annotations[CommandAnnotationPluginCommandPath]
|
||||
if commandPath == "" {
|
||||
commandPath = fmt.Sprintf("%s %s", cmd.CommandPath(), plugin.Name)
|
||||
}
|
||||
|
||||
attrSet := attribute.NewSet(
|
||||
cobraCommandPath.String(commandPath),
|
||||
)
|
||||
|
||||
kvs := make([]attribute.KeyValue, 0, attrSet.Len())
|
||||
for iter := attrSet.Iter(); iter.Next(); {
|
||||
attr := iter.Attribute()
|
||||
kvs = append(kvs, attribute.KeyValue{
|
||||
Key: dockerCliAttributePrefix + "." + attr.Key,
|
||||
Value: attr.Value,
|
||||
})
|
||||
}
|
||||
return attribute.NewSet(kvs...)
|
||||
}
|
||||
|
||||
func appendPluginResourceAttributesEnvvar(env []string, cmd *cobra.Command, plugin Plugin) []string {
|
||||
if attrs := getPluginResourceAttributes(cmd, plugin); attrs.Len() > 0 {
|
||||
// values in environment variables need to be in baggage format
|
||||
// otel/baggage package can be used after update to v1.22, currently it encodes incorrectly
|
||||
attrsSlice := make([]string, attrs.Len())
|
||||
for iter := attrs.Iter(); iter.Next(); {
|
||||
i, v := iter.IndexedAttribute()
|
||||
attrsSlice[i] = string(v.Key) + "=" + url.PathEscape(v.Value.AsString())
|
||||
}
|
||||
env = append(env, ResourceAttributesEnvvar+"="+strings.Join(attrsSlice, ","))
|
||||
}
|
||||
return env
|
||||
}
|
54
vendor/github.com/docker/cli/cli-plugins/manager/error.go
generated
vendored
54
vendor/github.com/docker/cli/cli-plugins/manager/error.go
generated
vendored
@@ -1,54 +0,0 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.22
|
||||
|
||||
package manager
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// pluginError is set as Plugin.Err by NewPlugin if the plugin
|
||||
// candidate fails one of the candidate tests. This exists primarily
|
||||
// to implement encoding.TextMarshaller such that rendering a plugin as JSON
|
||||
// (e.g. for `docker info -f '{{json .CLIPlugins}}'`) renders the Err
|
||||
// field as a useful string and not just `{}`. See
|
||||
// https://github.com/golang/go/issues/10748 for some discussion
|
||||
// around why the builtin error type doesn't implement this.
|
||||
type pluginError struct {
|
||||
cause error
|
||||
}
|
||||
|
||||
// Error satisfies the core error interface for pluginError.
|
||||
func (e *pluginError) Error() string {
|
||||
return e.cause.Error()
|
||||
}
|
||||
|
||||
// Cause satisfies the errors.causer interface for pluginError.
|
||||
func (e *pluginError) Cause() error {
|
||||
return e.cause
|
||||
}
|
||||
|
||||
// Unwrap provides compatibility for Go 1.13 error chains.
|
||||
func (e *pluginError) Unwrap() error {
|
||||
return e.cause
|
||||
}
|
||||
|
||||
// MarshalText marshalls the pluginError into a textual form.
|
||||
func (e *pluginError) MarshalText() (text []byte, err error) {
|
||||
return []byte(e.cause.Error()), nil
|
||||
}
|
||||
|
||||
// wrapAsPluginError wraps an error in a pluginError with an
|
||||
// additional message, analogous to errors.Wrapf.
|
||||
func wrapAsPluginError(err error, msg string) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &pluginError{cause: errors.Wrap(err, msg)}
|
||||
}
|
||||
|
||||
// NewPluginError creates a new pluginError, analogous to
|
||||
// errors.Errorf.
|
||||
func NewPluginError(msg string, args ...any) error {
|
||||
return &pluginError{cause: errors.Errorf(msg, args...)}
|
||||
}
|
199
vendor/github.com/docker/cli/cli-plugins/manager/hooks.go
generated
vendored
199
vendor/github.com/docker/cli/cli-plugins/manager/hooks.go
generated
vendored
@@ -1,199 +0,0 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli-plugins/hooks"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
// HookPluginData is the type representing the information
|
||||
// that plugins declaring support for hooks get passed when
|
||||
// being invoked following a CLI command execution.
|
||||
type HookPluginData struct {
|
||||
// RootCmd is a string representing the matching hook configuration
|
||||
// which is currently being invoked. If a hook for `docker context` is
|
||||
// configured and the user executes `docker context ls`, the plugin will
|
||||
// be invoked with `context`.
|
||||
RootCmd string
|
||||
Flags map[string]string
|
||||
CommandError string
|
||||
}
|
||||
|
||||
// RunCLICommandHooks is the entrypoint into the hooks execution flow after
|
||||
// a main CLI command was executed. It calls the hook subcommand for all
|
||||
// present CLI plugins that declare support for hooks in their metadata and
|
||||
// parses/prints their responses.
|
||||
func RunCLICommandHooks(ctx context.Context, dockerCli command.Cli, rootCmd, subCommand *cobra.Command, cmdErrorMessage string) {
|
||||
commandName := strings.TrimPrefix(subCommand.CommandPath(), rootCmd.Name()+" ")
|
||||
flags := getCommandFlags(subCommand)
|
||||
|
||||
runHooks(ctx, dockerCli, rootCmd, subCommand, commandName, flags, cmdErrorMessage)
|
||||
}
|
||||
|
||||
// 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 command.Cli, rootCmd, subCommand *cobra.Command, args []string) {
|
||||
commandName := strings.Join(args, " ")
|
||||
flags := getNaiveFlags(args)
|
||||
|
||||
runHooks(ctx, dockerCli, rootCmd, subCommand, commandName, flags, "")
|
||||
}
|
||||
|
||||
func runHooks(ctx context.Context, dockerCli command.Cli, rootCmd, subCommand *cobra.Command, invokedCommand string, flags map[string]string, cmdErrorMessage string) {
|
||||
nextSteps := invokeAndCollectHooks(ctx, dockerCli, rootCmd, subCommand, invokedCommand, flags, cmdErrorMessage)
|
||||
|
||||
hooks.PrintNextSteps(dockerCli.Err(), nextSteps)
|
||||
}
|
||||
|
||||
func invokeAndCollectHooks(ctx context.Context, dockerCli command.Cli, rootCmd, subCmd *cobra.Command, subCmdStr string, flags map[string]string, cmdErrorMessage string) []string {
|
||||
// check if the context was cancelled before invoking hooks
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
|
||||
pluginsCfg := dockerCli.ConfigFile().Plugins
|
||||
if pluginsCfg == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
nextSteps := make([]string, 0, len(pluginsCfg))
|
||||
for pluginName, cfg := range pluginsCfg {
|
||||
match, ok := pluginMatch(cfg, subCmdStr)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
p, err := GetPlugin(pluginName, dockerCli, rootCmd)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
hookReturn, err := p.RunHook(ctx, HookPluginData{
|
||||
RootCmd: match,
|
||||
Flags: flags,
|
||||
CommandError: cmdErrorMessage,
|
||||
})
|
||||
if err != nil {
|
||||
// skip misbehaving plugins, but don't halt execution
|
||||
continue
|
||||
}
|
||||
|
||||
var hookMessageData hooks.HookMessage
|
||||
err = json.Unmarshal(hookReturn, &hookMessageData)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// currently the only hook type
|
||||
if hookMessageData.Type != hooks.NextSteps {
|
||||
continue
|
||||
}
|
||||
|
||||
processedHook, err := hooks.ParseTemplate(hookMessageData.Template, subCmd)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var appended bool
|
||||
nextSteps, appended = appendNextSteps(nextSteps, processedHook)
|
||||
if !appended {
|
||||
logrus.Debugf("Plugin %s responded with an empty hook message %q. Ignoring.", pluginName, string(hookReturn))
|
||||
}
|
||||
}
|
||||
return nextSteps
|
||||
}
|
||||
|
||||
// appendNextSteps appends the processed hook output to the nextSteps slice.
|
||||
// If the processed hook output is empty, it is not appended.
|
||||
// Empty lines are not stripped if there's at least one non-empty line.
|
||||
func appendNextSteps(nextSteps []string, processed []string) ([]string, bool) {
|
||||
empty := true
|
||||
for _, l := range processed {
|
||||
if strings.TrimSpace(l) != "" {
|
||||
empty = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if empty {
|
||||
return nextSteps, false
|
||||
}
|
||||
|
||||
return append(nextSteps, processed...), true
|
||||
}
|
||||
|
||||
// pluginMatch takes a plugin configuration and a string representing the
|
||||
// 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 == "" {
|
||||
return "", false
|
||||
}
|
||||
|
||||
commands := strings.Split(configuredPluginHooks, ",")
|
||||
for _, hookCmd := range commands {
|
||||
if hookMatch(hookCmd, subCmd) {
|
||||
return hookCmd, true
|
||||
}
|
||||
}
|
||||
|
||||
return "", false
|
||||
}
|
||||
|
||||
func hookMatch(hookCmd, subCmd string) bool {
|
||||
hookCmdTokens := strings.Split(hookCmd, " ")
|
||||
subCmdTokens := strings.Split(subCmd, " ")
|
||||
|
||||
if len(hookCmdTokens) > len(subCmdTokens) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i, v := range hookCmdTokens {
|
||||
if v != subCmdTokens[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func getCommandFlags(cmd *cobra.Command) map[string]string {
|
||||
flags := make(map[string]string)
|
||||
cmd.Flags().Visit(func(f *pflag.Flag) {
|
||||
var fValue string
|
||||
if f.Value.Type() == "bool" {
|
||||
fValue = f.Value.String()
|
||||
}
|
||||
flags[f.Name] = fValue
|
||||
})
|
||||
return flags
|
||||
}
|
||||
|
||||
// getNaiveFlags string-matches argv and parses them into a map.
|
||||
// This is used when calling hooks after a plugin command, since
|
||||
// in this case we can't rely on the cobra command tree to parse
|
||||
// flags in this case. In this case, no values are ever passed,
|
||||
// since we don't have enough information to process them.
|
||||
func getNaiveFlags(args []string) map[string]string {
|
||||
flags := make(map[string]string)
|
||||
for _, arg := range args {
|
||||
if strings.HasPrefix(arg, "--") {
|
||||
flags[arg[2:]] = ""
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(arg, "-") {
|
||||
flags[arg[1:]] = ""
|
||||
}
|
||||
}
|
||||
return flags
|
||||
}
|
247
vendor/github.com/docker/cli/cli-plugins/manager/manager.go
generated
vendored
247
vendor/github.com/docker/cli/cli-plugins/manager/manager.go
generated
vendored
@@ -1,247 +0,0 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/config"
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
"github.com/fvbommel/sortorder"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
const (
|
||||
// ReexecEnvvar is the name of an ennvar which is set to the command
|
||||
// used to originally invoke the docker CLI when executing a
|
||||
// plugin. Assuming $PATH and $CWD remain unchanged this should allow
|
||||
// the plugin to re-execute the original CLI.
|
||||
ReexecEnvvar = "DOCKER_CLI_PLUGIN_ORIGINAL_CLI_COMMAND"
|
||||
|
||||
// ResourceAttributesEnvvar is the name of the envvar that includes additional
|
||||
// resource attributes for OTEL.
|
||||
ResourceAttributesEnvvar = "OTEL_RESOURCE_ATTRIBUTES"
|
||||
)
|
||||
|
||||
// errPluginNotFound is the error returned when a plugin could not be found.
|
||||
type errPluginNotFound string
|
||||
|
||||
func (errPluginNotFound) NotFound() {}
|
||||
|
||||
func (e errPluginNotFound) Error() string {
|
||||
return "Error: No such CLI plugin: " + string(e)
|
||||
}
|
||||
|
||||
type notFound interface{ NotFound() }
|
||||
|
||||
// IsNotFound is true if the given error is due to a plugin not being found.
|
||||
func IsNotFound(err error) bool {
|
||||
if e, ok := err.(*pluginError); ok {
|
||||
err = e.Cause()
|
||||
}
|
||||
_, ok := err.(notFound)
|
||||
return ok
|
||||
}
|
||||
|
||||
// getPluginDirs returns the platform-specific locations to search for plugins
|
||||
// in order of preference.
|
||||
//
|
||||
// Plugin-discovery is performed in the following order of preference:
|
||||
//
|
||||
// 1. The "cli-plugins" directory inside the CLIs [config.Path] (usually "~/.docker/cli-plugins").
|
||||
// 2. Additional plugin directories as configured through [ConfigFile.CLIPluginsExtraDirs].
|
||||
// 3. Platform-specific defaultSystemPluginDirs.
|
||||
//
|
||||
// [ConfigFile.CLIPluginsExtraDirs]: https://pkg.go.dev/github.com/docker/cli@v26.1.4+incompatible/cli/config/configfile#ConfigFile.CLIPluginsExtraDirs
|
||||
func getPluginDirs(cfg *configfile.ConfigFile) ([]string, error) {
|
||||
var pluginDirs []string
|
||||
|
||||
if cfg != nil {
|
||||
pluginDirs = append(pluginDirs, cfg.CLIPluginsExtraDirs...)
|
||||
}
|
||||
pluginDir, err := config.Path("cli-plugins")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pluginDirs = append(pluginDirs, pluginDir)
|
||||
pluginDirs = append(pluginDirs, defaultSystemPluginDirs...)
|
||||
return pluginDirs, nil
|
||||
}
|
||||
|
||||
func addPluginCandidatesFromDir(res map[string][]string, d string) {
|
||||
dentries, err := os.ReadDir(d)
|
||||
// Silently ignore any directories which we cannot list (e.g. due to
|
||||
// permissions or anything else) or which is not a directory
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, dentry := range dentries {
|
||||
switch dentry.Type() & os.ModeType {
|
||||
case 0, os.ModeSymlink:
|
||||
// Regular file or symlink, keep going
|
||||
default:
|
||||
// Something else, ignore.
|
||||
continue
|
||||
}
|
||||
name := dentry.Name()
|
||||
if !strings.HasPrefix(name, NamePrefix) {
|
||||
continue
|
||||
}
|
||||
name = strings.TrimPrefix(name, NamePrefix)
|
||||
var err error
|
||||
if name, err = trimExeSuffix(name); err != nil {
|
||||
continue
|
||||
}
|
||||
res[name] = append(res[name], filepath.Join(d, dentry.Name()))
|
||||
}
|
||||
}
|
||||
|
||||
// listPluginCandidates returns a map from plugin name to the list of (unvalidated) Candidates. The list is in descending order of priority.
|
||||
func listPluginCandidates(dirs []string) map[string][]string {
|
||||
result := make(map[string][]string)
|
||||
for _, d := range dirs {
|
||||
addPluginCandidatesFromDir(result, d)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// GetPlugin returns a plugin on the system by its name
|
||||
func GetPlugin(name string, dockerCli command.Cli, rootcmd *cobra.Command) (*Plugin, error) {
|
||||
pluginDirs, err := getPluginDirs(dockerCli.ConfigFile())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
candidates := listPluginCandidates(pluginDirs)
|
||||
if paths, ok := candidates[name]; ok {
|
||||
if len(paths) == 0 {
|
||||
return nil, errPluginNotFound(name)
|
||||
}
|
||||
c := &candidate{paths[0]}
|
||||
p, err := newPlugin(c, rootcmd.Commands())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !IsNotFound(p.Err) {
|
||||
p.ShadowedPaths = paths[1:]
|
||||
}
|
||||
return &p, nil
|
||||
}
|
||||
|
||||
return nil, errPluginNotFound(name)
|
||||
}
|
||||
|
||||
// ListPlugins produces a list of the plugins available on the system
|
||||
func ListPlugins(dockerCli command.Cli, rootcmd *cobra.Command) ([]Plugin, error) {
|
||||
pluginDirs, err := getPluginDirs(dockerCli.ConfigFile())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
candidates := listPluginCandidates(pluginDirs)
|
||||
|
||||
var plugins []Plugin
|
||||
var mu sync.Mutex
|
||||
eg, _ := errgroup.WithContext(context.TODO())
|
||||
cmds := rootcmd.Commands()
|
||||
for _, paths := range candidates {
|
||||
func(paths []string) {
|
||||
eg.Go(func() error {
|
||||
if len(paths) == 0 {
|
||||
return nil
|
||||
}
|
||||
c := &candidate{paths[0]}
|
||||
p, err := newPlugin(c, cmds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !IsNotFound(p.Err) {
|
||||
p.ShadowedPaths = paths[1:]
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
plugins = append(plugins, p)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}(paths)
|
||||
}
|
||||
if err := eg.Wait(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sort.Slice(plugins, func(i, j int) bool {
|
||||
return sortorder.NaturalLess(plugins[i].Name, plugins[j].Name)
|
||||
})
|
||||
|
||||
return plugins, nil
|
||||
}
|
||||
|
||||
// PluginRunCommand returns an "os/exec".Cmd which when .Run() will execute the named plugin.
|
||||
// The rootcmd argument is referenced to determine the set of builtin commands in order to detect conficts.
|
||||
// The error returned satisfies the IsNotFound() predicate if no plugin was found or if the first candidate plugin was invalid somehow.
|
||||
func PluginRunCommand(dockerCli command.Cli, name string, rootcmd *cobra.Command) (*exec.Cmd, error) {
|
||||
// This uses the full original args, not the args which may
|
||||
// have been provided by cobra to our caller. This is because
|
||||
// they lack e.g. global options which we must propagate here.
|
||||
args := os.Args[1:]
|
||||
if !pluginNameRe.MatchString(name) {
|
||||
// We treat this as "not found" so that callers will
|
||||
// fallback to their "invalid" command path.
|
||||
return nil, errPluginNotFound(name)
|
||||
}
|
||||
exename := addExeSuffix(NamePrefix + name)
|
||||
pluginDirs, err := getPluginDirs(dockerCli.ConfigFile())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, d := range pluginDirs {
|
||||
path := filepath.Join(d, exename)
|
||||
|
||||
// We stat here rather than letting the exec tell us
|
||||
// ENOENT because the latter does not distinguish a
|
||||
// file not existing from its dynamic loader or one of
|
||||
// its libraries not existing.
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
|
||||
c := &candidate{path: path}
|
||||
plugin, err := newPlugin(c, rootcmd.Commands())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if plugin.Err != nil {
|
||||
// TODO: why are we not returning plugin.Err?
|
||||
return nil, errPluginNotFound(name)
|
||||
}
|
||||
cmd := exec.Command(plugin.Path, args...) // #nosec G204 -- ignore "Subprocess launched with a potential tainted input or cmd arguments"
|
||||
|
||||
// Using dockerCli.{In,Out,Err}() here results in a hang until something is input.
|
||||
// See: - https://github.com/golang/go/issues/10338
|
||||
// - https://github.com/golang/go/commit/d000e8742a173aa0659584aa01b7ba2834ba28ab
|
||||
// os.Stdin is a *os.File which avoids this behaviour. We don't need the functionality
|
||||
// of the wrappers here anyway.
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
cmd.Env = append(cmd.Environ(), ReexecEnvvar+"="+os.Args[0])
|
||||
cmd.Env = appendPluginResourceAttributesEnvvar(cmd.Env, rootcmd, plugin)
|
||||
|
||||
return cmd, nil
|
||||
}
|
||||
return nil, errPluginNotFound(name)
|
||||
}
|
||||
|
||||
// IsPluginCommand checks if the given cmd is a plugin-stub.
|
||||
func IsPluginCommand(cmd *cobra.Command) bool {
|
||||
return cmd.Annotations[CommandAnnotationPlugin] == "true"
|
||||
}
|
20
vendor/github.com/docker/cli/cli-plugins/manager/manager_unix.go
generated
vendored
20
vendor/github.com/docker/cli/cli-plugins/manager/manager_unix.go
generated
vendored
@@ -1,20 +0,0 @@
|
||||
//go:build !windows
|
||||
|
||||
package manager
|
||||
|
||||
// defaultSystemPluginDirs are the platform-specific locations to search
|
||||
// for plugins in order of preference.
|
||||
//
|
||||
// Plugin-discovery is performed in the following order of preference:
|
||||
//
|
||||
// 1. The "cli-plugins" directory inside the CLIs config-directory (usually "~/.docker/cli-plugins").
|
||||
// 2. Additional plugin directories as configured through [ConfigFile.CLIPluginsExtraDirs].
|
||||
// 3. Platform-specific defaultSystemPluginDirs (as defined below).
|
||||
//
|
||||
// [ConfigFile.CLIPluginsExtraDirs]: https://pkg.go.dev/github.com/docker/cli@v26.1.4+incompatible/cli/config/configfile#ConfigFile.CLIPluginsExtraDirs
|
||||
var defaultSystemPluginDirs = []string{
|
||||
"/usr/local/lib/docker/cli-plugins",
|
||||
"/usr/local/libexec/docker/cli-plugins",
|
||||
"/usr/lib/docker/cli-plugins",
|
||||
"/usr/libexec/docker/cli-plugins",
|
||||
}
|
21
vendor/github.com/docker/cli/cli-plugins/manager/manager_windows.go
generated
vendored
21
vendor/github.com/docker/cli/cli-plugins/manager/manager_windows.go
generated
vendored
@@ -1,21 +0,0 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// defaultSystemPluginDirs are the platform-specific locations to search
|
||||
// for plugins in order of preference.
|
||||
//
|
||||
// Plugin-discovery is performed in the following order of preference:
|
||||
//
|
||||
// 1. The "cli-plugins" directory inside the CLIs config-directory (usually "~/.docker/cli-plugins").
|
||||
// 2. Additional plugin directories as configured through [ConfigFile.CLIPluginsExtraDirs].
|
||||
// 3. Platform-specific defaultSystemPluginDirs (as defined below).
|
||||
//
|
||||
// [ConfigFile.CLIPluginsExtraDirs]: https://pkg.go.dev/github.com/docker/cli@v26.1.4+incompatible/cli/config/configfile#ConfigFile.CLIPluginsExtraDirs
|
||||
var defaultSystemPluginDirs = []string{
|
||||
filepath.Join(os.Getenv("ProgramData"), "Docker", "cli-plugins"),
|
||||
filepath.Join(os.Getenv("ProgramFiles"), "Docker", "cli-plugins"),
|
||||
}
|
124
vendor/github.com/docker/cli/cli-plugins/manager/plugin.go
generated
vendored
124
vendor/github.com/docker/cli/cli-plugins/manager/plugin.go
generated
vendored
@@ -1,124 +0,0 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var pluginNameRe = regexp.MustCompile("^[a-z][a-z0-9]*$")
|
||||
|
||||
// Plugin represents a potential plugin with all it's metadata.
|
||||
type Plugin struct {
|
||||
Metadata
|
||||
|
||||
Name string `json:",omitempty"`
|
||||
Path string `json:",omitempty"`
|
||||
|
||||
// Err is non-nil if the plugin failed one of the candidate tests.
|
||||
Err error `json:",omitempty"`
|
||||
|
||||
// ShadowedPaths contains the paths of any other plugins which this plugin takes precedence over.
|
||||
ShadowedPaths []string `json:",omitempty"`
|
||||
}
|
||||
|
||||
// newPlugin determines if the given candidate is valid and returns a
|
||||
// Plugin. If the candidate fails one of the tests then `Plugin.Err`
|
||||
// is set, and is always a `pluginError`, but the `Plugin` is still
|
||||
// returned with no error. An error is only returned due to a
|
||||
// non-recoverable error.
|
||||
func newPlugin(c Candidate, cmds []*cobra.Command) (Plugin, error) {
|
||||
path := c.Path()
|
||||
if path == "" {
|
||||
return Plugin{}, errors.New("plugin candidate path cannot be empty")
|
||||
}
|
||||
|
||||
// The candidate listing process should have skipped anything
|
||||
// which would fail here, so there are all real errors.
|
||||
fullname := filepath.Base(path)
|
||||
if fullname == "." {
|
||||
return Plugin{}, errors.Errorf("unable to determine basename of plugin candidate %q", path)
|
||||
}
|
||||
var err error
|
||||
if fullname, err = trimExeSuffix(fullname); err != nil {
|
||||
return Plugin{}, errors.Wrapf(err, "plugin candidate %q", path)
|
||||
}
|
||||
if !strings.HasPrefix(fullname, NamePrefix) {
|
||||
return Plugin{}, errors.Errorf("plugin candidate %q: does not have %q prefix", path, NamePrefix)
|
||||
}
|
||||
|
||||
p := Plugin{
|
||||
Name: strings.TrimPrefix(fullname, NamePrefix),
|
||||
Path: path,
|
||||
}
|
||||
|
||||
// Now apply the candidate tests, so these update p.Err.
|
||||
if !pluginNameRe.MatchString(p.Name) {
|
||||
p.Err = NewPluginError("plugin candidate %q did not match %q", p.Name, pluginNameRe.String())
|
||||
return p, nil
|
||||
}
|
||||
|
||||
for _, cmd := range cmds {
|
||||
// Ignore conflicts with commands which are
|
||||
// just plugin stubs (i.e. from a previous
|
||||
// call to AddPluginCommandStubs).
|
||||
if IsPluginCommand(cmd) {
|
||||
continue
|
||||
}
|
||||
if cmd.Name() == p.Name {
|
||||
p.Err = NewPluginError("plugin %q duplicates builtin command", p.Name)
|
||||
return p, nil
|
||||
}
|
||||
if cmd.HasAlias(p.Name) {
|
||||
p.Err = NewPluginError("plugin %q duplicates an alias of builtin command %q", p.Name, cmd.Name())
|
||||
return p, nil
|
||||
}
|
||||
}
|
||||
|
||||
// We are supposed to check for relevant execute permissions here. Instead we rely on an attempt to execute.
|
||||
meta, err := c.Metadata()
|
||||
if err != nil {
|
||||
p.Err = wrapAsPluginError(err, "failed to fetch metadata")
|
||||
return p, nil
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(meta, &p.Metadata); err != nil {
|
||||
p.Err = wrapAsPluginError(err, "invalid metadata")
|
||||
return p, nil
|
||||
}
|
||||
if p.Metadata.SchemaVersion != "0.1.0" {
|
||||
p.Err = NewPluginError("plugin SchemaVersion %q is not valid, must be 0.1.0", p.Metadata.SchemaVersion)
|
||||
return p, nil
|
||||
}
|
||||
if p.Metadata.Vendor == "" {
|
||||
p.Err = NewPluginError("plugin metadata does not define a vendor")
|
||||
return p, nil
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// RunHook executes the plugin's hooks command
|
||||
// and returns its unprocessed output.
|
||||
func (p *Plugin) RunHook(ctx context.Context, hookData HookPluginData) ([]byte, error) {
|
||||
hDataBytes, err := json.Marshal(hookData)
|
||||
if err != nil {
|
||||
return nil, wrapAsPluginError(err, "failed to marshall hook data")
|
||||
}
|
||||
|
||||
pCmd := exec.CommandContext(ctx, p.Path, p.Name, HookSubcommandName, string(hDataBytes)) // #nosec G204 -- ignore "Subprocess launched with a potential tainted input or cmd arguments"
|
||||
pCmd.Env = os.Environ()
|
||||
pCmd.Env = append(pCmd.Env, ReexecEnvvar+"="+os.Args[0])
|
||||
hookCmdOutput, err := pCmd.Output()
|
||||
if err != nil {
|
||||
return nil, wrapAsPluginError(err, "failed to execute plugin hook subcommand")
|
||||
}
|
||||
|
||||
return hookCmdOutput, nil
|
||||
}
|
11
vendor/github.com/docker/cli/cli-plugins/manager/suffix_unix.go
generated
vendored
11
vendor/github.com/docker/cli/cli-plugins/manager/suffix_unix.go
generated
vendored
@@ -1,11 +0,0 @@
|
||||
//go:build !windows
|
||||
|
||||
package manager
|
||||
|
||||
func trimExeSuffix(s string) (string, error) {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func addExeSuffix(s string) string {
|
||||
return s
|
||||
}
|
26
vendor/github.com/docker/cli/cli-plugins/manager/suffix_windows.go
generated
vendored
26
vendor/github.com/docker/cli/cli-plugins/manager/suffix_windows.go
generated
vendored
@@ -1,26 +0,0 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// This is made slightly more complex due to needing to be case insensitive.
|
||||
func trimExeSuffix(s string) (string, error) {
|
||||
ext := filepath.Ext(s)
|
||||
if ext == "" {
|
||||
return "", errors.Errorf("path %q lacks required file extension", s)
|
||||
}
|
||||
|
||||
exe := ".exe"
|
||||
if !strings.EqualFold(ext, exe) {
|
||||
return "", errors.Errorf("path %q lacks required %q suffix", s, exe)
|
||||
}
|
||||
return strings.TrimSuffix(s, ext), nil
|
||||
}
|
||||
|
||||
func addExeSuffix(s string) string {
|
||||
return s + ".exe"
|
||||
}
|
28
vendor/github.com/docker/cli/cli-plugins/metadata/annotations.go
generated
vendored
Normal file
28
vendor/github.com/docker/cli/cli-plugins/metadata/annotations.go
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
package metadata
|
||||
|
||||
const (
|
||||
// CommandAnnotationPlugin is added to every stub command added by
|
||||
// AddPluginCommandStubs with the value "true" and so can be
|
||||
// used to distinguish plugin stubs from regular commands.
|
||||
CommandAnnotationPlugin = "com.docker.cli.plugin"
|
||||
|
||||
// CommandAnnotationPluginVendor is added to every stub command
|
||||
// added by AddPluginCommandStubs and contains the vendor of
|
||||
// that plugin.
|
||||
CommandAnnotationPluginVendor = "com.docker.cli.plugin.vendor"
|
||||
|
||||
// CommandAnnotationPluginVersion is added to every stub command
|
||||
// added by AddPluginCommandStubs and contains the version of
|
||||
// that plugin.
|
||||
CommandAnnotationPluginVersion = "com.docker.cli.plugin.version"
|
||||
|
||||
// CommandAnnotationPluginInvalid is added to any stub command
|
||||
// added by AddPluginCommandStubs for an invalid command (that
|
||||
// is, one which failed it's candidate test) and contains the
|
||||
// reason for the failure.
|
||||
CommandAnnotationPluginInvalid = "com.docker.cli.plugin-invalid"
|
||||
|
||||
// CommandAnnotationPluginCommandPath is added to overwrite the
|
||||
// command path for a plugin invocation.
|
||||
CommandAnnotationPluginCommandPath = "com.docker.cli.plugin.command_path"
|
||||
)
|
@@ -1,4 +1,4 @@
|
||||
package manager
|
||||
package metadata
|
||||
|
||||
const (
|
||||
// NamePrefix is the prefix required on all plugin binary names
|
||||
@@ -13,6 +13,12 @@ const (
|
||||
// which must be implemented by plugins declaring support
|
||||
// for hooks in their metadata.
|
||||
HookSubcommandName = "docker-cli-plugin-hooks"
|
||||
|
||||
// ReexecEnvvar is the name of an ennvar which is set to the command
|
||||
// used to originally invoke the docker CLI when executing a
|
||||
// plugin. Assuming $PATH and $CWD remain unchanged this should allow
|
||||
// the plugin to re-execute the original CLI.
|
||||
ReexecEnvvar = "DOCKER_CLI_PLUGIN_ORIGINAL_CLI_COMMAND"
|
||||
)
|
||||
|
||||
// Metadata provided by the plugin.
|
20
vendor/github.com/docker/cli/cli-plugins/plugin/plugin.go
generated
vendored
20
vendor/github.com/docker/cli/cli-plugins/plugin/plugin.go
generated
vendored
@@ -9,7 +9,7 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli-plugins/manager"
|
||||
"github.com/docker/cli/cli-plugins/metadata"
|
||||
"github.com/docker/cli/cli-plugins/socket"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/connhelper"
|
||||
@@ -30,7 +30,7 @@ import (
|
||||
var PersistentPreRunE func(*cobra.Command, []string) error
|
||||
|
||||
// RunPlugin executes the specified plugin command
|
||||
func RunPlugin(dockerCli *command.DockerCli, plugin *cobra.Command, meta manager.Metadata) error {
|
||||
func RunPlugin(dockerCli *command.DockerCli, plugin *cobra.Command, meta metadata.Metadata) error {
|
||||
tcmd := newPluginCommand(dockerCli, plugin, meta)
|
||||
|
||||
var persistentPreRunOnce sync.Once
|
||||
@@ -81,7 +81,7 @@ func RunPlugin(dockerCli *command.DockerCli, plugin *cobra.Command, meta manager
|
||||
}
|
||||
|
||||
// Run is the top-level entry point to the CLI plugin framework. It should be called from your plugin's `main()` function.
|
||||
func Run(makeCmd func(command.Cli) *cobra.Command, meta manager.Metadata) {
|
||||
func Run(makeCmd func(command.Cli) *cobra.Command, meta metadata.Metadata) {
|
||||
otel.SetErrorHandler(debug.OTELErrorHandler)
|
||||
|
||||
dockerCli, err := command.NewDockerCli()
|
||||
@@ -111,7 +111,7 @@ func Run(makeCmd func(command.Cli) *cobra.Command, meta manager.Metadata) {
|
||||
func withPluginClientConn(name string) command.CLIOption {
|
||||
return command.WithInitializeClient(func(dockerCli *command.DockerCli) (client.APIClient, error) {
|
||||
cmd := "docker"
|
||||
if x := os.Getenv(manager.ReexecEnvvar); x != "" {
|
||||
if x := os.Getenv(metadata.ReexecEnvvar); x != "" {
|
||||
cmd = x
|
||||
}
|
||||
var flags []string
|
||||
@@ -140,9 +140,9 @@ func withPluginClientConn(name string) command.CLIOption {
|
||||
})
|
||||
}
|
||||
|
||||
func newPluginCommand(dockerCli *command.DockerCli, plugin *cobra.Command, meta manager.Metadata) *cli.TopLevelCommand {
|
||||
func newPluginCommand(dockerCli *command.DockerCli, plugin *cobra.Command, meta metadata.Metadata) *cli.TopLevelCommand {
|
||||
name := plugin.Name()
|
||||
fullname := manager.NamePrefix + name
|
||||
fullname := metadata.NamePrefix + name
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: fmt.Sprintf("docker [OPTIONS] %s [ARG...]", name),
|
||||
@@ -177,12 +177,12 @@ func newPluginCommand(dockerCli *command.DockerCli, plugin *cobra.Command, meta
|
||||
return cli.NewTopLevelCommand(cmd, dockerCli, opts, cmd.Flags())
|
||||
}
|
||||
|
||||
func newMetadataSubcommand(plugin *cobra.Command, meta manager.Metadata) *cobra.Command {
|
||||
func newMetadataSubcommand(plugin *cobra.Command, meta metadata.Metadata) *cobra.Command {
|
||||
if meta.ShortDescription == "" {
|
||||
meta.ShortDescription = plugin.Short
|
||||
}
|
||||
cmd := &cobra.Command{
|
||||
Use: manager.MetadataSubcommandName,
|
||||
Use: metadata.MetadataSubcommandName,
|
||||
Hidden: true,
|
||||
// Suppress the global/parent PersistentPreRunE, which
|
||||
// needlessly initializes the client and tries to
|
||||
@@ -200,8 +200,8 @@ func newMetadataSubcommand(plugin *cobra.Command, meta manager.Metadata) *cobra.
|
||||
|
||||
// RunningStandalone tells a CLI plugin it is run standalone by direct execution
|
||||
func RunningStandalone() bool {
|
||||
if os.Getenv(manager.ReexecEnvvar) != "" {
|
||||
if os.Getenv(metadata.ReexecEnvvar) != "" {
|
||||
return false
|
||||
}
|
||||
return len(os.Args) < 2 || os.Args[1] != manager.MetadataSubcommandName
|
||||
return len(os.Args) < 2 || os.Args[1] != metadata.MetadataSubcommandName
|
||||
}
|
||||
|
20
vendor/github.com/docker/cli/cli/cobra.go
generated
vendored
20
vendor/github.com/docker/cli/cli/cobra.go
generated
vendored
@@ -3,15 +3,12 @@ package cli
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
pluginmanager "github.com/docker/cli/cli-plugins/manager"
|
||||
"github.com/docker/cli/cli-plugins/metadata"
|
||||
"github.com/docker/cli/cli/command"
|
||||
cliflags "github.com/docker/cli/cli/flags"
|
||||
"github.com/docker/docker/pkg/homedir"
|
||||
"github.com/docker/docker/registry"
|
||||
"github.com/fvbommel/sortorder"
|
||||
"github.com/moby/term"
|
||||
"github.com/morikuni/aec"
|
||||
@@ -62,13 +59,6 @@ func setupCommonRootCommand(rootCmd *cobra.Command) (*cliflags.ClientOptions, *c
|
||||
"docs.code-delimiter": `"`, // https://github.com/docker/cli-docs-tool/blob/77abede22166eaea4af7335096bdcedd043f5b19/annotation/annotation.go#L20-L22
|
||||
}
|
||||
|
||||
// Configure registry.CertsDir() when running in rootless-mode
|
||||
if os.Getenv("ROOTLESSKIT_STATE_DIR") != "" {
|
||||
if configHome, err := homedir.GetConfigHome(); err == nil {
|
||||
registry.SetCertsDir(filepath.Join(configHome, "docker/certs.d"))
|
||||
}
|
||||
}
|
||||
|
||||
return opts, helpCommand
|
||||
}
|
||||
|
||||
@@ -252,7 +242,7 @@ func hasAdditionalHelp(cmd *cobra.Command) bool {
|
||||
}
|
||||
|
||||
func isPlugin(cmd *cobra.Command) bool {
|
||||
return pluginmanager.IsPluginCommand(cmd)
|
||||
return cmd.Annotations[metadata.CommandAnnotationPlugin] == "true"
|
||||
}
|
||||
|
||||
func hasAliases(cmd *cobra.Command) bool {
|
||||
@@ -356,9 +346,9 @@ func decoratedName(cmd *cobra.Command) string {
|
||||
}
|
||||
|
||||
func vendorAndVersion(cmd *cobra.Command) string {
|
||||
if vendor, ok := cmd.Annotations[pluginmanager.CommandAnnotationPluginVendor]; ok && isPlugin(cmd) {
|
||||
if vendor, ok := cmd.Annotations[metadata.CommandAnnotationPluginVendor]; ok && isPlugin(cmd) {
|
||||
version := ""
|
||||
if v, ok := cmd.Annotations[pluginmanager.CommandAnnotationPluginVersion]; ok && v != "" {
|
||||
if v, ok := cmd.Annotations[metadata.CommandAnnotationPluginVersion]; ok && v != "" {
|
||||
version = ", " + v
|
||||
}
|
||||
return fmt.Sprintf("(%s%s)", vendor, version)
|
||||
@@ -417,7 +407,7 @@ func invalidPlugins(cmd *cobra.Command) []*cobra.Command {
|
||||
}
|
||||
|
||||
func invalidPluginReason(cmd *cobra.Command) string {
|
||||
return cmd.Annotations[pluginmanager.CommandAnnotationPluginInvalid]
|
||||
return cmd.Annotations[metadata.CommandAnnotationPluginInvalid]
|
||||
}
|
||||
|
||||
const usageTemplate = `Usage:
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user