From 865780adf5d2d4bbbc178f60ebdae88199607e5f Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Thu, 3 Oct 2024 07:26:38 -0700 Subject: [PATCH] Look up git metadata via GitHub env vars when available and fall back to `git` operations otherwise. This also changes the way that a `git` repo is detected to use `git` itself instead of checking for `.git` directory existence. (linked to internal issue PLAT-228) --- pkg/image/build.go | 79 ++++++++++++++++++++++++++--------------- pkg/image/build_test.go | 65 +++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+), 29 deletions(-) create mode 100644 pkg/image/build_test.go diff --git a/pkg/image/build.go b/pkg/image/build.go index ab6bf8bc0..57723bb5a 100644 --- a/pkg/image/build.go +++ b/pkg/image/build.go @@ -2,11 +2,14 @@ package image import ( "bytes" + "context" "encoding/json" + "errors" "fmt" "os" "os/exec" - "path" + "strings" + "time" "github.com/getkin/kin-openapi/openapi3" "github.com/google/go-containerregistry/pkg/name" @@ -25,6 +28,8 @@ const weightsManifestPath = ".cog/cache/weights_manifest.json" const bundledSchemaFile = ".cog/openapi_schema.json" const bundledSchemaPy = ".cog/schema.py" +var errGit = errors.New("git error") + // Build a Cog model from a config // // This is separated out from docker.Build(), so that can be as close as possible to the behavior of 'docker build'. @@ -206,18 +211,16 @@ func Build(cfg *config.Config, dir, imageName string, secrets []string, noCache, labels[global.LabelNamespace+"cog-base-image-last-layer-idx"] = fmt.Sprintf("%d", lastLayerIndex) } - if isGitRepo(dir) { - if commit, err := gitHead(dir); commit != "" && err == nil { - labels["org.opencontainers.image.revision"] = commit - } else { - console.Info("Unable to determine Git commit") - } + if commit, err := gitHead(dir); commit != "" && err == nil { + labels["org.opencontainers.image.revision"] = commit + } else { + console.Info("Unable to determine Git commit") + } - if tag, err := gitTag(dir); tag != "" && err == nil { - labels["org.opencontainers.image.version"] = tag - } else { - console.Info("Unable to determine Git tag") - } + if tag, err := gitTag(dir); tag != "" && err == nil { + labels["org.opencontainers.image.version"] = tag + } else { + console.Info("Unable to determine Git tag") } if err := docker.BuildAddLabelsAndSchemaToImage(imageName, labels, bundledSchemaFile, bundledSchemaPy); err != nil { @@ -257,38 +260,56 @@ func BuildBase(cfg *config.Config, dir string, useCudaBaseImage string, useCogBa return imageName, nil } -func isGitRepo(dir string) bool { - if _, err := os.Stat(path.Join(dir, ".git")); os.IsNotExist(err) { +func isGitWorkTree(dir string) bool { + ctx, cancel := context.WithTimeout(context.TODO(), 1*time.Second) + defer cancel() + + out, err := exec.CommandContext(ctx, "git", "-C", dir, "rev-parse", "--is-inside-work-tree").Output() + if err != nil { return false } - return true + return strings.TrimSpace(string(out)) == "true" } func gitHead(dir string) (string, error) { - cmd := exec.Command("git", "rev-parse", "HEAD") - cmd.Dir = dir - out, err := cmd.Output() - if err != nil { - return "", err + if v, ok := os.LookupEnv("GITHUB_SHA"); ok { + return v, nil } - commit := string(bytes.TrimSpace(out)) + if isGitWorkTree(dir) { + ctx, cancel := context.WithTimeout(context.TODO(), 1*time.Second) + defer cancel() - return commit, nil + out, err := exec.CommandContext(ctx, "git", "-C", dir, "rev-parse", "HEAD").Output() + if err != nil { + return "", err + } + + return string(bytes.TrimSpace(out)), nil + } + + return "", fmt.Errorf("Failed to find HEAD commit: %w", errGit) } func gitTag(dir string) (string, error) { - cmd := exec.Command("git", "describe", "--tags", "--dirty") - cmd.Dir = dir - out, err := cmd.Output() - if err != nil { - return "", err + if v, ok := os.LookupEnv("GITHUB_REF_NAME"); ok { + return v, nil } - tag := string(bytes.TrimSpace(out)) + if isGitWorkTree(dir) { + ctx, cancel := context.WithTimeout(context.TODO(), 1*time.Second) + defer cancel() + + out, err := exec.CommandContext(ctx, "git", "-C", dir, "describe", "--tags", "--dirty").Output() + if err != nil { + return "", err + } + + return string(bytes.TrimSpace(out)), nil + } - return tag, nil + return "", fmt.Errorf("Failed to find ref name: %w", errGit) } func buildWeightsImage(dir, dockerfileContents, imageName string, secrets []string, noCache bool, progressOutput string) error { diff --git a/pkg/image/build_test.go b/pkg/image/build_test.go new file mode 100644 index 000000000..4cac5163e --- /dev/null +++ b/pkg/image/build_test.go @@ -0,0 +1,65 @@ +package image + +import ( + "os/exec" + "testing" + + "github.com/stretchr/testify/require" +) + +func setupGitWorkTree(t *testing.T) string { + r := require.New(t) + + tmp := t.TempDir() + + r.NoError(exec.Command("git", "init", tmp).Run()) + r.NoError(exec.Command("git", "-C", tmp, "commit", "--allow-empty", "-m", "walrus").Run()) + r.NoError(exec.Command("git", "-C", tmp, "tag", "-a", "v0.0.1+walrus", "-m", "walrus time").Run()) + + return tmp +} + +func TestIsGitWorkTree(t *testing.T) { + r := require.New(t) + + r.False(isGitWorkTree("/dev/null")) + r.True(isGitWorkTree(setupGitWorkTree(t))) +} + +func TestGitHead(t *testing.T) { + r := require.New(t) + tmp := setupGitWorkTree(t) + + head, err := gitHead(tmp) + r.NoError(err) + r.NotEqual("", head) + + head, err = gitHead("/dev/null") + r.Error(err) + r.Equal("", head) + + t.Setenv("GITHUB_SHA", "fafafaf") + + head, err = gitHead("/dev/null") + r.NoError(err) + r.Equal("fafafaf", head) +} + +func TestGitTag(t *testing.T) { + r := require.New(t) + tmp := setupGitWorkTree(t) + + tag, err := gitTag(tmp) + r.NoError(err) + r.Equal("v0.0.1+walrus", tag) + + tag, err = gitTag("/dev/null") + r.Error(err) + r.Equal("", tag) + + t.Setenv("GITHUB_REF_NAME", "v0.0.1+manatee") + + tag, err = gitTag("/dev/null") + r.NoError(err) + r.Equal("v0.0.1+manatee", tag) +}