Ejemplo n.º 1
0
// hostCredentials returns credentials for the given Gerrit host. The
// function uses best effort to scan common locations where the
// credentials could exist.
func hostCredentials(seq runutil.Sequence, hostUrl *url.URL) (_ *credentials, e error) {
	// Look for the host credentials in the .netrc file.
	netrcPath := filepath.Join(os.Getenv("HOME"), ".netrc")
	file, err := seq.Open(netrcPath)
	if err != nil {
		if !runutil.IsNotExist(err) {
			return nil, err
		}
	} else {
		defer collect.Error(func() error { return file.Close() }, &e)
		credsMap, err := parseNetrcFile(file)
		if err != nil {
			return nil, err
		}
		creds, ok := credsMap[hostUrl.Host]
		if ok {
			return creds, nil
		}
	}

	// Look for the host credentials in the git cookie file.
	args := []string{"config", "--get", "http.cookiefile"}
	var stdout, stderr bytes.Buffer
	if err := seq.Capture(&stdout, &stderr).Last("git", args...); err == nil {
		cookieFilePath := strings.TrimSpace(stdout.String())
		file, err := seq.Open(cookieFilePath)
		if err != nil {
			if !runutil.IsNotExist(err) {
				return nil, err
			}
		} else {
			defer collect.Error(func() error { return file.Close() }, &e)
			credsMap, err := parseGitCookieFile(file)
			if err != nil {
				return nil, err
			}
			creds, ok := credsMap[hostUrl.Host]
			if ok {
				return creds, nil
			}
			// Account for site-wide credentials. Namely, the git cookie
			// file can contain credentials of the form ".<name>", which
			// should match any host "*.<name>".
			for host, creds := range credsMap {
				if strings.HasPrefix(host, ".") && strings.HasSuffix(hostUrl.Host, host) {
					return creds, nil
				}
			}
		}
	}

	return nil, fmt.Errorf("cannot find credentials for %q", hostUrl.String())
}
Ejemplo n.º 2
0
func runRebuild(jirix *jiri.X, args []string) (e error) {
	projects, tools, err := project.LoadManifest(jirix)
	if err != nil {
		return err
	}

	// Create a temporary directory in which tools will be built.
	tmpDir, err := jirix.NewSeq().TempDir("", "tmp-jiri-rebuild")
	if err != nil {
		return fmt.Errorf("TempDir() failed: %v", err)
	}

	// Make sure we cleanup the temp directory.
	defer collect.Error(func() error { return jirix.NewSeq().RemoveAll(tmpDir).Done() }, &e)

	// Paranoid sanity checking.
	if _, ok := tools[project.JiriName]; !ok {
		return fmt.Errorf("tool %q not found", project.JiriName)
	}

	// Build and install tools.
	if err := project.BuildTools(jirix, projects, tools, tmpDir); err != nil {
		return err
	}
	return project.InstallTools(jirix, tmpDir)
}
Ejemplo n.º 3
0
// commitAndPushChanges commits changes identified by the given manifest file
// and label to the containing repository and pushes these changes to the
// remote repository.
func commitAndPushChanges(jirix *jiri.X, snapshotDir, snapshotFile, label string) (e error) {
	cwd, err := os.Getwd()
	if err != nil {
		return err
	}
	defer collect.Error(func() error { return jirix.NewSeq().Chdir(cwd).Done() }, &e)
	if err := jirix.NewSeq().Chdir(snapshotDir).Done(); err != nil {
		return err
	}
	relativeSnapshotPath := strings.TrimPrefix(snapshotFile, snapshotDir+string(os.PathSeparator))
	git := gitutil.New(jirix.NewSeq())
	// Pull from master so we are up-to-date.
	if err := git.Pull("origin", "master"); err != nil {
		return err
	}
	if err := git.Add(relativeSnapshotPath); err != nil {
		return err
	}
	if err := git.Add(label); err != nil {
		return err
	}
	name := strings.TrimPrefix(snapshotFile, snapshotDir)
	if err := git.CommitNoVerify(fmt.Sprintf("adding snapshot %q for label %q", name, label)); err != nil {
		return err
	}
	if err := git.Push("origin", "master", gitutil.VerifyOpt(false)); err != nil {
		return err
	}
	return nil
}
Ejemplo n.º 4
0
// writeMetadata stores the given project metadata in the directory
// identified by the given path.
func writeMetadata(ctx *tool.Context, project Project, dir string) (e error) {
	metadataDir := filepath.Join(dir, metadataDirName)
	cwd, err := os.Getwd()
	if err != nil {
		return err
	}
	defer collect.Error(func() error { return ctx.Run().Chdir(cwd) }, &e)
	if err := ctx.Run().MkdirAll(metadataDir, os.FileMode(0755)); err != nil {
		return err
	}
	if err := ctx.Run().Chdir(metadataDir); err != nil {
		return err
	}
	// Replace absolute project paths with relative paths to make it
	// possible to move the $JIRI_ROOT directory locally.
	relPath, err := ToRel(project.Path)
	if err != nil {
		return err
	}
	project.Path = relPath
	bytes, err := xml.Marshal(project)
	if err != nil {
		return fmt.Errorf("Marhsal() failed: %v", err)
	}
	metadataFile := filepath.Join(metadataDir, metadataFileName)
	tmpMetadataFile := metadataFile + ".tmp"
	if err := ctx.Run().WriteFile(tmpMetadataFile, bytes, os.FileMode(0644)); err != nil {
		return err
	}
	if err := ctx.Run().Rename(tmpMetadataFile, metadataFile); err != nil {
		return err
	}
	return nil
}
Ejemplo n.º 5
0
// invoke invokes the Jenkins API using the given suffix, values and
// HTTP method.
func (j *Jenkins) invoke(method, suffix string, values url.Values) (_ []byte, err error) {
	// Return mock result in test mode.
	if j.testMode {
		return j.invokeMockResults[suffix], nil
	}

	apiURL, err := url.Parse(j.host)
	if err != nil {
		return nil, fmt.Errorf("Parse(%q) failed: %v", j.host, err)
	}
	apiURL.Path = fmt.Sprintf("%s/%s", apiURL.Path, suffix)
	apiURL.RawQuery = values.Encode()
	var body io.Reader
	url, body := apiURL.String(), nil
	req, err := http.NewRequest(method, url, body)
	if err != nil {
		return nil, fmt.Errorf("NewRequest(%q, %q, %v) failed: %v", method, url, body, err)
	}
	req.Header.Add("Accept", "application/json")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("Do(%v) failed: %v", req, err)
	}
	defer collect.Error(func() error { return res.Body.Close() }, &err)
	bytes, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}
	// queue/cancelItem API returns 404 even successful.
	// See: https://issues.jenkins-ci.org/browse/JENKINS-21311.
	if suffix != "queue/cancelItem" && res.StatusCode >= http.StatusBadRequest {
		return nil, fmt.Errorf("HTTP request %q returned %d:\n%s", url, res.StatusCode, string(bytes))
	}
	return bytes, nil
}
Ejemplo n.º 6
0
// setProjectRevisions sets the current project revision from the master for
// each project as found on the filesystem
func setProjectRevisions(ctx *tool.Context, projects Projects) (_ Projects, e error) {
	cwd, err := os.Getwd()
	if err != nil {
		return nil, err
	}
	defer collect.Error(func() error { return ctx.Run().Chdir(cwd) }, &e)

	for name, project := range projects {
		switch project.Protocol {
		case "git":
			if err := ctx.Run().Chdir(project.Path); err != nil {
				return nil, err
			}
			revision, err := ctx.Git().CurrentRevisionOfBranch("master")
			if err != nil {
				return nil, err
			}
			project.Revision = revision
		default:
			return nil, UnsupportedProtocolErr(project.Protocol)
		}
		projects[name] = project
	}
	return projects, nil
}
Ejemplo n.º 7
0
// SetTopic sets the topic of the given Gerrit reference.
func (g *Gerrit) SetTopic(cl string, opts CLOpts) (e error) {
	cred, err := hostCredentials(g.r, g.host)
	if err != nil {
		return err
	}
	topic := Topic{opts.Topic}
	data, err := json.Marshal(topic)
	if err != nil {
		return fmt.Errorf("Marshal(%#v) failed: %v", topic, err)
	}

	url := fmt.Sprintf("%s/a/changes/%s/topic", g.host, cl)
	method, body := "PUT", bytes.NewReader(data)
	req, err := http.NewRequest(method, url, body)
	if err != nil {
		return fmt.Errorf("NewRequest(%q, %q, %v) failed: %v", method, url, body, err)
	}
	req.Header.Add("Content-Type", "application/json;charset=UTF-8")
	req.SetBasicAuth(cred.username, cred.password)
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return fmt.Errorf("Do(%v) failed: %v", req, err)
	}
	if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusNoContent {
		return fmt.Errorf("SetTopic:Do(%v) failed: %v", req, res.StatusCode)
	}
	defer collect.Error(func() error { return res.Body.Close() }, &e)

	return nil
}
Ejemplo n.º 8
0
// UpdateUniverse updates all local projects and tools to match the
// remote counterparts identified by the given manifest. Optionally,
// the 'gc' flag can be used to indicate that local projects that no
// longer exist remotely should be removed.
func UpdateUniverse(ctx *tool.Context, gc bool) (e error) {
	ctx.TimerPush("update universe")
	defer ctx.TimerPop()
	_, remoteProjects, remoteTools, remoteHooks, err := readManifest(ctx, true)
	if err != nil {
		return err
	}
	// 1. Update all local projects to match their remote counterparts.
	if err := updateProjects(ctx, remoteProjects, gc); err != nil {
		return err
	}
	// 2. Build all tools in a temporary directory.
	tmpDir, err := ctx.Run().TempDir("", "tmp-jiri-tools-build")
	if err != nil {
		return fmt.Errorf("TempDir() failed: %v", err)
	}
	defer collect.Error(func() error { return ctx.Run().RemoveAll(tmpDir) }, &e)
	if err := buildToolsFromMaster(ctx, remoteTools, tmpDir); err != nil {
		return err
	}
	// 3. Install the tools into $JIRI_ROOT/devtools/bin.
	if err := InstallTools(ctx, tmpDir); err != nil {
		return err
	}
	// 4. Run all specified hooks
	return runHooks(ctx, remoteHooks)
}
Ejemplo n.º 9
0
// Query returns a list of QueryResult entries matched by the given
// Gerrit query string from the given Gerrit instance. The result is
// sorted by the last update time, most recently updated to oldest
// updated.
//
// See the following links for more details about Gerrit search syntax:
// - https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#list-changes
// - https://gerrit-review.googlesource.com/Documentation/user-search.html
func (g *Gerrit) Query(query string) (_ []Change, e error) {
	cred, err := hostCredentials(g.r, g.host)
	if err != nil {
		return nil, err
	}

	url := fmt.Sprintf("%s/a/changes/?o=CURRENT_REVISION&o=CURRENT_COMMIT&o=LABELS&o=DETAILED_ACCOUNTS&q=%s", g.host, url.QueryEscape(query))
	var body io.Reader
	method, body := "GET", nil
	req, err := http.NewRequest(method, url, body)
	if err != nil {
		return nil, fmt.Errorf("NewRequest(%q, %q, %v) failed: %v", method, url, body, err)
	}
	req.Header.Add("Accept", "application/json")
	req.SetBasicAuth(cred.username, cred.password)

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("Do(%v) failed: %v", req, err)
	}
	if res.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("Query:Do(%v) failed: %v", req, res.StatusCode)
	}
	defer collect.Error(func() error { return res.Body.Close() }, &e)
	return parseQueryResults(res.Body)
}
Ejemplo n.º 10
0
// revisionChanges commits changes identified by the given manifest
// file and label to the manifest repository and (if applicable)
// pushes these changes to the remote repository.
func revisionChanges(ctx *tool.Context, snapshotDir, snapshotFile, label string) (e error) {
	cwd, err := os.Getwd()
	if err != nil {
		return err
	}
	defer collect.Error(func() error { return ctx.Run().Chdir(cwd) }, &e)
	if err := ctx.Run().Chdir(snapshotDir); err != nil {
		return err
	}
	relativeSnapshotPath := strings.TrimPrefix(snapshotFile, snapshotDir+string(os.PathSeparator))
	if err := ctx.Git().Add(relativeSnapshotPath); err != nil {
		return err
	}
	if err := ctx.Git().Add(label); err != nil {
		return err
	}
	name := strings.TrimPrefix(snapshotFile, snapshotDir)
	if err := ctx.Git().CommitWithMessage(fmt.Sprintf("adding snapshot %q for label %q", name, label)); err != nil {
		return err
	}
	if remoteFlag {
		if err := ctx.Git().Push("origin", "master", gitutil.VerifyOpt(false)); err != nil {
			return err
		}
	}
	return nil
}
Ejemplo n.º 11
0
// reportNonMaster checks if the given project is on master branch and
// if not, reports this fact along with information on how to update it.
func reportNonMaster(ctx *tool.Context, project Project) (e error) {
	cwd, err := os.Getwd()
	if err != nil {
		return err
	}
	defer collect.Error(func() error { return ctx.Run().Chdir(cwd) }, &e)
	if err := ctx.Run().Chdir(project.Path); err != nil {
		return err
	}
	switch project.Protocol {
	case "git":
		current, err := ctx.Git().CurrentBranchName()
		if err != nil {
			return err
		}
		if current != "master" {
			line1 := fmt.Sprintf(`NOTE: "jiri update" only updates the "master" branch and the current branch is %q`, current)
			line2 := fmt.Sprintf(`to update the %q branch once the master branch is updated, run "git merge master"`, current)
			opts := runutil.Opts{Verbose: true}
			ctx.Run().OutputWithOpts(opts, []string{line1, line2})
		}
		return nil
	default:
		return UnsupportedProtocolErr(project.Protocol)
	}
}
Ejemplo n.º 12
0
func runRebuild(env *cmdline.Env, args []string) (e error) {
	ctx := tool.NewContextFromEnv(env)
	_, tools, err := project.ReadManifest(ctx)
	if err != nil {
		return err
	}

	// Create a temporary directory in which tools will be built.
	tmpDir, err := ctx.Run().TempDir("", "tmp-jiri-rebuild")
	if err != nil {
		return fmt.Errorf("TempDir() failed: %v", err)
	}

	// Make sure we cleanup the temp directory.
	defer collect.Error(func() error { return ctx.Run().RemoveAll(tmpDir) }, &e)

	// Paranoid sanity checking.
	if _, ok := tools[project.JiriName]; !ok {
		return fmt.Errorf("tool %q not found", project.JiriName)
	}

	// Build and install tools.
	if err := project.BuildTools(ctx, tools, tmpDir); err != nil {
		return err
	}
	return project.InstallTools(ctx, tmpDir)
}
Ejemplo n.º 13
0
// ApplyToLocalMaster applies an operation expressed as the given function to
// the local master branch of the given projects.
func ApplyToLocalMaster(ctx *tool.Context, projects Projects, fn func() error) (e error) {
	cwd, err := os.Getwd()
	if err != nil {
		return err
	}
	defer collect.Error(func() error { return ctx.Run().Chdir(cwd) }, &e)

	// Loop through all projects, checking out master and stashing any unstaged
	// changes.
	for _, project := range projects {
		p := project
		if err := ctx.Run().Chdir(p.Path); err != nil {
			return err
		}
		switch p.Protocol {
		case "git":
			branch, err := ctx.Git().CurrentBranchName()
			if err != nil {
				return err
			}
			stashed, err := ctx.Git().Stash()
			if err != nil {
				return err
			}
			if err := ctx.Git().CheckoutBranch("master"); err != nil {
				return err
			}
			// After running the function, return to this project's directory,
			// checkout the original branch, and stash pop if necessary.
			defer collect.Error(func() error {
				if err := ctx.Run().Chdir(p.Path); err != nil {
					return err
				}
				if err := ctx.Git().CheckoutBranch(branch); err != nil {
					return err
				}
				if stashed {
					return ctx.Git().StashPop()
				}
				return nil
			}, &e)
		default:
			return UnsupportedProtocolErr(p.Protocol)
		}
	}
	return fn()
}
Ejemplo n.º 14
0
func listCommitters(ctx *tool.Context) (_ []string, e error) {
	branch, err := ctx.Git().CurrentBranchName()
	if err != nil {
		return nil, err
	}
	stashed, err := ctx.Git().Stash()
	if err != nil {
		return nil, err
	}
	if stashed {
		defer collect.Error(func() error { return ctx.Git().StashPop() }, &e)
	}
	if err := ctx.Git().CheckoutBranch("master"); err != nil {
		return nil, err
	}
	defer collect.Error(func() error { return ctx.Git().CheckoutBranch(branch) }, &e)
	return ctx.Git().Committers()
}
Ejemplo n.º 15
0
// Submit submits the given changelist through Gerrit.
func (g *Gerrit) Submit(changeID string) (e error) {
	cred, err := hostCredentials(g.r, g.host)
	if err != nil {
		return err
	}

	// Encode data needed for Submit.
	data := struct {
		WaitForMerge bool `json:"wait_for_merge"`
	}{
		WaitForMerge: true,
	}
	encodedBytes, err := json.Marshal(data)
	if err != nil {
		return fmt.Errorf("Marshal(%#v) failed: %v", data, err)
	}

	// Call Submit API.
	// https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#submit-change
	url := fmt.Sprintf("%s/a/changes/%s/submit", g.host, changeID)
	var body io.Reader
	method, body := "POST", bytes.NewReader(encodedBytes)
	req, err := http.NewRequest(method, url, body)
	if err != nil {
		return fmt.Errorf("NewRequest(%q, %q, %v) failed: %v", method, url, body, err)
	}
	req.Header.Add("Content-Type", "application/json;charset=UTF-8")
	req.SetBasicAuth(cred.username, cred.password)

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return fmt.Errorf("Do(%v) failed: %v", req, err)
	}
	if res.StatusCode != http.StatusOK {
		return fmt.Errorf("Submit:Do(%v) failed: %v", req, res.StatusCode)
	}
	defer collect.Error(func() error { return res.Body.Close() }, &e)

	// Check response.
	bytes, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return err
	}
	resContent := string(bytes)
	// For a "TBR" CL, the response code is not 200 but the submit will still succeed.
	// In those cases, the "error" message will be "change is new".
	// We don't treat this case as error.
	if res.StatusCode != http.StatusOK && strings.TrimSpace(resContent) != "change is new" {
		return fmt.Errorf("Failed to submit CL %q:\n%s", changeID, resContent)
	}

	return nil
}
Ejemplo n.º 16
0
func cleanupCL(jirix *jiri.X, branches []string) (e error) {
	git := gitutil.New(jirix.NewSeq())
	originalBranch, err := git.CurrentBranchName()
	if err != nil {
		return err
	}
	stashed, err := git.Stash()
	if err != nil {
		return err
	}
	if stashed {
		defer collect.Error(func() error { return git.StashPop() }, &e)
	}
	if err := git.CheckoutBranch(remoteBranchFlag); err != nil {
		return err
	}
	checkoutOriginalBranch := true
	defer collect.Error(func() error {
		if checkoutOriginalBranch {
			return git.CheckoutBranch(originalBranch)
		}
		return nil
	}, &e)
	if err := git.FetchRefspec("origin", remoteBranchFlag); err != nil {
		return err
	}
	s := jirix.NewSeq()
	for _, branch := range branches {
		cleanupFn := func() error { return cleanupBranch(jirix, branch) }
		if err := s.Call(cleanupFn, "Cleaning up branch: %s", branch).Done(); err != nil {
			return err
		}
		if branch == originalBranch {
			checkoutOriginalBranch = false
		}
	}
	return nil
}
Ejemplo n.º 17
0
Archivo: cl.go Proyecto: 4shome/go.jiri
func cleanupCL(ctx *tool.Context, branches []string) (e error) {
	originalBranch, err := ctx.Git().CurrentBranchName()
	if err != nil {
		return err
	}
	stashed, err := ctx.Git().Stash()
	if err != nil {
		return err
	}
	if stashed {
		defer collect.Error(func() error { return ctx.Git().StashPop() }, &e)
	}
	if err := ctx.Git().CheckoutBranch(remoteBranchFlag); err != nil {
		return err
	}
	checkoutOriginalBranch := true
	defer collect.Error(func() error {
		if checkoutOriginalBranch {
			return ctx.Git().CheckoutBranch(originalBranch)
		}
		return nil
	}, &e)
	if err := ctx.Git().FetchRefspec("origin", remoteBranchFlag); err != nil {
		return err
	}
	for _, branch := range branches {
		cleanupFn := func() error { return cleanupBranch(ctx, branch) }
		if err := ctx.Run().Function(cleanupFn, "Cleaning up branch %q", branch); err != nil {
			return err
		}
		if branch == originalBranch {
			checkoutOriginalBranch = false
		}
	}
	return nil
}
Ejemplo n.º 18
0
// CleanupProjects restores the given jiri projects back to their master
// branches and gets rid of all the local changes. If "cleanupBranches" is
// true, it will also delete all the non-master branches.
func CleanupProjects(ctx *tool.Context, projects Projects, cleanupBranches bool) (e error) {
	wd, err := os.Getwd()
	if err != nil {
		return fmt.Errorf("Getwd() failed: %v", err)
	}
	defer collect.Error(func() error { return ctx.Run().Chdir(wd) }, &e)
	for _, project := range projects {
		localProjectDir := project.Path
		if err := ctx.Run().Chdir(localProjectDir); err != nil {
			return err
		}
		if err := resetLocalProject(ctx, cleanupBranches, project.RemoteBranch); err != nil {
			return err
		}
	}
	return nil
}
Ejemplo n.º 19
0
// PostReview posts a review to the given Gerrit reference.
func (g *Gerrit) PostReview(ref string, message string, labels map[string]string) (e error) {
	cred, err := hostCredentials(g.r, g.host)
	if err != nil {
		return err
	}

	review := Review{
		Message: message,
		Labels:  labels,
	}

	// Encode "review" as JSON.
	encodedBytes, err := json.Marshal(review)
	if err != nil {
		return fmt.Errorf("Marshal(%#v) failed: %v", review, err)
	}

	// Construct API URL.
	// ref is in the form of "refs/changes/<last two digits of change number>/<change number>/<patch set number>".
	parts := strings.Split(ref, "/")
	if expected, got := 5, len(parts); expected != got {
		return fmt.Errorf("unexpected number of %q parts: expected %v, got %v", ref, expected, got)
	}
	cl, revision := parts[3], parts[4]
	url := fmt.Sprintf("%s/a/changes/%s/revisions/%s/review", g.host, cl, revision)

	// Post the review.
	method, body := "POST", bytes.NewReader(encodedBytes)
	req, err := http.NewRequest(method, url, body)
	if err != nil {
		return fmt.Errorf("NewRequest(%q, %q, %v) failed: %v", method, url, body, err)
	}
	req.Header.Add("Content-Type", "application/json;charset=UTF-8")
	req.SetBasicAuth(cred.username, cred.password)
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return fmt.Errorf("Do(%v) failed: %v", req, err)
	}
	if res.StatusCode != http.StatusOK {
		return fmt.Errorf("PostReview:Do(%v) failed: %v", req, res.StatusCode)
	}
	defer collect.Error(func() error { return res.Body.Close() }, &e)

	return nil
}
Ejemplo n.º 20
0
// checkSnapshotDir makes sure that he local snapshot directory exists
// and is initialized properly.
func checkSnapshotDir(ctx *tool.Context) (e error) {
	snapshotDir, err := getSnapshotDir()
	if err != nil {
		return err
	}
	if _, err := ctx.Run().Stat(snapshotDir); err != nil {
		if !os.IsNotExist(err) {
			return err
		}
		if remoteFlag {
			if err := ctx.Run().MkdirAll(snapshotDir, 0755); err != nil {
				return err
			}
			return nil
		}
		createFn := func() (err error) {
			if err := ctx.Run().MkdirAll(snapshotDir, 0755); err != nil {
				return err
			}
			if err := ctx.Git().Init(snapshotDir); err != nil {
				return err
			}
			cwd, err := os.Getwd()
			if err != nil {
				return err
			}
			defer collect.Error(func() error { return ctx.Run().Chdir(cwd) }, &e)
			if err := ctx.Run().Chdir(snapshotDir); err != nil {
				return err
			}
			if err := ctx.Git().Commit(); err != nil {
				return err
			}
			return nil
		}
		if err := createFn(); err != nil {
			ctx.Run().RemoveAll(snapshotDir)
			return err
		}
	}
	return nil
}
Ejemplo n.º 21
0
// GitCloneRepo clones a repo at a specific revision in outDir.
func GitCloneRepo(ctx *tool.Context, remote, revision, outDir string, outDirPerm os.FileMode) (e error) {
	cwd, err := os.Getwd()
	if err != nil {
		return err
	}
	defer collect.Error(func() error { return ctx.Run().Chdir(cwd) }, &e)

	if err := ctx.Run().MkdirAll(outDir, outDirPerm); err != nil {
		return err
	}
	if err := ctx.Git().Clone(remote, outDir); err != nil {
		return err
	}
	if err := ctx.Run().Chdir(outDir); err != nil {
		return err
	}
	if err := ctx.Git().Reset(revision); err != nil {
		return err
	}
	return nil
}
Ejemplo n.º 22
0
// WriteLog writes the given list of CLs to a log file, as a json-encoded
// map of ref strings => CLs.
func WriteLog(logFilePath string, cls CLList) (e error) {
	// Index CLs with their refs.
	results := CLRefMap{}
	for _, cl := range cls {
		results[cl.Reference()] = cl
	}

	fd, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666)
	if err != nil {
		return fmt.Errorf("OpenFile(%q) failed: %v", logFilePath, err)
	}
	defer collect.Error(func() error { return fd.Close() }, &e)

	bytes, err := json.MarshalIndent(results, "", "  ")
	if err != nil {
		return fmt.Errorf("MarshalIndent(%v) failed: %v", results, err)
	}

	if err := ioutil.WriteFile(logFilePath, bytes, os.FileMode(0644)); err != nil {
		return fmt.Errorf("WriteFile(%q) failed: %v", logFilePath, err)
	}
	return nil
}
Ejemplo n.º 23
0
// PollProjects returns the set of changelists that exist remotely but not
// locally. Changes are grouped by projects and contain author identification
// and a description of their content.
func PollProjects(ctx *tool.Context, projectSet map[string]struct{}) (_ Update, e error) {
	ctx.TimerPush("poll projects")
	defer ctx.TimerPop()

	// Switch back to current working directory when we're done.
	cwd, err := os.Getwd()
	if err != nil {
		return nil, err
	}
	defer collect.Error(func() error { return ctx.Run().Chdir(cwd) }, &e)

	// Gather local & remote project data.
	localProjects, err := LocalProjects(ctx, FastScan)
	if err != nil {
		return nil, err
	}
	_, remoteProjects, _, _, err := readManifest(ctx, false)
	if err != nil {
		return nil, err
	}

	// Compute difference between local and remote.
	update := Update{}
	ops, err := computeOperations(localProjects, remoteProjects, false)
	if err != nil {
		return nil, err
	}

	for _, op := range ops {
		name := op.Project().Name

		// If given a project set, limit our results to those projects in the set.
		if len(projectSet) > 0 {
			if _, ok := projectSet[name]; !ok {
				continue
			}
		}

		// We only inspect this project if an update operation is required.
		cls := []CL{}
		if updateOp, ok := op.(updateOperation); ok {
			switch updateOp.project.Protocol {
			case "git":

				// Enter project directory - this assumes absolute paths.
				if err := ctx.Run().Chdir(updateOp.destination); err != nil {
					return nil, err
				}

				// Fetch the latest from remote.
				if err := ctx.Git().FetchRefspec("origin", updateOp.project.RemoteBranch); err != nil {
					return nil, err
				}

				// Collect commits visible from FETCH_HEAD that aren't visible from master.
				commitsText, err := ctx.Git().Log("FETCH_HEAD", "master", "%an%n%ae%n%B")
				if err != nil {
					return nil, err
				}

				// Format those commits and add them to the results.
				for _, commitText := range commitsText {
					if got, want := len(commitText), 3; got < want {
						return nil, fmt.Errorf("Unexpected length of %v: got %v, want at least %v", commitText, got, want)
					}
					cls = append(cls, CL{
						Author:      commitText[0],
						Email:       commitText[1],
						Description: strings.Join(commitText[2:], "\n"),
					})
				}
			default:
				return nil, UnsupportedProtocolErr(updateOp.project.Protocol)
			}
		}
		update[name] = cls
	}
	return update, nil
}
Ejemplo n.º 24
0
// hostCredentials returns credentials for the given Gerrit host. The
// function uses best effort to scan common locations where the
// credentials could exist.
func hostCredentials(run *runutil.Run, host string) (_ *credentials, e error) {
	// Check the host URL is valid.
	url, err := url.Parse(host)
	if err != nil {
		return nil, fmt.Errorf("Parse(%q) failed: %v", host, err)
	}
	if url.Host == "" {
		return nil, fmt.Errorf("%q has no host", host)
	}

	// Look for the host credentials in the .netrc file.
	netrcPath := filepath.Join(os.Getenv("HOME"), ".netrc")
	file, err := run.Open(netrcPath)
	if err != nil {
		if !os.IsNotExist(err) {
			return nil, err
		}
	} else {
		defer collect.Error(func() error { return file.Close() }, &e)
		credsMap, err := parseNetrcFile(file)
		if err != nil {
			return nil, err
		}
		creds, ok := credsMap[url.Host]
		if ok {
			return creds, nil
		}
	}

	// Look for the host credentials in the git cookie file.
	args := []string{"config", "--get", "http.cookiefile"}
	var stdout, stderr bytes.Buffer
	opts := run.Opts()
	opts.Stdout = &stdout
	opts.Stderr = &stderr
	if err := run.CommandWithOpts(opts, "git", args...); err == nil {
		cookieFilePath := strings.TrimSpace(stdout.String())
		file, err := run.Open(cookieFilePath)
		if err != nil {
			if !os.IsNotExist(err) {
				return nil, err
			}
		} else {
			defer collect.Error(func() error { return file.Close() }, &e)
			credsMap, err := parseGitCookieFile(file)
			if err != nil {
				return nil, err
			}
			creds, ok := credsMap[url.Host]
			if ok {
				return creds, nil
			}
			// Account for site-wide credentials. Namely, the git cookie
			// file can contain credentials of the form ".<name>", which
			// should match any host "*.<name>".
			for host, creds := range credsMap {
				if strings.HasPrefix(host, ".") && strings.HasSuffix(url.Host, host) {
					return creds, nil
				}
			}
		}
	}

	return nil, fmt.Errorf("cannot find credentials for %q", host)
}
Ejemplo n.º 25
0
Archivo: cl.go Proyecto: 4shome/go.jiri
func syncCL(ctx *tool.Context) (e error) {
	stashed, err := ctx.Git().Stash()
	if err != nil {
		return err
	}
	if stashed {
		defer collect.Error(func() error { return ctx.Git().StashPop() }, &e)
	}

	// Register a cleanup handler in case of subsequent errors.
	forceOriginalBranch := true
	originalBranch, err := ctx.Git().CurrentBranchName()
	if err != nil {
		return err
	}
	originalWd, err := os.Getwd()
	if err != nil {
		return err
	}

	defer func() {
		if forceOriginalBranch {
			ctx.Git().CheckoutBranch(originalBranch, gitutil.ForceOpt(true))
		}
		ctx.Run().Chdir(originalWd)
	}()

	// Switch to an existing directory in master so we can run commands.
	topLevel, err := ctx.Git().TopLevel()
	if err != nil {
		return err
	}
	if err := ctx.Run().Chdir(topLevel); err != nil {
		return err
	}

	// Identify the dependents CLs leading to (and including) the
	// current branch.
	branches, err := getDependentCLs(ctx, originalBranch)
	if err != nil {
		return err
	}
	branches = append(branches, originalBranch)

	// Sync from upstream.
	if err := ctx.Git().CheckoutBranch(branches[0]); err != nil {
		return err
	}
	if err := ctx.Git().Pull("origin", branches[0]); err != nil {
		return err
	}

	// Bring all CLs in the sequence of dependent CLs leading to the
	// current branch up to date with the <remoteBranchFlag> branch.
	for i := 1; i < len(branches); i++ {
		if err := ctx.Git().CheckoutBranch(branches[i]); err != nil {
			return err
		}
		if err := ctx.Git().Merge(branches[i-1]); err != nil {
			return fmt.Errorf(`Failed to automatically merge branch %v into branch %v: %v
The following steps are needed before the operation can be retried:
$ git checkout %v
$ git merge %v
# resolve all conflicts
$ git commit -a
$ git checkout %v
# retry the original operation
`, branches[i], branches[i-1], err, branches[i], branches[i-1], originalBranch)
		}
	}

	forceOriginalBranch = false
	return nil
}
Ejemplo n.º 26
0
Archivo: cl.go Proyecto: 4shome/go.jiri
// createReviewBranch creates a clean review branch from the remote
// branch this CL pertains to and then iterates over the sequence of
// dependent CLs leading to the current branch, creating one commit
// per CL by squashing all commits of each individual CL. The commit
// message for all but that last CL is derived from their
// <commitMessageFileName>, while the <message> argument is used as
// the commit message for the last commit.
func (review *review) createReviewBranch(message string) (e error) {
	// Create the review branch.
	if err := review.ctx.Git().FetchRefspec("origin", review.CLOpts.RemoteBranch); err != nil {
		return err
	}
	if review.ctx.Git().BranchExists(review.reviewBranch) {
		if err := review.ctx.Git().DeleteBranch(review.reviewBranch, gitutil.ForceOpt(true)); err != nil {
			return err
		}
	}
	upstream := "origin/" + review.CLOpts.RemoteBranch
	if err := review.ctx.Git().CreateBranchWithUpstream(review.reviewBranch, upstream); err != nil {
		return err
	}
	if err := review.ctx.Git().CheckoutBranch(review.reviewBranch); err != nil {
		return err
	}
	// Register a cleanup handler in case of subsequent errors.
	cleanup := true
	defer collect.Error(func() error {
		if !cleanup {
			return review.ctx.Git().CheckoutBranch(review.CLOpts.Branch)
		}
		review.ctx.Git().CheckoutBranch(review.CLOpts.Branch, gitutil.ForceOpt(true))
		review.ctx.Git().DeleteBranch(review.reviewBranch, gitutil.ForceOpt(true))
		return nil
	}, &e)

	// Report an error if the CL is empty.
	if !review.ctx.DryRun() {
		hasDiff, err := review.ctx.Git().BranchesDiffer(review.CLOpts.Branch, review.reviewBranch)
		if err != nil {
			return err
		}
		if !hasDiff {
			return emptyChangeError(struct{}{})
		}
	}

	// If <message> is empty, replace it with the default message.
	if len(message) == 0 {
		var err error
		message, err = review.defaultCommitMessage()
		if err != nil {
			return err
		}
	}

	// Iterate over all dependent CLs leading to (and including) the
	// current branch, creating one commit in the review branch per CL
	// by squashing all commits of each individual CL.
	branches, err := getDependentCLs(review.ctx, review.CLOpts.Branch)
	if err != nil {
		return err
	}
	branches = append(branches, review.CLOpts.Branch)
	if err := review.squashBranches(branches, message); err != nil {
		return err
	}

	cleanup = false
	return nil
}
Ejemplo n.º 27
0
Archivo: cl.go Proyecto: 4shome/go.jiri
// squashBranches iterates over the given list of branches, creating
// one commit per branch in the current branch by squashing all
// commits of each individual branch.
//
// TODO(jsimsa): Consider using "git rebase --onto" to avoid having to
// deal with merge conflicts.
func (review *review) squashBranches(branches []string, message string) (e error) {
	for i := 1; i < len(branches); i++ {
		// We want to merge the <branches[i]> branch on top of the review
		// branch, forcing all conflicts to be reviewed in favor of the
		// <branches[i]> branch. Unfortunately, git merge does not offer a
		// strategy that would do that for us. The solution implemented
		// here is based on:
		//
		// http://stackoverflow.com/questions/173919/is-there-a-theirs-version-of-git-merge-s-ours
		if err := review.ctx.Git().Merge(branches[i], gitutil.SquashOpt(true), gitutil.StrategyOpt("ours")); err != nil {
			return changeConflictError{
				localBranch:  branches[i],
				remoteBranch: review.CLOpts.RemoteBranch,
				message:      err.Error(),
			}
		}
		// Fetch the timestamp of the last commit of <branches[i]> and use
		// it to create the squashed commit. This is needed to make sure
		// that the commit hash of the squashed commit stays the same as
		// long as the squashed sequence of commits does not change. If
		// this was not the case, consecutive invocations of "jiri cl mail"
		// could fail if some, but not all, of the dependent CLs submitted
		// to Gerrit have changed.
		output, err := review.ctx.Git().Log(branches[i], branches[i]+"^", "%ad%n%cd")
		if err != nil {
			return err
		}
		if len(output) < 1 || len(output[0]) < 2 {
			return fmt.Errorf("unexpected output length: %v", output)
		}
		authorDate := tool.AuthorDateOpt(output[0][0])
		committerDate := tool.CommitterDateOpt(output[0][1])
		if i < len(branches)-1 {
			file, err := getCommitMessageFileName(review.ctx, branches[i])
			if err != nil {
				return err
			}
			message, err := review.ctx.Run().ReadFile(file)
			if err != nil {
				return err
			}
			if err := review.ctx.Git(authorDate, committerDate).CommitWithMessage(string(message)); err != nil {
				return err
			}
		} else {
			committer := review.ctx.Git(authorDate, committerDate).NewCommitter(review.CLOpts.Edit)
			if err := committer.Commit(message); err != nil {
				return err
			}
		}
		tmpBranch := review.reviewBranch + "-" + branches[i] + "-TMP"
		if err := review.ctx.Git().CreateBranch(tmpBranch); err != nil {
			return err
		}
		defer collect.Error(func() error {
			return review.ctx.Git().DeleteBranch(tmpBranch, gitutil.ForceOpt(true))
		}, &e)
		if err := review.ctx.Git().Reset(branches[i]); err != nil {
			return err
		}
		if err := review.ctx.Git().Reset(tmpBranch, gitutil.ModeOpt("soft")); err != nil {
			return err
		}
		if err := review.ctx.Git(authorDate, committerDate).CommitAmend(); err != nil {
			return err
		}
	}
	return nil
}
Ejemplo n.º 28
0
Archivo: cl.go Proyecto: 4shome/go.jiri
// run implements checks that the review passes all local checks
// and then mails it to Gerrit.
func (review *review) run() (e error) {
	if uncommittedFlag {
		changes, err := review.ctx.Git().FilesWithUncommittedChanges()
		if err != nil {
			return err
		}
		if len(changes) != 0 {
			return uncommittedChangesError(changes)
		}
	}
	if review.CLOpts.Branch == remoteBranchFlag {
		return fmt.Errorf("cannot do a review from the %q branch.", remoteBranchFlag)
	}
	stashed, err := review.ctx.Git().Stash()
	if err != nil {
		return err
	}
	wd, err := os.Getwd()
	if err != nil {
		return fmt.Errorf("Getwd() failed: %v", err)
	}
	defer collect.Error(func() error { return review.ctx.Run().Chdir(wd) }, &e)
	topLevel, err := review.ctx.Git().TopLevel()
	if err != nil {
		return err
	}
	if err := review.ctx.Run().Chdir(topLevel); err != nil {
		return err
	}
	defer collect.Error(func() error { return review.cleanup(stashed) }, &e)
	file, err := getCommitMessageFileName(review.ctx, review.CLOpts.Branch)
	if err != nil {
		return err
	}
	message := messageFlag
	if message == "" {
		// Message was not passed in flag.  Attempt to read it from file.
		data, err := review.ctx.Run().ReadFile(file)
		if err != nil {
			if !os.IsNotExist(err) {
				return err
			}
		} else {
			message = string(data)
		}
	}

	// Add/remove labels to/from the commit message before asking users
	// to edit it. We do this only when this is not the initial commit
	// where the message is empty.
	//
	// For the initial commit, the labels will be processed after the
	// message is edited by users, which happens in the
	// updateReviewMessage method.
	if message != "" {
		message = review.processLabels(message)
	}
	if err := review.createReviewBranch(message); err != nil {
		return err
	}
	if err := review.updateReviewMessage(file); err != nil {
		return err
	}
	if err := review.send(); err != nil {
		return err
	}
	if setTopicFlag {
		if err := review.setTopic(); err != nil {
			return err
		}
	}
	return nil
}
Ejemplo n.º 29
0
func (op createOperation) Run(ctx *tool.Context, manifest *Manifest) (e error) {
	hosts, _, _, _, err := readManifest(ctx, false)
	if err != nil {
		return err
	}

	path, perm := filepath.Dir(op.destination), os.FileMode(0755)
	if err := ctx.Run().MkdirAll(path, perm); err != nil {
		return err
	}
	// Create a temporary directory for the initial setup of the
	// project to prevent an untimely termination from leaving the
	// $JIRI_ROOT directory in an inconsistent state.
	tmpDirPrefix := strings.Replace(op.Project().Name, "/", ".", -1) + "-"
	tmpDir, err := ctx.Run().TempDir(path, tmpDirPrefix)
	if err != nil {
		return err
	}
	defer collect.Error(func() error { return ctx.Run().RemoveAll(tmpDir) }, &e)
	switch op.project.Protocol {
	case "git":
		if err := ctx.Git().Clone(op.project.Remote, tmpDir); err != nil {
			return err
		}

		// Apply git hooks.  We're creating this repo, so there's no danger of
		// overriding existing hooks.  Customizing your git hooks with jiri is a bad
		// idea anyway, since jiri won't know to not delete the project when you
		// switch between manifests or do a cleanup.
		host, found := hosts["git"]
		if found && strings.HasPrefix(op.project.Remote, host.Location) {
			gitHookDir := filepath.Join(tmpDir, ".git", "hooks")
			for _, githook := range host.GitHooks {
				mdir, err := ManifestDir()
				if err != nil {
					return err
				}
				src, err := ctx.Run().ReadFile(filepath.Join(mdir, githook.Path))
				if err != nil {
					return err
				}
				dst := filepath.Join(gitHookDir, githook.Name)
				if err := ctx.Run().WriteFile(dst, src, perm); err != nil {
					return err
				}
			}
		}

		// Apply exclusion for /.jiri/. We're creating the repo so we can safely
		// write to .git/info/exclude
		excludeString := "/.jiri/\n"
		excludeDir := filepath.Join(tmpDir, ".git", "info")
		if err := ctx.Run().MkdirAll(excludeDir, os.FileMode(0750)); err != nil {
			return err
		}
		excludeFile := filepath.Join(excludeDir, "exclude")
		if err := ctx.Run().WriteFile(excludeFile, []byte(excludeString), perm); err != nil {
			return err
		}

		cwd, err := os.Getwd()
		if err != nil {
			return err
		}
		defer collect.Error(func() error { return ctx.Run().Chdir(cwd) }, &e)
		if err := ctx.Run().Chdir(tmpDir); err != nil {
			return err
		}
		if err := ctx.Git().Reset(op.project.Revision); err != nil {
			return err
		}
	default:
		return UnsupportedProtocolErr(op.project.Protocol)
	}
	if err := writeMetadata(ctx, op.project, tmpDir); err != nil {
		return err
	}
	if err := ctx.Run().Chmod(tmpDir, os.FileMode(0755)); err != nil {
		return err
	}
	if err := ctx.Run().Rename(tmpDir, op.destination); err != nil {
		return err
	}
	if err := resetProject(ctx, op.project); err != nil {
		return err
	}
	return addProjectToManifest(ctx, manifest, op.project)
}