Skip to content

Commit

Permalink
add: add support for git source
Browse files Browse the repository at this point in the history
Signed-off-by: Danish Prakash <[email protected]>
  • Loading branch information
danishprakash committed Sep 9, 2024
1 parent 695a29d commit 47297b3
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 20 deletions.
68 changes: 64 additions & 4 deletions add.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (

"github.com/containers/buildah/copier"
"github.com/containers/buildah/define"
"github.com/containers/buildah/internal/tmpdir"
"github.com/containers/buildah/pkg/chrootuser"
"github.com/containers/common/pkg/retry"
"github.com/containers/image/v5/pkg/tlsclientconfig"
Expand Down Expand Up @@ -93,9 +94,14 @@ type AddAndCopyOptions struct {
RetryDelay time.Duration
}

// sourceIsGit returns true if "source" is a git location.
func sourceIsGit(source string) bool {
return strings.HasPrefix(source, "git://") || strings.Contains(source, ".git")
}

// sourceIsRemote returns true if "source" is a remote location.
func sourceIsRemote(source string) bool {
return strings.HasPrefix(source, "http://") || strings.HasPrefix(source, "https://")
return (strings.HasPrefix(source, "http://") || strings.HasPrefix(source, "https://")) && !strings.Contains(source, ".git")
}

// getURL writes a tar archive containing the named content
Expand Down Expand Up @@ -274,7 +280,7 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption
}

// Figure out what sorts of sources we have.
var localSources, remoteSources []string
var localSources, remoteSources, gitSources []string
for i, src := range sources {
if src == "" {
return errors.New("empty source location")
Expand All @@ -283,12 +289,22 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption
remoteSources = append(remoteSources, src)
continue
}
if sourceIsGit(src) {
gitSources = append(gitSources, src)
continue
}
if !filepath.IsAbs(src) && options.ContextDir == "" {
sources[i] = filepath.Join(currentDir, src)
}
localSources = append(localSources, sources[i])
}

// Treat git sources as a subset of remote sources
// differentiating only in how we fetch the two later on.
if len(gitSources) > 0 {
remoteSources = append(remoteSources, gitSources...)
}

// Check how many items our local source specs matched. Each spec
// should have matched at least one item, otherwise we consider it an
// error.
Expand Down Expand Up @@ -320,7 +336,7 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption
}
numLocalSourceItems += len(localSourceStat.Globbed)
}
if numLocalSourceItems+len(remoteSources) == 0 {
if numLocalSourceItems+len(remoteSources)+len(gitSources) == 0 {
return fmt.Errorf("no sources %v found: %w", sources, syscall.ENOENT)
}

Expand Down Expand Up @@ -377,6 +393,9 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption
destCanBeFile = true
}
}
if len(gitSources) > 0 {
destMustBeDirectory = true
}
}

