cmd/cloudshell_open/clone.go (80 lines of code) (raw):
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"fmt"
"net"
"os/exec"
"regexp"
"strings"
)
var (
repoPattern = regexp.MustCompile(`^(https://)[a-zA-Z0-9/._:-]*$`)
)
func validRepoURL(repo string) bool { return repoPattern.MatchString(repo) }
func handleRepo(repo string) (string, error) {
if !validRepoURL(repo) {
return "", fmt.Errorf("invalid git repo url: %s", repo)
}
dir, err := repoDirName(repo)
if err != nil {
return "", err
}
if ok, err := hasSubDirsInPATH(dir); err != nil {
return "", fmt.Errorf("failed to determine if clone dir has subdirectories in PATH: %v", err)
} else if ok {
return "", fmt.Errorf("cloning git repo to %s could potentially add executable files to PATH", dir)
}
return dir, clone(repo, dir)
}
func repoDirName(repo string) (string, error) {
repo = strings.TrimSuffix(repo, ".git")
i := strings.LastIndex(repo, "/")
if i == -1 {
return "", fmt.Errorf("cannot infer directory name from repo %s", repo)
}
dir := repo[i+1:]
if dir == "" {
return "", fmt.Errorf("cannot parse directory name from repo %s", repo)
}
if strings.HasPrefix(dir, ".") {
return "", fmt.Errorf("attempt to clone into hidden directory: %s", dir)
}
return dir, nil
}
func clone(gitRepo, dir string) error {
cmd := exec.Command("git", "clone", "--", gitRepo, dir)
b, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("git clone failed: %+v, output:\n%s", err, string(b))
}
return nil
}
func gitCheckout(dir, rev string) error {
cmd := exec.Command("git", "checkout", "-q", "-f", rev)
cmd.Dir = dir
b, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("git checkout failed: %+v, output:\n%s", err, string(b))
}
return nil
}
// signalRepoCloneStatus signals to the cloudshell host that the repo is
// cloned or not (bug/178009327).
func signalRepoCloneStatus(success bool) error {
c, err := net.Dial("tcp", net.JoinHostPort("localhost", "8998"))
if err != nil {
return fmt.Errorf("failed to connect to cloudshell host: %w", err)
}
msgFmt := `[null,null,null,[null,null,null,null,[%d]]]`
var msg string
if success {
msg = fmt.Sprintf(msgFmt, 1)
} else {
msg = fmt.Sprintf(msgFmt, 0)
}
msg = fmt.Sprintf("%d\n%s", len(msg), msg)
if _, err := c.Write([]byte(msg)); err != nil {
return fmt.Errorf("failed to send data to cloudshell host: %w", nil)
}
if err := c.Close(); err != nil {
return fmt.Errorf("failed to close conn to cloudshell host: %w", nil)
}
return nil
}