Skip to content

Commit

Permalink
Look up git metadata via GitHub env vars when available (#1974)
Browse files Browse the repository at this point in the history
* 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)

* Skip tests that use `git` when `git` is not available

* Log output of `git` errors from tests

* Log combined stdout and stderr of `git`

* Configure git user bits

* Use correct octal literal format

* Overwrite GitHub env vars in tests

so that they work in a GitHub Actions context
  • Loading branch information
meatballhat authored Oct 3, 2024
1 parent cfa064a commit fcb3c3c
Show file tree
Hide file tree
Showing 2 changed files with 167 additions and 29 deletions.
79 changes: 50 additions & 29 deletions pkg/image/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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'.
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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(), 3*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 && v != "" {
return v, nil
}

commit := string(bytes.TrimSpace(out))
if isGitWorkTree(dir) {
ctx, cancel := context.WithTimeout(context.TODO(), 3*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 && v != "" {
return v, nil
}

tag := string(bytes.TrimSpace(out))
if isGitWorkTree(dir) {
ctx, cancel := context.WithTimeout(context.TODO(), 3*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 {
Expand Down
117 changes: 117 additions & 0 deletions pkg/image/build_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package image

import (
"context"
"os"
"os/exec"
"path/filepath"
"testing"
"time"

"github.com/stretchr/testify/require"
)

var hasGit = (func() bool {
_, err := exec.LookPath("git")
return err == nil
})()

func gitRun(argv []string, t *testing.T) {
ctx, cancel := context.WithTimeout(context.TODO(), 2*time.Second)
t.Cleanup(cancel)

out, err := exec.CommandContext(ctx, "git", argv...).CombinedOutput()
t.Logf("git output:\n%s", string(out))

require.NoError(t, err)
}

func setupGitWorkTree(t *testing.T) string {
if !hasGit {
t.Skip("no git executable available")
return ""
}

r := require.New(t)

tmp := filepath.Join(t.TempDir(), "wd")
r.NoError(os.MkdirAll(tmp, 0o755))

gitRun([]string{"init", tmp}, t)
gitRun([]string{"-C", tmp, "config", "user.email", "cog@localhost"}, t)
gitRun([]string{"-C", tmp, "config", "user.name", "Cog Tests"}, t)
gitRun([]string{"-C", tmp, "commit", "--allow-empty", "-m", "walrus"}, t)
gitRun([]string{"-C", tmp, "tag", "-a", "v0.0.1+walrus", "-m", "walrus time"}, t)

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) {
t.Run("via github env", func(t *testing.T) {
t.Setenv("GITHUB_SHA", "fafafaf")

head, err := gitHead("/dev/null")

require.NoError(t, err)
require.Equal(t, "fafafaf", head)
})

t.Run("via git", func(t *testing.T) {
tmp := setupGitWorkTree(t)
if tmp == "" {
return
}

t.Setenv("GITHUB_SHA", "")

head, err := gitHead(tmp)
require.NoError(t, err)
require.NotEqual(t, "", head)
})

t.Run("unavailable", func(t *testing.T) {
t.Setenv("GITHUB_SHA", "")

head, err := gitHead("/dev/null")
require.Error(t, err)
require.Equal(t, "", head)
})
}

func TestGitTag(t *testing.T) {
t.Run("via github env", func(t *testing.T) {
t.Setenv("GITHUB_REF_NAME", "v0.0.1+manatee")

tag, err := gitTag("/dev/null")
require.NoError(t, err)
require.Equal(t, "v0.0.1+manatee", tag)
})

t.Run("via git", func(t *testing.T) {
tmp := setupGitWorkTree(t)
if tmp == "" {
return
}

t.Setenv("GITHUB_REF_NAME", "")

tag, err := gitTag(tmp)
require.NoError(t, err)
require.Equal(t, "v0.0.1+walrus", tag)
})

t.Run("unavailable", func(t *testing.T) {
t.Setenv("GITHUB_REF_NAME", "")

tag, err := gitTag("/dev/null")
require.Error(t, err)
require.Equal(t, "", tag)
})
}

0 comments on commit fcb3c3c

Please sign in to comment.