Merge "v.io/jiri/project: use runutil.Sequence."
diff --git a/cl.go b/cl.go
index 9460338..7ad11d6 100644
--- a/cl.go
+++ b/cl.go
@@ -825,7 +825,7 @@
return err
}
}
- if err := gerrit.Push(review.jirix.Run(), review.CLOpts); err != nil {
+ if err := gerrit.Push(review.jirix.NewSeq(), review.CLOpts); err != nil {
return gerritError(err.Error())
}
return nil
diff --git a/contrib.go b/contrib.go
index 066850a..5c4897b 100644
--- a/contrib.go
+++ b/contrib.go
@@ -126,7 +126,7 @@
}
func runContributors(jirix *jiri.X, args []string) error {
- projects, err := project.LocalProjects(jirix, project.FastScan)
+ localProjects, err := project.LocalProjects(jirix, project.FastScan)
if err != nil {
return err
}
@@ -134,8 +134,8 @@
if len(args) != 0 {
projectNames = set.String.FromSlice(args)
} else {
- for name, _ := range projects {
- projectNames[name] = struct{}{}
+ for _, p := range localProjects {
+ projectNames[p.Name] = struct{}{}
}
}
@@ -145,41 +145,43 @@
}
contributors := map[string]*contributor{}
for name, _ := range projectNames {
- project, ok := projects[name]
- if !ok {
+ projects := localProjects.Find(name)
+ if len(projects) == 0 {
continue
}
- if err := jirix.Run().Chdir(project.Path); err != nil {
- return err
- }
- switch project.Protocol {
- case "git":
- lines, err := listCommitters(jirix)
- if err != nil {
+ for _, project := range projects {
+ if err := jirix.Run().Chdir(project.Path); err != nil {
return err
}
- for _, line := range lines {
- matches := contributorRE.FindStringSubmatch(line)
- if got, want := len(matches), 4; got != want {
- return fmt.Errorf("unexpected length of %v: got %v, want %v", matches, got, want)
- }
- count, err := strconv.Atoi(strings.TrimSpace(matches[1]))
+ switch project.Protocol {
+ case "git":
+ lines, err := listCommitters(jirix)
if err != nil {
- return fmt.Errorf("Atoi(%v) failed: %v", strings.TrimSpace(matches[1]), err)
+ return err
}
- c := &contributor{
- count: count,
- email: strings.TrimSpace(matches[3]),
- name: strings.TrimSpace(matches[2]),
- }
- if c.email == "jenkins.veyron@gmail.com" || c.email == "jenkins.veyron.rw@gmail.com" {
- continue
- }
- c.email, c.name = canonicalize(aliases, c.email, c.name)
- if existing, ok := contributors[c.name]; ok {
- existing.count += c.count
- } else {
- contributors[c.name] = c
+ for _, line := range lines {
+ matches := contributorRE.FindStringSubmatch(line)
+ if got, want := len(matches), 4; got != want {
+ return fmt.Errorf("unexpected length of %v: got %v, want %v", matches, got, want)
+ }
+ count, err := strconv.Atoi(strings.TrimSpace(matches[1]))
+ if err != nil {
+ return fmt.Errorf("Atoi(%v) failed: %v", strings.TrimSpace(matches[1]), err)
+ }
+ c := &contributor{
+ count: count,
+ email: strings.TrimSpace(matches[3]),
+ name: strings.TrimSpace(matches[2]),
+ }
+ if c.email == "jenkins.veyron@gmail.com" || c.email == "jenkins.veyron.rw@gmail.com" {
+ continue
+ }
+ c.email, c.name = canonicalize(aliases, c.email, c.name)
+ if existing, ok := contributors[c.name]; ok {
+ existing.count += c.count
+ } else {
+ contributors[c.name] = c
+ }
}
}
}
diff --git a/gerrit/.api b/gerrit/.api
new file mode 100644
index 0000000..3ab044a
--- /dev/null
+++ b/gerrit/.api
@@ -0,0 +1,63 @@
+pkg gerrit, const PresubmitTestTypeAll PresubmitTestType
+pkg gerrit, const PresubmitTestTypeNone PresubmitTestType
+pkg gerrit, func New(*runutil.Sequence, string) *Gerrit
+pkg gerrit, func PresubmitTestTypes() []string
+pkg gerrit, func Push(*runutil.Sequence, CLOpts) error
+pkg gerrit, func Reference(CLOpts) string
+pkg gerrit, method (*Gerrit) PostReview(string, string, map[string]string) error
+pkg gerrit, method (*Gerrit) Query(string) ([]Change, error)
+pkg gerrit, method (*Gerrit) SetTopic(string, CLOpts) error
+pkg gerrit, method (*Gerrit) Submit(string) error
+pkg gerrit, method (Change) OwnerEmail() string
+pkg gerrit, method (Change) Reference() string
+pkg gerrit, type CLOpts struct
+pkg gerrit, type CLOpts struct, Autosubmit bool
+pkg gerrit, type CLOpts struct, Branch string
+pkg gerrit, type CLOpts struct, Ccs []string
+pkg gerrit, type CLOpts struct, Draft bool
+pkg gerrit, type CLOpts struct, Edit bool
+pkg gerrit, type CLOpts struct, Host string
+pkg gerrit, type CLOpts struct, Presubmit PresubmitTestType
+pkg gerrit, type CLOpts struct, Remote string
+pkg gerrit, type CLOpts struct, RemoteBranch string
+pkg gerrit, type CLOpts struct, Reviewers []string
+pkg gerrit, type CLOpts struct, Topic string
+pkg gerrit, type CLOpts struct, Verify bool
+pkg gerrit, type Change struct
+pkg gerrit, type Change struct, AutoSubmit bool
+pkg gerrit, type Change struct, Change_id string
+pkg gerrit, type Change struct, Current_revision string
+pkg gerrit, type Change struct, Labels map[string]map[string]interface{}
+pkg gerrit, type Change struct, MultiPart *MultiPartCLInfo
+pkg gerrit, type Change struct, Owner Owner
+pkg gerrit, type Change struct, PresubmitTest PresubmitTestType
+pkg gerrit, type Change struct, Project string
+pkg gerrit, type Change struct, Revisions Revisions
+pkg gerrit, type Change struct, Topic string
+pkg gerrit, type Comment struct
+pkg gerrit, type Comment struct, Line int
+pkg gerrit, type Comment struct, Message string
+pkg gerrit, type Commit struct
+pkg gerrit, type Commit struct, Message string
+pkg gerrit, type Fetch struct
+pkg gerrit, type Fetch struct, embedded Http
+pkg gerrit, type Gerrit struct
+pkg gerrit, type Http struct
+pkg gerrit, type Http struct, Ref string
+pkg gerrit, type MultiPartCLInfo struct
+pkg gerrit, type MultiPartCLInfo struct, Index int
+pkg gerrit, type MultiPartCLInfo struct, Topic string
+pkg gerrit, type MultiPartCLInfo struct, Total int
+pkg gerrit, type Owner struct
+pkg gerrit, type Owner struct, Email string
+pkg gerrit, type PresubmitTestType string
+pkg gerrit, type Review struct
+pkg gerrit, type Review struct, Comments map[string][]Comment
+pkg gerrit, type Review struct, Labels map[string]string
+pkg gerrit, type Review struct, Message string
+pkg gerrit, type Revision struct
+pkg gerrit, type Revision struct, embedded Commit
+pkg gerrit, type Revision struct, embedded Fetch
+pkg gerrit, type Revisions map[string]Revision
+pkg gerrit, type Topic struct
+pkg gerrit, type Topic struct, Topic string
diff --git a/gerrit/gerrit.go b/gerrit/gerrit.go
index 558df3f..6aee750 100644
--- a/gerrit/gerrit.go
+++ b/gerrit/gerrit.go
@@ -80,20 +80,20 @@
// Gerrit records a hostname of a Gerrit instance.
type Gerrit struct {
host string
- r *runutil.Run
+ s *runutil.Sequence
}
// New is the Gerrit factory.
-func New(r *runutil.Run, host string) *Gerrit {
+func New(s *runutil.Sequence, host string) *Gerrit {
return &Gerrit{
host: host,
- r: r,
+ s: s,
}
}
// 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)
+ cred, err := hostCredentials(g.s, g.host)
if err != nil {
return err
}
@@ -144,7 +144,7 @@
// 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)
+ cred, err := hostCredentials(g.s, g.host)
if err != nil {
return err
}
@@ -317,7 +317,7 @@
// - 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)
+ cred, err := hostCredentials(g.s, g.host)
if err != nil {
return nil, err
}
@@ -345,7 +345,7 @@
// Submit submits the given changelist through Gerrit.
func (g *Gerrit) Submit(changeID string) (e error) {
- cred, err := hostCredentials(g.r, g.host)
+ cred, err := hostCredentials(g.s, g.host)
if err != nil {
return err
}
@@ -427,13 +427,10 @@
// getRemoteURL returns the URL of the Gerrit project with respect to the
// project identified by the current working directory.
-func getRemoteURL(run *runutil.Run, clOpts CLOpts) (string, error) {
+func getRemoteURL(seq *runutil.Sequence, clOpts CLOpts) (string, error) {
args := []string{"config", "--get", "remote.origin.url"}
var stdout, stderr bytes.Buffer
- opts := run.Opts()
- opts.Stdout = &stdout
- opts.Stderr = &stderr
- if err := run.CommandWithOpts(opts, "git", args...); err != nil {
+ if err := seq.Capture(&stdout, &stderr).Last("git", args...); err != nil {
return "", gitutil.Error(stdout.String(), stderr.String(), args...)
}
baseUrl := clOpts.Host
@@ -444,11 +441,11 @@
}
// Push pushes the current branch to Gerrit.
-func Push(run *runutil.Run, clOpts CLOpts) error {
+func Push(seq *runutil.Sequence, clOpts CLOpts) error {
remote := clOpts.Remote
if remote == "" {
var err error
- remote, err = getRemoteURL(run, clOpts)
+ remote, err = getRemoteURL(seq, clOpts)
if err != nil {
return err
}
@@ -464,10 +461,7 @@
args = append(args, "--no-verify")
}
var stdout, stderr bytes.Buffer
- opts := run.Opts()
- opts.Stdout = &stdout
- opts.Stderr = &stderr
- if err := run.CommandWithOpts(opts, "git", args...); err != nil {
+ if err := seq.Capture(&stdout, &stderr).Last("git", args...); err != nil {
return gitutil.Error(stdout.String(), stderr.String(), args...)
}
for _, line := range strings.Split(stderr.String(), "\n") {
@@ -486,7 +480,7 @@
// 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) {
+func hostCredentials(seq *runutil.Sequence, host string) (_ *credentials, e error) {
// Check the host URL is valid.
url, err := url.Parse(host)
if err != nil {
@@ -498,7 +492,7 @@
// Look for the host credentials in the .netrc file.
netrcPath := filepath.Join(os.Getenv("HOME"), ".netrc")
- file, err := run.Open(netrcPath)
+ file, err := seq.Open(netrcPath)
if err != nil {
if !os.IsNotExist(err) {
return nil, err
@@ -518,12 +512,9 @@
// 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 {
+ if err := seq.Capture(&stdout, &stderr).Last("git", args...); err == nil {
cookieFilePath := strings.TrimSpace(stdout.String())
- file, err := run.Open(cookieFilePath)
+ file, err := seq.Open(cookieFilePath)
if err != nil {
if !os.IsNotExist(err) {
return nil, err
diff --git a/profiles/commandline/driver.go b/profiles/commandline/driver.go
index 9277eb0..2e3679c 100644
--- a/profiles/commandline/driver.go
+++ b/profiles/commandline/driver.go
@@ -195,7 +195,7 @@
func runList(jirix *jiri.X, args []string) error {
if showManifestFlag {
- data, err := jirix.Run().ReadFile(manifestFlag)
+ data, err := jirix.NewSeq().ReadFile(manifestFlag)
if err != nil {
return err
}
diff --git a/profiles/env.go b/profiles/env.go
index a5788ea..7d5325b 100644
--- a/profiles/env.go
+++ b/profiles/env.go
@@ -243,7 +243,7 @@
// account for Go workspaces that span multiple jiri projects,
// such as: $JIRI_ROOT/release/go.
if strings.HasPrefix(absWorkspace, project.Path) || strings.HasPrefix(project.Path, absWorkspace) {
- if _, err := jirix.Run().Stat(filepath.Join(absWorkspace)); err == nil {
+ if _, err := jirix.NewSeq().Stat(filepath.Join(absWorkspace)); err == nil {
path = append(path, absWorkspace)
break
}
diff --git a/profiles/env_test.go b/profiles/env_test.go
index 1280186..501368f 100644
--- a/profiles/env_test.go
+++ b/profiles/env_test.go
@@ -181,7 +181,7 @@
case "VDLPATH":
// Make a fake src directory.
want = filepath.Join(fake.X.Root, "test", "src")
- if err := fake.X.Run().MkdirAll(want, 0755); err != nil {
+ if err := fake.X.NewSeq().MkdirAll(want, 0755).Done(); err != nil {
t.Fatalf("%v", err)
}
want = "VDLPATH=" + want
diff --git a/profiles/manifest.go b/profiles/manifest.go
index 0bad100..8528d34 100644
--- a/profiles/manifest.go
+++ b/profiles/manifest.go
@@ -13,6 +13,7 @@
"time"
"v.io/jiri/jiri"
+ "v.io/jiri/runutil"
)
const (
@@ -242,9 +243,9 @@
defer pdb.Unlock()
pdb.db = make(map[string]*Profile)
- data, err := jirix.Run().ReadFile(filename)
+ data, err := jirix.NewSeq().ReadFile(filename)
if err != nil {
- if os.IsNotExist(err) {
+ if runutil.IsNotExist(err) {
return nil
}
return err
@@ -317,17 +318,18 @@
oldName := filename + ".prev"
newName := filename + fmt.Sprintf(".%d", time.Now().UnixNano())
- if err := jirix.Run().WriteFile(newName, data, defaultFileMode); err != nil {
+ s := jirix.NewSeq()
+ if err := s.WriteFile(newName, data, defaultFileMode).Done(); err != nil {
return err
}
- if jirix.Run().FileExists(filename) {
- if err := jirix.Run().Rename(filename, oldName); err != nil {
+ if exists, _ := s.FileExists(filename); exists {
+ if err := s.Rename(filename, oldName).Done(); err != nil {
return err
}
}
- if err := jirix.Run().Rename(newName, filename); err != nil {
+ if err := s.Rename(newName, filename).Done(); err != nil {
return err
}
diff --git a/project.go b/project.go
index f59871a..fa2e77b 100644
--- a/project.go
+++ b/project.go
@@ -7,7 +7,6 @@
import (
"encoding/json"
"fmt"
- "path"
"path/filepath"
"sort"
"strings"
@@ -62,13 +61,14 @@
if err != nil {
return err
}
- projects := map[string]project.Project{}
+ var projects project.Projects
if len(args) > 0 {
for _, arg := range args {
- if p, ok := localProjects[arg]; ok {
- projects[p.Name] = p
+ p, err := localProjects.FindUnique(arg)
+ if err != nil {
+ fmt.Fprintf(jirix.Stderr(), "Error finding local project %q: %v.\n", p.Name, err)
} else {
- fmt.Fprintf(jirix.Stderr(), "Local project %q not found.\n", p.Name)
+ projects[p.Key()] = p
}
}
} else {
@@ -94,21 +94,21 @@
if err != nil {
return err
}
- names := []string{}
- for name := range states {
- names = append(names, name)
+ var keys project.ProjectKeys
+ for key := range states {
+ keys = append(keys, key)
}
- sort.Strings(names)
+ sort.Sort(keys)
- for _, name := range names {
- state := states[name]
+ for _, key := range keys {
+ state := states[key]
if noPristineFlag {
pristine := len(state.Branches) == 1 && state.CurrentBranch == "master" && !state.HasUncommitted && !state.HasUntracked
if pristine {
continue
}
}
- fmt.Fprintf(jirix.Stdout(), "project=%q path=%q\n", path.Base(name), state.Project.Path)
+ fmt.Fprintf(jirix.Stdout(), "project-key=%q path=%q\n", key, state.Project.Path)
if branchesFlag {
for _, branch := range state.Branches {
s := " "
@@ -144,20 +144,20 @@
if err != nil {
return err
}
- names := []string{}
- for name := range states {
- names = append(names, name)
+ var keys project.ProjectKeys
+ for key := range states {
+ keys = append(keys, key)
}
- sort.Strings(names)
+ sort.Sort(keys)
- // Get the name of the current project.
- currentProjectName, err := project.CurrentProjectName(jirix)
+ // Get the key of the current project.
+ currentProjectKey, err := project.CurrentProjectKey(jirix)
if err != nil {
return err
}
var statuses []string
- for _, name := range names {
- state := states[name]
+ for _, key := range keys {
+ state := states[key]
status := ""
if checkDirtyFlag {
if state.HasUncommitted {
@@ -168,8 +168,8 @@
}
}
short := state.CurrentBranch + status
- long := filepath.Base(name) + ":" + short
- if name == currentProjectName {
+ long := filepath.Base(states[key].Project.Name) + ":" + short
+ if key == currentProjectKey {
if showNameFlag {
statuses = append([]string{long}, statuses...)
} else {
diff --git a/project/.api b/project/.api
index 7558eaa..82b6d30 100644
--- a/project/.api
+++ b/project/.api
@@ -5,18 +5,24 @@
pkg project, func CleanupProjects(*jiri.X, Projects, bool) error
pkg project, func CreateSnapshot(*jiri.X, string) error
pkg project, func CurrentManifest(*jiri.X) (*Manifest, error)
-pkg project, func CurrentProjectName(*jiri.X) (string, error)
+pkg project, func CurrentProjectKey(*jiri.X) (ProjectKey, error)
pkg project, func DataDirPath(*jiri.X, string) (string, error)
pkg project, func GerritHost(*jiri.X) (string, error)
-pkg project, func GetProjectStates(*jiri.X, bool) (map[string]*ProjectState, error)
+pkg project, func GetProjectStates(*jiri.X, bool) (map[ProjectKey]*ProjectState, error)
pkg project, func GitHost(*jiri.X) (string, error)
pkg project, func InstallTools(*jiri.X, string) error
pkg project, func LocalProjects(*jiri.X, ScanMode) (Projects, error)
-pkg project, func ParseNames(*jiri.X, []string, map[string]struct{}) (map[string]Project, error)
+pkg project, func ParseNames(*jiri.X, []string, map[string]struct{}) (Projects, error)
pkg project, func PollProjects(*jiri.X, map[string]struct{}) (Update, error)
pkg project, func ReadManifest(*jiri.X) (Projects, Tools, error)
pkg project, func TransitionBinDir(*jiri.X) error
pkg project, func UpdateUniverse(*jiri.X, bool) error
+pkg project, method (Project) Key() ProjectKey
+pkg project, method (ProjectKeys) Len() int
+pkg project, method (ProjectKeys) Less(int, int) bool
+pkg project, method (ProjectKeys) Swap(int, int)
+pkg project, method (Projects) Find(string) Projects
+pkg project, method (Projects) FindUnique(string) (Project, error)
pkg project, method (UnsupportedProtocolErr) Error() string
pkg project, type BranchState struct
pkg project, type BranchState struct, HasGerritMessage bool
@@ -62,13 +68,15 @@
pkg project, type Project struct, Remote string
pkg project, type Project struct, RemoteBranch string
pkg project, type Project struct, Revision string
+pkg project, type ProjectKey string
+pkg project, type ProjectKeys []ProjectKey
pkg project, type ProjectState struct
pkg project, type ProjectState struct, Branches []BranchState
pkg project, type ProjectState struct, CurrentBranch string
pkg project, type ProjectState struct, HasUncommitted bool
pkg project, type ProjectState struct, HasUntracked bool
pkg project, type ProjectState struct, Project Project
-pkg project, type Projects map[string]Project
+pkg project, type Projects map[ProjectKey]Project
pkg project, type ScanMode bool
pkg project, type Tool struct
pkg project, type Tool struct, Data string
diff --git a/project/paths.go b/project/paths.go
index 51ed88b..ce0e523 100644
--- a/project/paths.go
+++ b/project/paths.go
@@ -31,10 +31,12 @@
if !ok {
return "", fmt.Errorf("tool %q not found in the manifest", toolName)
}
- projectName := tool.Project
- project, ok := projects[projectName]
- if !ok {
- return "", fmt.Errorf("project %q not found in the manifest", projectName)
+ // TODO(nlacasse): Tools refer to their project by name, but project name
+ // might not be unique. We really should stop telling telling tools what their
+ // projects are.
+ project, err := projects.FindUnique(tool.Project)
+ if err != nil {
+ return "", err
}
return filepath.Join(project.Path, tool.Data), nil
}
@@ -61,8 +63,6 @@
return getHost(jirix, "git")
}
-// toAbs returns the given path rooted in JIRI_ROOT, if it is not already an
-// absolute path.
func toAbs(jirix *jiri.X, path string) string {
if filepath.IsAbs(path) {
return path
diff --git a/project/project.go b/project/project.go
index 2699c4d..9a59667 100644
--- a/project/project.go
+++ b/project/project.go
@@ -106,8 +106,15 @@
Name string `xml:"name,attr"`
}
-// Projects maps project names to their detailed description.
-type Projects map[string]Project
+// ProjectKey is a unique string for a project.
+type ProjectKey string
+
+// ProjectKeys is a slice of ProjectKeys implementing the Sort interface.
+type ProjectKeys []ProjectKey
+
+func (pks ProjectKeys) Len() int { return len(pks) }
+func (pks ProjectKeys) Less(i, j int) bool { return string(pks[i]) < string(pks[j]) }
+func (pks ProjectKeys) Swap(i, j int) { pks[i], pks[j] = pks[j], pks[i] }
// Project represents a jiri project.
type Project struct {
@@ -136,6 +143,47 @@
Revision string `xml:"revision,attr"`
}
+// projectKeySeparator is a reserved string used in ProjectKeys. It cannot
+// occur in Project names or remotes.
+const projectKeySeparator = "="
+
+// Key returns a unique ProjectKey for the project.
+func (p Project) Key() ProjectKey {
+ return ProjectKey(p.Name + projectKeySeparator + p.Remote)
+}
+
+// Projects maps ProjectKeys to Projects.
+type Projects map[ProjectKey]Project
+
+// Find returns all projects in Projects with the given key or name.
+func (ps Projects) Find(name string) Projects {
+ projects := Projects{}
+ for _, p := range ps {
+ if name == p.Name {
+ projects[p.Key()] = p
+ }
+ }
+ return projects
+}
+
+// FindUnique returns the project in Projects with the given key or
+// name, and returns an error if none or multiple matching projects are found.
+func (ps Projects) FindUnique(name string) (Project, error) {
+ var p Project
+ projects := ps.Find(name)
+ if len(projects) == 0 {
+ return p, fmt.Errorf("no projects found with name %q", name)
+ }
+ if len(projects) > 1 {
+ return p, fmt.Errorf("multiple projects found with name %q", name)
+ }
+ // Return the only project in projects.
+ for _, project := range projects {
+ p = project
+ }
+ return p, nil
+}
+
// Tools maps jiri tool names, to their detailed description.
type Tools map[string]Tool
@@ -262,10 +310,10 @@
return jirix.NewSeq().WriteFile(currentManifestPath, bytes, os.FileMode(0644)).Done()
}
-// CurrentProjectName gets the name of the current project from the
-// current directory by reading the jiri project metadata located in a
-// directory at the root of the current repository.
-func CurrentProjectName(jirix *jiri.X) (string, error) {
+// CurrentProjectKey gets the key of the current project from the current
+// directory by reading the jiri project metadata located in a directory at the
+// root of the current repository.
+func CurrentProjectKey(jirix *jiri.X) (ProjectKey, error) {
topLevel, err := jirix.Git().TopLevel()
if err != nil {
return "", nil
@@ -282,7 +330,7 @@
if err := xml.Unmarshal(bytes, &project); err != nil {
return "", fmt.Errorf("Unmarshal() failed: %v", err)
}
- return project.Name, nil
+ return project.Key(), nil
}
return "", nil
}
@@ -615,9 +663,9 @@
workspaceSet := map[string]bool{}
for _, tool := range tools {
toolPkgs = append(toolPkgs, tool.Package)
- toolProject, ok := projects[tool.Project]
- if !ok {
- return fmt.Errorf("project not found for tool %v", tool.Name)
+ toolProject, err := projects.FindUnique(tool.Project)
+ if err != nil {
+ return err
}
// Identify the Go workspace the tool is in. To this end we use a
// heuristic that identifies the maximal suffix of the project path
@@ -683,11 +731,11 @@
if tool.Package == "" {
continue
}
- project, ok := localProjects[tool.Project]
- if !ok {
- fmt.Errorf("unknown project %v for tool %v", tool.Project, tool.Name)
+ project, err := localProjects.FindUnique(tool.Project)
+ if err != nil {
+ return err
}
- toolProjects[tool.Project] = project
+ toolProjects[project.Key()] = project
toolsToBuild[tool.Name] = tool
toolNames = append(toolNames, tool.Name)
}
@@ -824,10 +872,10 @@
if absPath != project.Path {
return fmt.Errorf("project %v has path %v but was found in %v", project.Name, project.Path, absPath)
}
- if p, ok := projects[project.Name]; ok {
- return fmt.Errorf("name conflict: both %v and %v contain the project %v", p.Path, project.Path, project.Name)
+ if p, ok := projects[project.Key()]; ok {
+ return fmt.Errorf("name conflict: both %v and %v contain project with key %v", p.Path, project.Path, project.Key())
}
- projects[project.Name] = project
+ projects[project.Key()] = project
}
// Recurse into all the sub directories.
@@ -988,7 +1036,7 @@
return UnsupportedProtocolErr(project.Protocol)
}
}
- return ApplyToLocalMaster(jirix, Projects{project.Name: project}, fn)
+ return ApplyToLocalMaster(jirix, Projects{project.Key(): project}, fn)
}
// loadManifest loads the given manifest, processing all of its
@@ -1019,10 +1067,13 @@
}
// Process all projects.
for _, project := range m.Projects {
+ if strings.Contains(project.Name, projectKeySeparator) {
+ return fmt.Errorf("project name cannot contain %q: %q", projectKeySeparator, project.Name)
+ }
if project.Exclude {
// Exclude the project in case it was
// previously included.
- delete(projects, project.Name)
+ delete(projects, project.Key())
continue
}
// Replace the relative path with an absolute one.
@@ -1045,7 +1096,7 @@
if project.RemoteBranch == "" {
project.RemoteBranch = "master"
}
- projects[project.Name] = project
+ projects[project.Key()] = project
}
// Process all tools.
for _, tool := range m.Tools {
@@ -1073,10 +1124,9 @@
delete(hooks, hook.Name)
continue
}
- project, found := projects[hook.Project]
- if !found {
- return fmt.Errorf("hook %v specified project %v which was not found",
- hook.Name, hook.Project)
+ project, err := projects.FindUnique(hook.Project)
+ if err != nil {
+ return fmt.Errorf("error while finding project %q for hook %q: %v", hook.Project, hook.Name, err)
}
// Replace project-relative path with absolute path.
hook.Path = filepath.Join(project.Path, hook.Path)
@@ -1648,16 +1698,16 @@
// projects.
func computeOperations(localProjects, remoteProjects Projects, gc bool) (operations, error) {
result := operations{}
- allProjects := map[string]struct{}{}
- for name, _ := range localProjects {
- allProjects[name] = struct{}{}
+ allProjects := map[ProjectKey]struct{}{}
+ for _, p := range localProjects {
+ allProjects[p.Key()] = struct{}{}
}
- for name, _ := range remoteProjects {
- allProjects[name] = struct{}{}
+ for _, p := range remoteProjects {
+ allProjects[p.Key()] = struct{}{}
}
- for name, _ := range allProjects {
- if localProject, ok := localProjects[name]; ok {
- if remoteProject, ok := remoteProjects[name]; ok {
+ for key, _ := range allProjects {
+ if localProject, ok := localProjects[key]; ok {
+ if remoteProject, ok := remoteProjects[key]; ok {
if localProject.Path != remoteProject.Path {
// moveOperation also does an update, so we don't need to
// check the revision here.
@@ -1688,14 +1738,14 @@
source: localProject.Path,
}, gc})
}
- } else if remoteProject, ok := remoteProjects[name]; ok {
+ } else if remoteProject, ok := remoteProjects[key]; ok {
result = append(result, createOperation{commonOperation{
destination: remoteProject.Path,
project: remoteProject,
source: "",
}})
} else {
- return nil, fmt.Errorf("project %v does not exist", name)
+ return nil, fmt.Errorf("project with key %v does not exist", key)
}
}
sort.Sort(result)
@@ -1704,23 +1754,25 @@
// ParseNames identifies the set of projects that a jiri command should
// be applied to.
-func ParseNames(jirix *jiri.X, args []string, defaultProjects map[string]struct{}) (map[string]Project, error) {
- projects, _, err := ReadManifest(jirix)
+func ParseNames(jirix *jiri.X, args []string, defaultProjects map[string]struct{}) (Projects, error) {
+ manifestProjects, _, err := ReadManifest(jirix)
if err != nil {
return nil, err
}
- result := map[string]Project{}
+ result := Projects{}
if len(args) == 0 {
// Use the default set of projects.
args = set.String.ToSlice(defaultProjects)
}
for _, name := range args {
- if project, ok := projects[name]; ok {
- result[name] = project
- } else {
+ projects := manifestProjects.Find(name)
+ if len(projects) == 0 {
// Issue a warning if the target project does not exist in the
// project manifest.
- fmt.Fprintf(jirix.Stderr(), "WARNING: project %q does not exist in the project manifest and will be skipped\n", name)
+ fmt.Fprintf(jirix.Stderr(), "project %q does not exist in the project manifest", name)
+ }
+ for _, project := range projects {
+ result[project.Key()] = project
}
}
return result, nil
diff --git a/project/project_test.go b/project/project_test.go
index c78324c..03eb785 100644
--- a/project/project_test.go
+++ b/project/project_test.go
@@ -169,7 +169,7 @@
commitManifest(t, jirix, &manifest, manifestDir)
}
-func deleteProject(t *testing.T, jirix *jiri.X, manifestDir, name string) {
+func deleteProject(t *testing.T, jirix *jiri.X, manifestDir, remote string) {
manifestFile := filepath.Join(manifestDir, "v2", "default")
data, err := ioutil.ReadFile(manifestFile)
if err != nil {
@@ -179,7 +179,7 @@
if err := xml.Unmarshal(data, &manifest); err != nil {
t.Fatalf("Unmarshal() failed: %v\n%v", err, data)
}
- manifest.Projects = append(manifest.Projects, project.Project{Exclude: true, Name: name})
+ manifest.Projects = append(manifest.Projects, project.Project{Exclude: true, Name: remote, Remote: remote})
commitManifest(t, jirix, &manifest, manifestDir)
}
diff --git a/project/state.go b/project/state.go
index aaa7cc4..49c350d 100644
--- a/project/state.go
+++ b/project/state.go
@@ -70,18 +70,18 @@
ch <- nil
}
-func GetProjectStates(jirix *jiri.X, checkDirty bool) (map[string]*ProjectState, error) {
+func GetProjectStates(jirix *jiri.X, checkDirty bool) (map[ProjectKey]*ProjectState, error) {
projects, err := LocalProjects(jirix, FastScan)
if err != nil {
return nil, err
}
- states := make(map[string]*ProjectState, len(projects))
+ states := make(map[ProjectKey]*ProjectState, len(projects))
sem := make(chan error, len(projects))
- for name, project := range projects {
+ for key, project := range projects {
state := &ProjectState{
Project: project,
}
- states[name] = state
+ states[key] = state
// jirix is not threadsafe, so we make a clone for each goroutine.
go setProjectState(jirix.Clone(tool.ContextOpts{}), state, checkDirty, sem)
}
diff --git a/snapshot.go b/snapshot.go
index 16da995..eef6c0c 100644
--- a/snapshot.go
+++ b/snapshot.go
@@ -117,7 +117,7 @@
Protocol: "git",
Revision: "HEAD",
}
- if err := project.ApplyToLocalMaster(jirix, project.Projects{p.Name: p}, createFn); err != nil {
+ if err := project.ApplyToLocalMaster(jirix, project.Projects{p.Key(): p}, createFn); err != nil {
return err
}
return nil
diff --git a/tool/context.go b/tool/context.go
index fed3577..b885130 100644
--- a/tool/context.go
+++ b/tool/context.go
@@ -140,7 +140,7 @@
// Gerrit returns the Gerrit instance of the context.
func (ctx Context) Gerrit(host string) *gerrit.Gerrit {
- return gerrit.New(ctx.run, host)
+ return gerrit.New(ctx.NewSeq(), host)
}
type gitOpt interface {