Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

build: display Dockerfile path on check warnings #2672

Merged
merged 2 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 17 additions & 12 deletions build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ type Inputs struct {
ContextState *llb.State
DockerfileInline string
NamedContexts map[string]NamedContext
// DockerfileMappingSrc and DockerfileMappingDst are filled in by the builder.
DockerfileMappingSrc string
daghack marked this conversation as resolved.
Show resolved Hide resolved
DockerfileMappingDst string
}

type NamedContext struct {
Expand Down Expand Up @@ -147,11 +150,11 @@ func toRepoOnly(in string) (string, error) {
return strings.Join(out, ","), nil
}

func Build(ctx context.Context, nodes []builder.Node, opt map[string]Options, docker *dockerutil.Client, configDir string, w progress.Writer) (resp map[string]*client.SolveResponse, err error) {
return BuildWithResultHandler(ctx, nodes, opt, docker, configDir, w, nil)
func Build(ctx context.Context, nodes []builder.Node, opts map[string]Options, docker *dockerutil.Client, configDir string, w progress.Writer) (resp map[string]*client.SolveResponse, err error) {
return BuildWithResultHandler(ctx, nodes, opts, docker, configDir, w, nil)
}

func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opt map[string]Options, docker *dockerutil.Client, configDir string, w progress.Writer, resultHandleFunc func(driverIndex int, rCtx *ResultHandle)) (resp map[string]*client.SolveResponse, err error) {
func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[string]Options, docker *dockerutil.Client, configDir string, w progress.Writer, resultHandleFunc func(driverIndex int, rCtx *ResultHandle)) (resp map[string]*client.SolveResponse, err error) {
if len(nodes) == 0 {
return nil, errors.Errorf("driver required for build")
}
Expand All @@ -169,9 +172,9 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opt map[s
}
}

if noMobyDriver != nil && !noDefaultLoad() && noCallFunc(opt) {
if noMobyDriver != nil && !noDefaultLoad() && noCallFunc(opts) {
var noOutputTargets []string
for name, opt := range opt {
for name, opt := range opts {
if noMobyDriver.Features(ctx)[driver.DefaultLoad] {
continue
}
Expand All @@ -192,7 +195,7 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opt map[s
}
}

drivers, err := resolveDrivers(ctx, nodes, opt, w)
drivers, err := resolveDrivers(ctx, nodes, opts, w)
if err != nil {
return nil, err
}
Expand All @@ -209,7 +212,7 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opt map[s
reqForNodes := make(map[string][]*reqForNode)
eg, ctx := errgroup.WithContext(ctx)

for k, opt := range opt {
for k, opt := range opts {
multiDriver := len(drivers[k]) > 1
hasMobyDriver := false
addGitAttrs, err := getGitAttributes(ctx, opt.Inputs.ContextPath, opt.Inputs.DockerfilePath)
Expand All @@ -229,7 +232,9 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opt map[s
if err != nil {
return nil, err
}
so, release, err := toSolveOpt(ctx, np.Node(), multiDriver, opt, gatewayOpts, configDir, w, docker)
localOpt := opt
so, release, err := toSolveOpt(ctx, np.Node(), multiDriver, &localOpt, gatewayOpts, configDir, w, docker)
opts[k] = localOpt
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -269,7 +274,7 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opt map[s
}

// validate that all links between targets use same drivers
for name := range opt {
for name := range opts {
dps := reqForNodes[name]
for i, dp := range dps {
so := reqForNodes[name][i].so
Expand Down Expand Up @@ -305,10 +310,10 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opt map[s
var respMu sync.Mutex
results := waitmap.New()

multiTarget := len(opt) > 1
childTargets := calculateChildTargets(reqForNodes, opt)
multiTarget := len(opts) > 1
childTargets := calculateChildTargets(reqForNodes, opts)

for k, opt := range opt {
for k, opt := range opts {
err := func(k string) (err error) {
opt := opt
dps := drivers[k]
Expand Down
25 changes: 17 additions & 8 deletions build/opt.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import (
"github.com/tonistiigi/fsutil"
)

func toSolveOpt(ctx context.Context, node builder.Node, multiDriver bool, opt Options, bopts gateway.BuildOpts, configDir string, pw progress.Writer, docker *dockerutil.Client) (_ *client.SolveOpt, release func(), err error) {
func toSolveOpt(ctx context.Context, node builder.Node, multiDriver bool, opt *Options, bopts gateway.BuildOpts, configDir string, pw progress.Writer, docker *dockerutil.Client) (_ *client.SolveOpt, release func(), err error) {
nodeDriver := node.Driver
defers := make([]func(), 0, 2)
releaseF := func() {
Expand Down Expand Up @@ -263,7 +263,7 @@ func toSolveOpt(ctx context.Context, node builder.Node, multiDriver bool, opt Op
so.Exports = opt.Exports
so.Session = slices.Clone(opt.Session)

releaseLoad, err := loadInputs(ctx, nodeDriver, opt.Inputs, pw, &so)
releaseLoad, err := loadInputs(ctx, nodeDriver, &opt.Inputs, pw, &so)
if err != nil {
return nil, nil, err
}
Expand Down Expand Up @@ -356,19 +356,20 @@ func toSolveOpt(ctx context.Context, node builder.Node, multiDriver bool, opt Op
return &so, releaseF, nil
}

func loadInputs(ctx context.Context, d *driver.DriverHandle, inp Inputs, pw progress.Writer, target *client.SolveOpt) (func(), error) {
func loadInputs(ctx context.Context, d *driver.DriverHandle, inp *Inputs, pw progress.Writer, target *client.SolveOpt) (func(), error) {
if inp.ContextPath == "" {
return nil, errors.New("please specify build context (e.g. \".\" for the current directory)")
}

// TODO: handle stdin, symlinks, remote contexts, check files exist

var (
err error
dockerfileReader io.ReadCloser
dockerfileDir string
dockerfileName = inp.DockerfilePath
toRemove []string
err error
dockerfileReader io.ReadCloser
dockerfileDir string
dockerfileName = inp.DockerfilePath
dockerfileSrcName = inp.DockerfilePath
toRemove []string
)

switch {
Expand Down Expand Up @@ -440,6 +441,11 @@ func loadInputs(ctx context.Context, d *driver.DriverHandle, inp Inputs, pw prog

if inp.DockerfileInline != "" {
dockerfileReader = io.NopCloser(strings.NewReader(inp.DockerfileInline))
dockerfileSrcName = "inline"
} else if inp.DockerfilePath == "-" {
dockerfileSrcName = "stdin"
Comment on lines +444 to +446
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What will be the output for inline and stdin case? I guess it should be omitted like:

Command 'copy' should match the case of the command majority (uppercase)
--------------------
   1 |     FROM alpine
   2 | >>> copy ./tmp /tmp
   3 |
   4 |
--------------------

Something like this would be confusing:

Command 'copy' should match the case of the command majority (uppercase)
inline:2
--------------------
   1 |     FROM alpine
   2 | >>> copy ./tmp /tmp
   3 |
   4 |
--------------------

If someone has a Dockerfile named inline

Copy link
Contributor Author

@daghack daghack Sep 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing the line entirely requires either a buildkit PR or refactoring how buildx is printing warnings to effectively duplicate that functionality. I'm not opposed, but if this is a strong ask then it might be best to merge this and then make a corresponding issue in buildkit for the follow-up work.

I was trying to model what @tonistiigi had suggested, here. For MY part, I think that ambiguity around two very specifically named Dockerfiles (in this case, stdin and inline) is less confusing than simply not showing anything in these cases. That being said, it could be updated to something a little more clear and even less likely to be a filename?

Command 'copy' should match the case of the command majority (uppercase)
Inline Dockerfile:2
--------------------
...
Command 'copy' should match the case of the command majority (uppercase)
Dockerfile (inline):2
--------------------
...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That being said, it could be updated to something a little more clear and even less likely to be a filename?

Yes that sounds good, let's check that for a follow-up then

} else if inp.DockerfilePath == "" {
dockerfileSrcName = filepath.Join(inp.ContextPath, "Dockerfile")
}

if dockerfileReader != nil {
Expand Down Expand Up @@ -540,6 +546,9 @@ func loadInputs(ctx context.Context, d *driver.DriverHandle, inp Inputs, pw prog
_ = os.RemoveAll(dir)
}
}

inp.DockerfileMappingSrc = dockerfileSrcName
inp.DockerfileMappingDst = dockerfileName
return release, nil
}

Expand Down
4 changes: 2 additions & 2 deletions commands/bake.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba
if callFormatJSON {
jsonResults[name] = map[string]any{}
buf := &bytes.Buffer{}
if code, err := printResult(buf, pf, res); err != nil {
if code, err := printResult(buf, pf, res, name, &req.Inputs); err != nil {
jsonResults[name]["error"] = err.Error()
exitCode = 1
} else if code != 0 && exitCode == 0 {
Expand All @@ -361,7 +361,7 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba
}

fmt.Fprintln(dockerCli.Out())
if code, err := printResult(dockerCli.Out(), pf, res); err != nil {
if code, err := printResult(dockerCli.Out(), pf, res, name, &req.Inputs); err != nil {
fmt.Fprintf(dockerCli.Out(), "error: %v\n", err)
exitCode = 1
} else if code != 0 && exitCode == 0 {
Expand Down
66 changes: 39 additions & 27 deletions commands/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import (
"github.com/moby/buildkit/frontend/subrequests/outline"
"github.com/moby/buildkit/frontend/subrequests/targets"
"github.com/moby/buildkit/solver/errdefs"
solverpb "github.com/moby/buildkit/solver/pb"
"github.com/moby/buildkit/util/grpcerrors"
"github.com/moby/buildkit/util/progress/progressui"
"github.com/morikuni/aec"
Expand Down Expand Up @@ -346,11 +347,12 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions)

done := timeBuildCommand(mp, attributes)
var resp *client.SolveResponse
var inputs *build.Inputs
var retErr error
if confutil.IsExperimental() {
resp, retErr = runControllerBuild(ctx, dockerCli, opts, options, printer)
resp, inputs, retErr = runControllerBuild(ctx, dockerCli, opts, options, printer)
} else {
resp, retErr = runBasicBuild(ctx, dockerCli, opts, printer)
resp, inputs, retErr = runBasicBuild(ctx, dockerCli, opts, printer)
}

if err := printer.Wait(); retErr == nil {
Expand Down Expand Up @@ -387,7 +389,7 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions)
}
}
if opts.CallFunc != nil {
if exitcode, err := printResult(dockerCli.Out(), opts.CallFunc, resp.ExporterResponse); err != nil {
if exitcode, err := printResult(dockerCli.Out(), opts.CallFunc, resp.ExporterResponse, options.target, inputs); err != nil {
return err
} else if exitcode != 0 {
os.Exit(exitcode)
Expand All @@ -405,22 +407,22 @@ func getImageID(resp map[string]string) string {
return dgst
}

func runBasicBuild(ctx context.Context, dockerCli command.Cli, opts *controllerapi.BuildOptions, printer *progress.Printer) (*client.SolveResponse, error) {
resp, res, err := cbuild.RunBuild(ctx, dockerCli, *opts, dockerCli.In(), printer, false)
func runBasicBuild(ctx context.Context, dockerCli command.Cli, opts *controllerapi.BuildOptions, printer *progress.Printer) (*client.SolveResponse, *build.Inputs, error) {
resp, res, dfmap, err := cbuild.RunBuild(ctx, dockerCli, *opts, dockerCli.In(), printer, false)
if res != nil {
res.Done()
}
return resp, err
return resp, dfmap, err
}

func runControllerBuild(ctx context.Context, dockerCli command.Cli, opts *controllerapi.BuildOptions, options buildOptions, printer *progress.Printer) (*client.SolveResponse, error) {
func runControllerBuild(ctx context.Context, dockerCli command.Cli, opts *controllerapi.BuildOptions, options buildOptions, printer *progress.Printer) (*client.SolveResponse, *build.Inputs, error) {
if options.invokeConfig != nil && (options.dockerfileName == "-" || options.contextPath == "-") {
// stdin must be usable for monitor
return nil, errors.Errorf("Dockerfile or context from stdin is not supported with invoke")
return nil, nil, errors.Errorf("Dockerfile or context from stdin is not supported with invoke")
}
c, err := controller.NewController(ctx, options.ControlOptions, dockerCli, printer)
if err != nil {
return nil, err
return nil, nil, err
}
defer func() {
if err := c.Close(); err != nil {
Expand All @@ -432,12 +434,13 @@ func runControllerBuild(ctx context.Context, dockerCli command.Cli, opts *contro
// so we need to resolve paths to abosolute ones in the client.
opts, err = controllerapi.ResolveOptionPaths(opts)
if err != nil {
return nil, err
return nil, nil, err
}

var ref string
var retErr error
var resp *client.SolveResponse
var inputs *build.Inputs

var f *ioset.SingleForwarder
var pr io.ReadCloser
Expand All @@ -455,15 +458,15 @@ func runControllerBuild(ctx context.Context, dockerCli command.Cli, opts *contro
})
}

ref, resp, err = c.Build(ctx, *opts, pr, printer)
ref, resp, inputs, err = c.Build(ctx, *opts, pr, printer)
if err != nil {
var be *controllererrors.BuildError
if errors.As(err, &be) {
ref = be.Ref
retErr = err
// We can proceed to monitor
} else {
return nil, errors.Wrapf(err, "failed to build")
return nil, nil, errors.Wrapf(err, "failed to build")
}
}

Expand Down Expand Up @@ -504,7 +507,7 @@ func runControllerBuild(ctx context.Context, dockerCli command.Cli, opts *contro
}
}

return resp, retErr
return resp, inputs, retErr
}

func printError(err error, printer *progress.Printer) error {
Expand Down Expand Up @@ -882,7 +885,7 @@ func printWarnings(w io.Writer, warnings []client.VertexWarning, mode progressui
}
}

func printResult(w io.Writer, f *controllerapi.CallFunc, res map[string]string) (int, error) {
func printResult(w io.Writer, f *controllerapi.CallFunc, res map[string]string, target string, inp *build.Inputs) (int, error) {
switch f.Name {
case "outline":
return 0, printValue(w, outline.PrintOutline, outline.SubrequestsOutlineDefinition.Version, f.Format, res)
Expand All @@ -908,8 +911,27 @@ func printResult(w io.Writer, f *controllerapi.CallFunc, res map[string]string)
}
fmt.Fprintf(w, "Check complete, %s\n", warningCountMsg)
}
sourceInfoMap := func(sourceInfo *solverpb.SourceInfo) *solverpb.SourceInfo {
if sourceInfo == nil || inp == nil {
return sourceInfo
}
if target == "" {
target = "default"
}

if inp.DockerfileMappingSrc != "" {
newSourceInfo := *sourceInfo
newSourceInfo.Filename = inp.DockerfileMappingSrc
return &newSourceInfo
}
return sourceInfo
}

printLintWarnings := func(dt []byte, w io.Writer) error {
return lintResults.PrintTo(w, sourceInfoMap)
}

err := printValue(w, printLintViolationsWrapper, lint.SubrequestLintDefinition.Version, f.Format, res)
err := printValue(w, printLintWarnings, lint.SubrequestLintDefinition.Version, f.Format, res)
if err != nil {
return 0, err
}
Expand All @@ -924,13 +946,8 @@ func printResult(w io.Writer, f *controllerapi.CallFunc, res map[string]string)
if f.Format != "json" && len(lintResults.Warnings) > 0 {
fmt.Fprintln(w)
}
lintBuf := bytes.NewBuffer([]byte(lintResults.Error.Message + "\n"))
sourceInfo := lintResults.Sources[lintResults.Error.Location.SourceIndex]
source := errdefs.Source{
Info: sourceInfo,
Ranges: lintResults.Error.Location.Ranges,
}
source.Print(lintBuf)
lintBuf := bytes.NewBuffer(nil)
lintResults.PrintErrorTo(lintBuf, sourceInfoMap)
return 0, errors.New(lintBuf.String())
} else if len(lintResults.Warnings) == 0 && f.Format != "json" {
fmt.Fprintln(w, "Check complete, no warnings found.")
Expand Down Expand Up @@ -968,11 +985,6 @@ func printValue(w io.Writer, printer callFunc, version string, format string, re
return printer([]byte(res["result.json"]), w)
}

// FIXME: remove once https://github.com/docker/buildx/pull/2672 is sorted
func printLintViolationsWrapper(dt []byte, w io.Writer) error {
return lint.PrintLintViolations(dt, w, nil)
}

type invokeConfig struct {
controllerapi.InvokeConfig
onFlag string
Expand Down
Loading