Skip to content

Commit

Permalink
Add git provenance labels
Browse files Browse the repository at this point in the history
as per docker#1290

Signed-off-by: Christian Dupuis <cd@atomist.com>
  • Loading branch information
cdupuis committed Aug 29, 2022
1 parent 1bb375f commit b929c2f
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 1 deletion.
79 changes: 78 additions & 1 deletion commands/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
Expand Down Expand Up @@ -128,6 +129,11 @@ func runBuild(dockerCli command.Cli, in buildOptions) (err error) {
return err
}

labels, err := addGitProvenance(in.labels, in.contextPath, in.dockerfileName)
if err != nil {
return err
}

opts := build.Options{
Inputs: build.Inputs{
ContextPath: in.contextPath,
Expand All @@ -138,7 +144,7 @@ func runBuild(dockerCli command.Cli, in buildOptions) (err error) {
BuildArgs: listToMap(in.buildArgs, true),
ExtraHosts: in.extraHosts,
ImageIDFile: in.imageIDFile,
Labels: listToMap(in.labels, false),
Labels: listToMap(labels, false),
NetworkMode: in.networkMode,
NoCache: noCache,
NoCacheFilter: in.noCacheFilter,
Expand Down Expand Up @@ -643,6 +649,77 @@ func parsePrintFunc(str string) (*build.PrintFunc, error) {
return f, nil
}

func addGitProvenance(labels []string, contextPath string, dockerfilePath string) ([]string, error) {
if v, ok := os.LookupEnv("BUILDX_GIT_INFO"); ok && contextPath != "" {
if len(labels) == 0 {
labels = make([]string, 0)
}

// figure out in which directory the git command needs to run
var wd string
cwd, _ := os.Getwd()
if !filepath.IsAbs(contextPath) {
wd, _ = filepath.Abs(filepath.Join(cwd, contextPath))
} else {
wd = contextPath
}

// obtain Git sha of current HEAD
cmd := exec.Command("git", "rev-parse", "HEAD")
cmd.Dir = wd
sha, err := cmd.Output()
if err != nil {
return nil, fmt.Errorf("error obtaining git head: %w", err)
}

// check if the current HEAD is clean
cmd = exec.Command("git", "diff", "--quiet", "--exit-code")
cmd.Dir = wd
_, err = cmd.Output()
if err == nil {
labels = append(labels, []string{fmt.Sprintf("org.opencontainers.image.revision=%s", strings.TrimSpace(string(sha)))}...)
} else {
labels = append(labels, []string{fmt.Sprintf("org.opencontainers.image.revision=%s-dirty", strings.TrimSpace(string(sha)))}...)
}

// add the origin url if full Git details are requested
if v == "full" {
cmd = exec.Command("git", "config", "--get", "remote.origin.url")
cmd.Dir = wd
remote, err := cmd.Output()
if err != nil {
return nil, err
}
labels = append(labels, []string{fmt.Sprintf("org.opencontainers.image.source=%s", strings.TrimSpace(string(remote)))}...)
}

// add Dockerfile path; there is no org.opencontainers annotation for this
if dockerfilePath == "" {
dockerfilePath = "Dockerfile"
}

// obtain Git root directory
cmd = exec.Command("git", "rev-parse", "--show-toplevel")
cmd.Dir = wd
root, err := cmd.Output()
if err != nil {
return nil, err
}

// make the Dockerfile path relative to the Git root
if !filepath.IsAbs(dockerfilePath) {
dockerfilePath = filepath.Join(cwd, dockerfilePath)
}
dockerfilePath, err = filepath.Rel(strings.TrimSpace(string(root)), dockerfilePath)
if err != nil {
return nil, err
}
labels = append(labels, []string{fmt.Sprintf("com.docker.image.dockerfile.path=%s", dockerfilePath)}...)
}

return labels, nil
}

func writeMetadataFile(filename string, dt interface{}) error {
b, err := json.MarshalIndent(dt, "", " ")
if err != nil {
Expand Down
70 changes: 70 additions & 0 deletions commands/build_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package commands

import (
"os"
"strings"
"testing"
)

func setupTest(tb testing.TB) func(tb testing.TB) {
return func(tb testing.TB) {
os.Unsetenv("BUILDX_GIT_INFO")
}
}

func TestAddGitProvenanceDataWithoutEnv(t *testing.T) {
labels, err := addGitProvenance(nil, ".", "")
if err != nil {
t.Error("No error expected")
}
if labels != nil {
t.Error("No labels expected")
}
}

func TestAddGitProvenanceDataWithoutLabels(t *testing.T) {
os.Setenv("BUILDX_GIT_INFO", "full")
labels, err := addGitProvenance(nil, ".", "")
if err != nil {
t.Error("No error expected")
}
if len(labels) != 3 {
t.Error("Exactly 3 git provenance labels expected")
}
dockerfileLabel := strings.Split(labels[2], "=")
if dockerfileLabel[0] != "com.docker.image.dockerfile.path" || dockerfileLabel[1] != "Dockerfile" {
t.Error("Expected a dockerfile path provenance label")
}
shaLabel := strings.Split(labels[0], "=")
if shaLabel[0] != "org.opencontainers.image.revision" {
t.Error("Expected a sha provenance label")
}
originLabel := strings.Split(labels[1], "=")
if originLabel[0] != "org.opencontainers.image.source" {
t.Error("Expected a origin provenance label")
}
}

func TestAddGitProvenanceDataWithLabels(t *testing.T) {
os.Setenv("BUILDX_GIT_INFO", "full")
existingLabels := []string{"foo=bar"}
labels, err := addGitProvenance(existingLabels, ".", "")
if err != nil {
t.Error("No error expected")
}
if len(labels) != 4 {
t.Error("Exactly 3 git provenance labels expected")
}
dockerfileLabel := strings.Split(labels[3], "=")
if dockerfileLabel[0] != "com.docker.image.dockerfile.path" || dockerfileLabel[1] != "Dockerfile" {
t.Error("Expected a dockerfile path provenance label")
}
shaLabel := strings.Split(labels[1], "=")
if shaLabel[0] != "org.opencontainers.image.revision" {
t.Error("Expected a sha provenance label")
}
originLabel := strings.Split(labels[2], "=")
if originLabel[0] != "org.opencontainers.image.source" {
t.Error("Expected a origin provenance label")
}
}

0 comments on commit b929c2f

Please sign in to comment.