Skip to content
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
19 changes: 4 additions & 15 deletions build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,10 @@ func toSolveOpt(ctx context.Context, node builder.Node, multiDriver bool, opt Op
so.FrontendAttrs["attest:provenance"] = "mode=min,inline-only=true"
}

for k, v := range getGitAttributes(ctx, opt.Inputs.ContextPath, opt.Inputs.DockerfilePath) {
so.FrontendAttrs[k] = v
}

// set platforms
if len(opt.Platforms) != 0 {
pp := make([]string, len(opt.Platforms))
Expand Down Expand Up @@ -846,21 +850,6 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opt map[s

eg, ctx := errgroup.WithContext(ctx)

for _, opt := range opt {
gitLabels, err := addGitProvenance(ctx, opt.Inputs.ContextPath, opt.Inputs.DockerfilePath)
if err != nil {
return nil, err
}
for n, v := range gitLabels {
if _, ok := opt.Labels[n]; !ok {
if opt.Labels == nil {
opt.Labels = map[string]string{}
}
opt.Labels[n] = v
}
}
}

for k, opt := range opt {
multiDriver := len(m[k]) > 1
hasMobyDriver := false
Expand Down
129 changes: 70 additions & 59 deletions build/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,41 @@ package build
import (
"context"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"

ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/docker/buildx/util/gitutil"
specs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/sirupsen/logrus"
)

const DockerfileLabel = "com.docker.image.source.entrypoint"

func addGitProvenance(ctx context.Context, contextPath string, dockerfilePath string) (map[string]string, error) {
v := os.Getenv("BUILDX_GIT_LABELS")
if (v != "1" && v != "full") || contextPath == "" {
return nil, nil
func getGitAttributes(ctx context.Context, contextPath string, dockerfilePath string) (res map[string]string) {
res = make(map[string]string)
if contextPath == "" {
return
}

setGitLabels := false
if v, ok := os.LookupEnv("BUILDX_GIT_LABELS"); ok {
if v == "full" { // backward compatibility with old "full" mode
setGitLabels = true
} else if v, _ := strconv.ParseBool(v); v {
setGitLabels = v
}
}
setGitInfo := true
if v, ok := os.LookupEnv("BUILDX_GIT_INFO"); ok {
if v, _ := strconv.ParseBool(v); v {
setGitInfo = v
}
}

if !setGitLabels && !setGitInfo {
return
}
labels := make(map[string]string, 0)

// figure out in which directory the git command needs to run in
var wd string
Expand All @@ -30,69 +48,62 @@ func addGitProvenance(ctx context.Context, contextPath string, dockerfilePath st
wd, _ = filepath.Abs(filepath.Join(cwd, contextPath))
}

// check if inside git working tree
cmd := exec.CommandContext(ctx, "git", "rev-parse", "--is-inside-work-tree")
cmd.Dir = wd
err := cmd.Run()
if err != nil {
gitc := gitutil.New(gitutil.WithContext(ctx), gitutil.WithWorkingDir(wd))
if !gitc.IsInsideWorkTree() {
Comment thread
crazy-max marked this conversation as resolved.
logrus.Warnf("Unable to determine Git information")
return nil, nil
return
}

// obtain Git sha of current HEAD
cmd = exec.CommandContext(ctx, "git", "rev-parse", "HEAD")
cmd.Dir = wd
out, err := cmd.Output()
if err != nil {
return nil, errors.Wrap(err, "error obtaining git head")
}
sha := strings.TrimSpace(string(out))
var resRevision, resSource, resDockerfilePath string

// check if the current HEAD is clean
cmd = exec.CommandContext(ctx, "git", "status", "--porcelain", "--ignored")
cmd.Dir = wd
out, err = cmd.Output()
if err != nil {
return nil, errors.Wrap(err, "error obtaining git status")
}
if len(strings.TrimSpace(string(out))) != 0 {
sha += "-dirty"
}
labels[ocispecs.AnnotationRevision] = sha

// add a remote url if full Git details are requested; if there aren't any remotes don't fail
if v == "full" {
cmd = exec.CommandContext(ctx, "git", "ls-remote", "--get-url")
cmd.Dir = wd
out, _ := cmd.Output()
if len(out) > 0 {
labels[ocispecs.AnnotationSource] = strings.TrimSpace(string(out))
if sha, err := gitc.FullCommit(); err == nil && sha != "" {
resRevision = sha
if gitc.IsDirty() {

@tonistiigi tonistiigi Dec 13, 2022

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It looks like this increases the amount of git calls compared to the previous implementation? Can be handled in follow up, but every invocation counts.

Edit: I might have counted the requests wrong. Looks like previous also did this with 2 commands.

resRevision += "-dirty"
}
}

// add Dockerfile path; there is no org.opencontainers annotation for this
if dockerfilePath == "" {
dockerfilePath = filepath.Join(wd, "Dockerfile")
if rurl, err := gitc.RemoteURL(); err == nil && rurl != "" {
resSource = rurl
}

// obtain Git root directory
cmd = exec.CommandContext(ctx, "git", "rev-parse", "--show-toplevel")
cmd.Dir = wd
out, err = cmd.Output()
if err != nil {
return nil, errors.Wrap(err, "failed to get git root")
if setGitLabels {
if root, err := gitc.RootDir(); err == nil && root != "" {
if dockerfilePath == "" {
dockerfilePath = filepath.Join(wd, "Dockerfile")
}
if !filepath.IsAbs(dockerfilePath) {
cwd, _ := os.Getwd()
dockerfilePath = filepath.Join(cwd, dockerfilePath)
}
dockerfilePath, _ = filepath.Rel(root, dockerfilePath)
if !strings.HasPrefix(dockerfilePath, "..") {
resDockerfilePath = dockerfilePath
}
}
}
root := strings.TrimSpace(string(out))

// record only Dockerfile paths that are within the Git root
if !filepath.IsAbs(dockerfilePath) {
cwd, _ := os.Getwd()
dockerfilePath = filepath.Join(cwd, dockerfilePath)
if resSource != "" {
if setGitLabels {
res["label:"+specs.AnnotationSource] = resSource
}
if setGitInfo {
res["vcs:source"] = resSource
}
}
dockerfilePath, _ = filepath.Rel(root, dockerfilePath)
if !strings.HasPrefix(dockerfilePath, "..") {
labels[DockerfileLabel] = dockerfilePath
if resRevision != "" {
if setGitLabels {
res["label:"+specs.AnnotationRevision] = resRevision
}
if setGitInfo {
res["vcs:revision"] = resRevision
}
}
if resDockerfilePath != "" {
if setGitLabels {
res["label:"+DockerfileLabel] = resDockerfilePath
}
}

return labels, nil
return
}
Loading