Skip to content

Commit

Permalink
fix: full clone of template repos on add (#904)
Browse files Browse the repository at this point in the history
* full clone remote repos on write

* use filepath to parse path
  • Loading branch information
lkingland committed Mar 16, 2022
1 parent c60079d commit 564a34b
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 16 deletions.
7 changes: 2 additions & 5 deletions repositories_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -338,11 +338,8 @@ func TestRepositories_Remove(t *testing.T) {
// TestRepositories_URL ensures that a repository populates its URL member
// from the git repository's origin url (if it is a git repo and exists)
func TestRepositories_URL(t *testing.T) {
// FIXME: This test is temporarily disabled. See not in Repository.Write
// in short: as a side-effect of removing the double-clone, the in-memory
// repo is insufficient as it does not include a .git directory.
if true {
return
if runtime.GOOS == "windows" {
t.Skip("TODO fix this test on Windows CI") // TODO fix this
}

uri := TestRepoURI(RepositoriesTestRepo, t)
Expand Down
59 changes: 48 additions & 11 deletions repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package function

import (
"fmt"
"io/ioutil"
"net/url"
"os"
"path"
Expand Down Expand Up @@ -449,24 +450,60 @@ func (r *Repository) Runtime(name string) (runtime Runtime, err error) {
}

// Write all files in the repository to the given path.
func (r *Repository) Write(path string) error {
// NOTE: Writing internal .git directory does not work
func (r *Repository) Write(path string) (err error) {
fs := r.FS // The FS to copy

// NOTE
// We re-load in-memory git repos via a temp directory to avoid what
// appears to be a missing .git directory in the default worktree FS.
//
// A quirk of the git library's implementation is that the filesystem
// returned does not include the .git directory. This is usually not an
// issue when utilizing the repository's filesystem (for writing templates),
// but it does cause problems here (used for installing a repo locally) where
// we effectively want a full clone.
// TODO: switch to using a temp directory?

return copy(".", path, r.FS) // copy 'all' to 'dest' from 'FS'
// This missing .git dir is usually not an issue when utilizing the
// repository's filesystem (for writing templates, etc), but it does cause
// problems here where we are writing the entire repository to disk (cloning).
// We effectively want a full clone with a working tree. So here we do a
// plain clone first to a temp directory and then copy the files on disk
// using a regular file copy operation which thus includes the repo metadata.
if _, ok := r.FS.(billyFilesystem); ok {
var (
tempDir string
clone *git.Repository
wt *git.Worktree
)
if tempDir, err = ioutil.TempDir("", "func"); err != nil {
return
}
if clone, err = git.PlainClone(tempDir, false, // not bare
&git.CloneOptions{URL: r.uri, Depth: 1, Tags: git.NoTags,
RecurseSubmodules: git.NoRecurseSubmodules}); err != nil {
return
}
if wt, err = clone.Worktree(); err != nil {
return
}
fs = billyFilesystem{fs: wt.Filesystem}
}
return copy(".", path, fs)
}

// URL attempts to read the remote git origin URL of the repository. Best
// effort; returns empty string if the repository is not a git repo or the repo
// has been mutated beyond recognition on disk (ex: removing the origin remote)
func (r *Repository) URL() string {
repo, err := git.PlainOpen(r.uri)
uri := r.uri

// The default builtin repository is indicated by an empty URI.
// It has no remote URL, and without this check the current working directory
// would be checked.
if uri == "" {
return ""
}

// git.PlainOpen does not seem to
if strings.HasPrefix(uri, "file://") {
uri = filepath.FromSlash(r.uri[7:])
}

repo, err := git.PlainOpen(uri)
if err != nil {
return "" // not a git repository
}
Expand Down

0 comments on commit 564a34b

Please sign in to comment.