// We care if the destination either doesn't exist, or exists and is a
Expand Down Expand Up @@ -448,7 +467,7 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption
var multiErr *multierror.Error
var getErr, closeErr, renameErr, putErr error
var wg sync.WaitGroup
if sourceIsRemote(src) {
if sourceIsRemote(src) || sourceIsGit(src) {
pipeReader, pipeWriter := io.Pipe()
var srcDigest digest.Digest
if options.Checksum != "" {
Expand All @@ -457,7 +476,9 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption
return fmt.Errorf("invalid checksum flag: %w", err)
}
}

wg.Add(1)

go func() {
getErr = retry.IfNecessary(context.TODO(), func() error {
return getURL(src, chownFiles, mountPoint, renameTarget, pipeWriter, chmodDirsFiles, srcDigest, options.CertPath, options.InsecureSkipTLSVerify)
Expand All @@ -468,6 +489,45 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption
pipeWriter.Close()
wg.Done()
}()

if sourceIsGit(src) {
go func() {
var cloneDir string
cloneDir, _, getErr = define.TempDirForURL(tmpdir.GetTempDir(), "", src)

renamedItems := 0
writer := io.WriteCloser(pipeWriter)
writer = newTarFilterer(writer, func(hdr *tar.Header) (bool, bool, io.Reader) {
return false, false, nil
})
getOptions := copier.GetOptions{
UIDMap: srcUIDMap,
GIDMap: srcGIDMap,
Excludes: options.Excludes,
ExpandArchives: extract,
ChownDirs: chownDirs,
ChmodDirs: chmodDirsFiles,
ChownFiles: chownFiles,
ChmodFiles: chmodDirsFiles,
StripSetuidBit: options.StripSetuidBit,
StripSetgidBit: options.StripSetgidBit,
StripStickyBit: options.StripStickyBit,
}
getErr = copier.Get(cloneDir, cloneDir, getOptions, []string{"."}, writer)
closeErr = writer.Close()
if renameTarget != "" && renamedItems > 1 {
renameErr = fmt.Errorf("internal error: renamed %d items when we expected to only rename 1", renamedItems)
}
wg.Done()
}()
} else {
go func() {
getErr = getURL(src, chownFiles, mountPoint, renameTarget, pipeWriter, chmodDirsFiles, srcDigest)
pipeWriter.Close()
wg.Done()
}()
}

wg.Add(1)
go func() {
b.ContentDigester.Start("")
Expand Down
35 changes: 19 additions & 16 deletions define/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,9 +254,16 @@ func parseGitBuildContext(url string) (string, string, string) {
return gitBranchPart[0], gitSubdir, gitBranch
}

func isGitTag(remote, ref, dir string) (bool, error) {
if _, err := exec.Command("git", "ls-remote", "--exit-code", remote, ref).Output(); err != nil {
return true, nil
}
return false, nil
}

func cloneToDirectory(url, dir string) ([]byte, string, error) {
var cmd *exec.Cmd
gitRepo, gitSubdir, gitBranch := parseGitBuildContext(url)
gitRepo, gitSubdir, gitRef := parseGitBuildContext(url)
// init repo
cmd = exec.Command("git", "init", dir)
combinedOutput, err := cmd.CombinedOutput()
Expand All @@ -270,27 +277,23 @@ func cloneToDirectory(url, dir string) ([]byte, string, error) {
if err != nil {
return combinedOutput, gitSubdir, fmt.Errorf("failed while performing `git remote add`: %w", err)
}
// fetch required branch or commit and perform checkout
// Always default to `HEAD` if nothing specified
fetch := "HEAD"
if gitBranch != "" {
fetch = gitBranch

if gitRef != "" {
if ok, _ := isGitTag(url, gitRef, dir); ok {
gitRef += ":refs/tags/" + gitRef
}
}
logrus.Debugf("fetching repo %q and branch (or commit ID) %q to %q", gitRepo, fetch, dir)
cmd = exec.Command("git", "fetch", "--depth=1", "origin", "--", fetch)

logrus.Debugf("fetching repo %q and branch (or commit ID) %q to %q", gitRepo, gitRef, dir)
args := []string{"fetch", "-u", "--depth=1", "origin", "--", gitRef}
cmd = exec.Command("git", args...)
cmd.Dir = dir
combinedOutput, err = cmd.CombinedOutput()
if err != nil {
return combinedOutput, gitSubdir, fmt.Errorf("failed while performing `git fetch`: %w", err)
}
if fetch == "HEAD" {
// We fetched default branch therefore
// we don't have any valid `branch` or
// `commit` name hence checkout detached
// `FETCH_HEAD`
fetch = "FETCH_HEAD"
}
cmd = exec.Command("git", "checkout", fetch)

cmd = exec.Command("git", "checkout", "FETCH_HEAD")
cmd.Dir = dir
combinedOutput, err = cmd.CombinedOutput()
if err != nil {
Expand Down

0 comments on commit 47297b3

Please sign in to comment.