Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve error reporting in project clone #485

Merged
merged 2 commits into from
Jul 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions project-clone/internal/git/operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ func CheckoutReference(repo *git.Repository, project *dw.Project) error {
continue
}
if ref.Name().IsBranch() {
return checkoutRemoteBranch(repo, defaultRemoteName, ref)
return checkoutRemoteBranch(internal.GetClonePath(project), repo, defaultRemoteName, ref)
} else if ref.Name().IsTag() {
return checkoutTag(repo, defaultRemoteName, ref)
}
Expand All @@ -139,7 +139,7 @@ func CheckoutReference(repo *git.Repository, project *dw.Project) error {
return checkoutCommit(repo, hash)
}

func checkoutRemoteBranch(repo *git.Repository, remote string, branchRef *plumbing.Reference) error {
func checkoutRemoteBranch(projectPath string, repo *git.Repository, remote string, branchRef *plumbing.Reference) error {
// Implement logic of `git checkout <remote-branch-name>`:
// 1. Create tracking info in .git/config to properly track remote branch
// 2. Create local branch to match name of remote branch with hash matching remote branch
Expand Down Expand Up @@ -171,6 +171,14 @@ func checkoutRemoteBranch(repo *git.Repository, remote string, branchRef *plumbi
return fmt.Errorf("failed to checkout branch %s: %s", branchName, err)
}

// Need to also reset git repo due to how go-git handles some untracked files (https://github.com/go-git/go-git/issues/99)
// NOTE: using reset in go-git will not work in some cases, as that implementation of reset respects gitignore, so e.g.
// a .gitignored file that is checked in will never be reset.
err = shell.GitResetProject(path.Join(internal.ProjectsRoot, projectPath))
if err != nil {
return fmt.Errorf("failed to git reset: %s", err)
}

return nil
}

Expand Down
21 changes: 21 additions & 0 deletions project-clone/internal/shell/execute.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
package shell

import (
"fmt"
"log"
"os"
"os/exec"
)
Expand All @@ -30,6 +32,25 @@ func GitCloneProject(repoUrl, defaultRemoteName, destPath string) error {
return executeCommand("git", args...)
}

// GitResetProject runs `git reset --hard` in the project specified by projectPath
func GitResetProject(projectPath string) error {
currDir, err := os.Getwd()
if err != nil {
return fmt.Errorf("failed to get current working directory: %s", err)
}
defer func() {
if err := os.Chdir(currDir); err != nil {
log.Printf("failed to return to original working directory: %s", err)
}
}()

err = os.Chdir(projectPath)
if err != nil {
return fmt.Errorf("failed to move to project directory %s: %s", projectPath, err)
}
return executeCommand("git", "reset", "--hard")
}

func executeCommand(name string, args ...string) error {
cmd := exec.Command(name, args...)
cmd.Stderr = os.Stderr
Expand Down
41 changes: 39 additions & 2 deletions project-clone/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,31 @@
package main

import (
"io"
"log"
"os"
"path"

"github.com/devfile/devworkspace-operator/project-clone/internal"
"github.com/devfile/devworkspace-operator/project-clone/internal/git"
"github.com/devfile/devworkspace-operator/project-clone/internal/zip"
)

const (
logFileName = "project-clone-errors.log"
tmpLogFilePath = "/tmp/" + logFileName
)

// TODO: Handle sparse checkout
// TODO: Add support for auth
func main() {
f, err := os.Create(tmpLogFilePath)
if err != nil {
log.Printf("failed to open file %s for logging: %s", tmpLogFilePath, err)
}
mw := io.MultiWriter(os.Stdout, f)
log.SetOutput(mw)

workspace, err := internal.ReadFlattenedDevWorkspace()
if err != nil {
log.Printf("Failed to read current DevWorkspace: %s", err)
Expand All @@ -39,11 +53,34 @@ func main() {
err = zip.SetupZipProject(project)
default:
log.Printf("Project does not specify Git or Zip source")
os.Exit(1)
copyLogFileToProjectsRoot()
os.Exit(0)
}
if err != nil {
log.Printf("Encountered error while setting up project %s: %s", project.Name, err)
os.Exit(1)
copyLogFileToProjectsRoot()
os.Exit(0)
}
}
}

// copyLogFileToProjectsRoot copies the predefined log file into a persistent directory ($PROJECTS_ROOT)
// so that issues in setting up a devfile's projects are persisted beyond workspace restarts. Note that
// not all output from the project clone container is propagated to the log file. For example, the progress
// in cloning a project using the `git` binary only appears in stdout/stderr.
func copyLogFileToProjectsRoot() {
infile, err := os.Open(tmpLogFilePath)
if err != nil {
log.Printf("Failed to open log file: %s", err)
}
defer infile.Close()
outfile, err := os.Create(path.Join(internal.ProjectsRoot, logFileName))
if err != nil {
log.Printf("Failed to create log file: %s", err)
}
defer outfile.Close()

if _, err := io.Copy(outfile, infile); err != nil {
log.Printf("Failed to copy log file to $PROJECTS_ROOT: %s", err)
}
}