jiri: Implement new remote imports mechanism.
The existing remote imports mechanism has lots of flaws. Here's
an attempt at a new implementation that has a chance of working.
MultiPart: 1/2
Change-Id: Ifbe280aa8fb32529e4af1e729890a0edca26ede0
diff --git a/doc.go b/doc.go
index c74bb6b..2c3b843 100644
--- a/doc.go
+++ b/doc.go
@@ -214,31 +214,21 @@
doesn't already exist, otherwise additional imports are added to the existing
file.
-<manifest> specifies the manifest file to use.
+An <import> element is added to the manifest representing a remote manifest
+import. The manifest file path is relative to the root directory of the remote
+import repository.
-[remote] optionally specifies the remote manifest repository.
-
-If [remote] is not specified, a <fileimport> element is added to the manifest,
-representing a local file import. The manifest file may be an absolute path, or
-relative to the current working directory. The resulting path must be a
-subdirectory of $JIRI_ROOT.
-
-If [remote] is specified, an <import> element is added to the manifest,
-representing a remote manifest import. The remote manifest repository is
-treated similar to regular projects; "jiri update" will update all remote
-manifest repository projects before updating regular projects. The manifest
-file path is relative to the root directory of the remote import repository.
-
-Example of a local file import:
- $ jiri import $JIRI_ROOT/path/to/manifest/file
-
-Example of a remote manifest import:
+Example:
$ jiri import myfile https://foo.com/bar.git
Run "jiri help manifest" for details on manifests.
Usage:
- jiri import [flags] <manifest> [remote]
+ jiri import [flags] <manifest> <remote>
+
+<manifest> specifies the manifest file to use.
+
+<remote> specifies the remote manifest repository.
The jiri import flags are:
-name=
@@ -251,15 +241,11 @@
Write a new .jiri_manifest file with the given specification. If it already
exists, the existing content will be ignored and the file will be
overwritten.
- -path=
- Path to store the manifest project locally. Uses "manifest" if unspecified.
-protocol=git
The version control protocol used by the remote manifest project.
-remote-branch=master
The branch of the remote manifest project to track, without the leading
"origin/".
- -revision=HEAD
- The revision of the remote manifest project to reset to during "jiri update".
-root=
Root to store the manifest project locally.
diff --git a/import.go b/import.go
index fc9ce35..7636f13 100644
--- a/import.go
+++ b/import.go
@@ -5,10 +5,7 @@
package main
import (
- "fmt"
"os"
- "path/filepath"
- "strings"
"v.io/jiri/jiri"
"v.io/jiri/project"
@@ -18,7 +15,7 @@
var (
// Flags for configuring project attributes for remote imports.
- flagImportName, flagImportPath, flagImportProtocol, flagImportRemoteBranch, flagImportRevision, flagImportRoot string
+ flagImportName, flagImportProtocol, flagImportRemoteBranch, flagImportRoot string
// Flags for controlling the behavior of the command.
flagImportOverwrite bool
flagImportOut string
@@ -26,10 +23,8 @@
func init() {
cmdImport.Flags.StringVar(&flagImportName, "name", "", `The name of the remote manifest project, used to disambiguate manifest projects with the same remote. Typically empty.`)
- cmdImport.Flags.StringVar(&flagImportPath, "path", "", `Path to store the manifest project locally. Uses "manifest" if unspecified.`)
cmdImport.Flags.StringVar(&flagImportProtocol, "protocol", "git", `The version control protocol used by the remote manifest project.`)
cmdImport.Flags.StringVar(&flagImportRemoteBranch, "remote-branch", "master", `The branch of the remote manifest project to track, without the leading "origin/".`)
- cmdImport.Flags.StringVar(&flagImportRevision, "revision", "HEAD", `The revision of the remote manifest project to reset to during "jiri update".`)
cmdImport.Flags.StringVar(&flagImportRoot, "root", "", `Root to store the manifest project locally.`)
cmdImport.Flags.BoolVar(&flagImportOverwrite, "overwrite", false, `Write a new .jiri_manifest file with the given specification. If it already exists, the existing content will be ignored and the file will be overwritten.`)
@@ -46,34 +41,25 @@
doesn't already exist, otherwise additional imports are added to the existing
file.
-<manifest> specifies the manifest file to use.
+An <import> element is added to the manifest representing a remote manifest
+import. The manifest file path is relative to the root directory of the remote
+import repository.
-[remote] optionally specifies the remote manifest repository.
-
-If [remote] is not specified, a <fileimport> element is added to the manifest,
-representing a local file import. The manifest file may be an absolute path, or
-relative to the current working directory. The resulting path must be a
-subdirectory of $JIRI_ROOT.
-
-If [remote] is specified, an <import> element is added to the manifest,
-representing a remote manifest import. The remote manifest repository is
-treated similar to regular projects; "jiri update" will update all remote
-manifest repository projects before updating regular projects. The manifest
-file path is relative to the root directory of the remote import repository.
-
-Example of a local file import:
- $ jiri import $JIRI_ROOT/path/to/manifest/file
-
-Example of a remote manifest import:
+Example:
$ jiri import myfile https://foo.com/bar.git
Run "jiri help manifest" for details on manifests.
`,
- ArgsName: "<manifest> [remote]",
+ ArgsName: "<manifest> <remote>",
+ ArgsLong: `
+<manifest> specifies the manifest file to use.
+
+<remote> specifies the remote manifest repository.
+`,
}
func runImport(jirix *jiri.X, args []string) error {
- if len(args) == 0 || len(args) > 2 {
+ if len(args) != 2 {
return jirix.UsageErrorf("wrong number of arguments")
}
// Initialize manifest.
@@ -88,44 +74,16 @@
if manifest == nil {
manifest = &project.Manifest{}
}
- // Add the local or remote import.
- if len(args) == 1 {
- // FileImport.File is relative to the directory containing the manifest
- // file; since the .jiri_manifest file is in JIRI_ROOT, that's what it
- // should be relative to.
- if _, err := os.Stat(args[0]); err != nil {
- return err
- }
- abs, err := filepath.Abs(args[0])
- if err != nil {
- return err
- }
- rel, err := filepath.Rel(jirix.Root, abs)
- if err != nil {
- return err
- }
- if strings.HasPrefix(rel, "..") {
- return fmt.Errorf("%s is not a subdirectory of JIRI_ROOT %s", abs, jirix.Root)
- }
- manifest.FileImports = append(manifest.FileImports, project.FileImport{
- File: rel,
- })
- } else {
- // There's not much error checking when writing the .jiri_manifest file;
- // errors will be reported when "jiri update" is run.
- manifest.Imports = append(manifest.Imports, project.Import{
- Manifest: args[0],
- Root: flagImportRoot,
- Project: project.Project{
- Name: flagImportName,
- Path: flagImportPath,
- Protocol: flagImportProtocol,
- Remote: args[1],
- RemoteBranch: flagImportRemoteBranch,
- Revision: flagImportRevision,
- },
- })
- }
+ // There's not much error checking when writing the .jiri_manifest file;
+ // errors will be reported when "jiri update" is run.
+ manifest.Imports = append(manifest.Imports, project.Import{
+ Manifest: args[0],
+ Name: flagImportName,
+ Protocol: flagImportProtocol,
+ Remote: args[1],
+ RemoteBranch: flagImportRemoteBranch,
+ Root: flagImportRoot,
+ })
// Write output to stdout or file.
outFile := flagImportOut
if outFile == "" {
diff --git a/import_test.go b/import_test.go
index d1ae209..7b13b97 100644
--- a/import_test.go
+++ b/import_test.go
@@ -29,58 +29,19 @@
Stderr: `wrong number of arguments`,
},
{
+ Args: []string{"a"},
+ Stderr: `wrong number of arguments`,
+ },
+ {
Args: []string{"a", "b", "c"},
Stderr: `wrong number of arguments`,
},
- // Local file imports, default append behavior
- {
- Args: []string{"manfile"},
- Want: `<manifest>
- <imports>
- <fileimport file="manfile"/>
- </imports>
-</manifest>
-`,
- },
- {
- Args: []string{"./manfile"},
- Want: `<manifest>
- <imports>
- <fileimport file="manfile"/>
- </imports>
-</manifest>
-`,
- },
- {
- Args: []string{"manfile"},
- Exist: `<manifest>
- <imports>
- <import manifest="bar" remote="https://github.com/orig.git"/>
- </imports>
-</manifest>
-`,
- Want: `<manifest>
- <imports>
- <import manifest="bar" remote="https://github.com/orig.git"/>
- <fileimport file="manfile"/>
- </imports>
-</manifest>
-`,
- },
- {
- Args: []string{"../manfile"},
- Stderr: `not a subdirectory of JIRI_ROOT`,
- },
- {
- Args: []string{"noexist"},
- Stderr: `no such file`,
- },
// Remote imports, default append behavior
{
- Args: []string{"-name=name", "-path=path", "-remote-branch=remotebranch", "-revision=revision", "-root=root", "foo", "https://github.com/new.git"},
+ Args: []string{"-name=name", "-remote-branch=remotebranch", "-root=root", "foo", "https://github.com/new.git"},
Want: `<manifest>
<imports>
- <import manifest="foo" root="root" name="name" path="path" remote="https://github.com/new.git" remotebranch="remotebranch" revision="revision"/>
+ <import manifest="foo" name="name" remote="https://github.com/new.git" remotebranch="remotebranch" root="root"/>
</imports>
</manifest>
`,
@@ -129,48 +90,6 @@
</manifest>
`,
},
- // Local file imports, explicit overwrite behavior
- {
- Args: []string{"-overwrite", "manfile"},
- Want: `<manifest>
- <imports>
- <fileimport file="manfile"/>
- </imports>
-</manifest>
-`,
- },
- {
- Args: []string{"-overwrite", "./manfile"},
- Want: `<manifest>
- <imports>
- <fileimport file="manfile"/>
- </imports>
-</manifest>
-`,
- },
- {
- Args: []string{"-overwrite", "manfile"},
- Exist: `<manifest>
- <imports>
- <import manifest="bar" remote="https://github.com/orig.git"/>
- </imports>
-</manifest>
-`,
- Want: `<manifest>
- <imports>
- <fileimport file="manfile"/>
- </imports>
-</manifest>
-`,
- },
- {
- Args: []string{"-overwrite", "../manfile"},
- Stderr: `not a subdirectory of JIRI_ROOT`,
- },
- {
- Args: []string{"-overwrite", "noexist"},
- Stderr: `no such file`,
- },
// Remote imports, explicit overwrite behavior
{
Args: []string{"-overwrite", "foo", "https://github.com/new.git"},
diff --git a/jiri/x.go b/jiri/x.go
index 6a574b3..c67a452 100644
--- a/jiri/x.go
+++ b/jiri/x.go
@@ -144,6 +144,9 @@
// ResolveManifestPath resolves the given manifest name to an absolute path in
// the local filesystem.
+//
+// TODO(toddw): Remove this once the transition to new manifests is done. In
+// the new world, we always start with the JiriManifestFile.
func (x *X) ResolveManifestPath(name string) (string, error) {
if x.UsingOldManifests() {
return x.resolveManifestPathDeprecated(name)
diff --git a/profiles/profilesreader/reader.go b/profiles/profilesreader/reader.go
index eff4533..09bea9c 100644
--- a/profiles/profilesreader/reader.go
+++ b/profiles/profilesreader/reader.go
@@ -104,7 +104,6 @@
jirix *jiri.X
config *util.Config
projects project.Projects
- tools project.Tools
pdb *profiles.DB
}
@@ -117,7 +116,7 @@
if err != nil {
return nil, err
}
- projects, tools, err := project.ReadJiriManifest(jirix)
+ projects, _, err := project.LoadManifest(jirix)
if err != nil {
return nil, err
}
@@ -131,7 +130,6 @@
jirix: jirix,
config: config,
projects: projects,
- tools: tools,
profilesMode: bool(profilesMode),
pdb: pdb,
}
diff --git a/project/.api b/project/.api
index f475c27..7f0d015 100644
--- a/project/.api
+++ b/project/.api
@@ -9,6 +9,7 @@
pkg project, func DataDirPath(*jiri.X, string) (string, error)
pkg project, func GetProjectStates(*jiri.X, bool) (map[ProjectKey]*ProjectState, error)
pkg project, func InstallTools(*jiri.X, string) error
+pkg project, func LoadManifest(*jiri.X) (Projects, Tools, error)
pkg project, func LocalProjects(*jiri.X, ScanMode) (Projects, error)
pkg project, func MakeProjectKey(string, string) ProjectKey
pkg project, func ManifestFromBytes([]byte) (*Manifest, error)
@@ -17,13 +18,11 @@
pkg project, func PollProjects(*jiri.X, map[string]struct{}) (Update, error)
pkg project, func ProjectAtPath(*jiri.X, string) (Project, error)
pkg project, func ProjectFromFile(*jiri.X, string) (*Project, error)
-pkg project, func ReadJiriManifest(*jiri.X) (Projects, Tools, error)
pkg project, func TransitionBinDir(*jiri.X) error
pkg project, func UpdateUniverse(*jiri.X, bool) error
+pkg project, method (*Import) ProjectKey() ProjectKey
pkg project, method (*Manifest) ToBytes() ([]byte, error)
pkg project, method (*Manifest) ToFile(*jiri.X, string) error
-pkg project, method (Import) Key() ProjectKey
-pkg project, method (Import) ToFile(*jiri.X, string) error
pkg project, method (Project) Key() ProjectKey
pkg project, method (Project) ToFile(*jiri.X, string) error
pkg project, method (ProjectKeys) Len() int
@@ -39,18 +38,21 @@
pkg project, type CL struct, Author string
pkg project, type CL struct, Description string
pkg project, type CL struct, Email string
-pkg project, type FileImport struct
-pkg project, type FileImport struct, File string
-pkg project, type FileImport struct, XMLName struct{}
pkg project, type Import struct
pkg project, type Import struct, Manifest string
+pkg project, type Import struct, Name string
+pkg project, type Import struct, Protocol string
+pkg project, type Import struct, Remote string
+pkg project, type Import struct, RemoteBranch string
pkg project, type Import struct, Root string
pkg project, type Import struct, XMLName struct{}
-pkg project, type Import struct, embedded Project
+pkg project, type LocalImport struct
+pkg project, type LocalImport struct, File string
+pkg project, type LocalImport struct, XMLName struct{}
pkg project, type Manifest struct
-pkg project, type Manifest struct, FileImports []FileImport
pkg project, type Manifest struct, Imports []Import
pkg project, type Manifest struct, Label string
+pkg project, type Manifest struct, LocalImports []LocalImport
pkg project, type Manifest struct, Projects []Project
pkg project, type Manifest struct, Tools []Tool
pkg project, type Manifest struct, XMLName struct{}
diff --git a/project/paths.go b/project/paths.go
index d34c41f..e21e9bc 100644
--- a/project/paths.go
+++ b/project/paths.go
@@ -18,7 +18,7 @@
// breaks. We should revisit the whole data directory thing, and in particular
// see if we can get rid of tools having to know their own names.
func DataDirPath(jirix *jiri.X, toolName string) (string, error) {
- projects, tools, err := ReadJiriManifest(jirix)
+ projects, tools, err := LoadManifest(jirix)
if err != nil {
return "", err
}
diff --git a/project/project.go b/project/project.go
index 7b81ad4..3d52109 100644
--- a/project/project.go
+++ b/project/project.go
@@ -8,6 +8,7 @@
"bytes"
"encoding/xml"
"fmt"
+ "hash/fnv"
"io/ioutil"
"net/url"
"os"
@@ -40,12 +41,12 @@
// Manifest represents a setting used for updating the universe.
type Manifest struct {
- Imports []Import `xml:"imports>import"`
- FileImports []FileImport `xml:"imports>fileimport"`
- Label string `xml:"label,attr,omitempty"`
- Projects []Project `xml:"projects>project"`
- Tools []Tool `xml:"tools>tool"`
- XMLName struct{} `xml:"manifest"`
+ Imports []Import `xml:"imports>import"`
+ LocalImports []LocalImport `xml:"imports>localimport"`
+ Label string `xml:"label,attr,omitempty"`
+ Projects []Project `xml:"projects>project"`
+ Tools []Tool `xml:"tools>tool"`
+ XMLName struct{} `xml:"manifest"`
}
// ManifestFromBytes returns a manifest parsed from data, with defaults filled
@@ -81,11 +82,11 @@
emptyProjectsBytes = []byte("\n <projects></projects>\n")
emptyToolsBytes = []byte("\n <tools></tools>\n")
- endElemBytes = []byte("/>\n")
- endImportBytes = []byte("></import>\n")
- endFileImportBytes = []byte("></fileimport>\n")
- endProjectBytes = []byte("></project>\n")
- endToolBytes = []byte("></tool>\n")
+ endElemBytes = []byte("/>\n")
+ endImportBytes = []byte("></import>\n")
+ endLocalImportBytes = []byte("></localimport>\n")
+ endProjectBytes = []byte("></project>\n")
+ endToolBytes = []byte("></tool>\n")
endImportSoloBytes = []byte("></import>")
endProjectSoloBytes = []byte("></project>")
@@ -97,7 +98,7 @@
x := new(Manifest)
x.Label = m.Label
x.Imports = append([]Import(nil), m.Imports...)
- x.FileImports = append([]FileImport(nil), m.FileImports...)
+ x.LocalImports = append([]LocalImport(nil), m.LocalImports...)
x.Projects = append([]Project(nil), m.Projects...)
x.Tools = append([]Tool(nil), m.Tools...)
return x
@@ -119,7 +120,7 @@
data = bytes.Replace(data, emptyProjectsBytes, newlineBytes, -1)
data = bytes.Replace(data, emptyToolsBytes, newlineBytes, -1)
data = bytes.Replace(data, endImportBytes, endElemBytes, -1)
- data = bytes.Replace(data, endFileImportBytes, endElemBytes, -1)
+ data = bytes.Replace(data, endLocalImportBytes, endElemBytes, -1)
data = bytes.Replace(data, endProjectBytes, endElemBytes, -1)
data = bytes.Replace(data, endToolBytes, endElemBytes, -1)
if !bytes.HasSuffix(data, newlineBytes) {
@@ -153,8 +154,8 @@
return err
}
}
- for index := range m.FileImports {
- if err := m.FileImports[index].validate(); err != nil {
+ for index := range m.LocalImports {
+ if err := m.LocalImports[index].validate(); err != nil {
return err
}
}
@@ -177,8 +178,8 @@
return err
}
}
- for index := range m.FileImports {
- if err := m.FileImports[index].validate(); err != nil {
+ for index := range m.LocalImports {
+ if err := m.LocalImports[index].validate(); err != nil {
return err
}
}
@@ -199,36 +200,37 @@
type Import struct {
// Manifest file to use from the remote manifest project.
Manifest string `xml:"manifest,attr,omitempty"`
- // Root path, prepended to the manifest project path, as well as all projects
- // specified in the manifest file.
- Root string `xml:"root,attr,omitempty"`
- // Project description of the manifest repository.
- Project
+ // Name is the name of the remote manifest project, used to determine the
+ // project key.
+ //
+ // If Remote and Manifest are empty, it is the old-style name of the manifest
+ // to import, similar to localimport. This is deprecated behavior, and will be
+ // removed.
+ //
+ // TODO(toddw): Remove the old behavior when the transition to new-style
+ // manifests is complete.
+ Name string `xml:"name,attr,omitempty"`
+ // Protocol is the version control protocol used by the remote manifest
+ // project. If not set, "git" is used as the default.
+ Protocol string `xml:"protocol,attr,omitempty"`
+ // Remote is the remote manifest project to import.
+ Remote string `xml:"remote,attr,omitempty"`
+ // RemoteBranch is the name of the remote branch to track. It doesn't affect
+ // the name of the local branch that jiri maintains, which is always
+ // "master". If not set, "master" is used as the default.
+ RemoteBranch string `xml:"remotebranch,attr,omitempty"`
+ // Root path, prepended to all project paths specified in the manifest file.
+ Root string `xml:"root,attr,omitempty"`
XMLName struct{} `xml:"import"`
}
-// ToFile writes the import i to a file with the given filename, with defaults
-// unfilled.
-func (i Import) ToFile(jirix *jiri.X, filename string) error {
- if err := i.unfillDefaults(); err != nil {
- return err
- }
- data, err := xml.Marshal(i)
- if err != nil {
- return fmt.Errorf("import xml.Marshal failed: %v", err)
- }
- // Same logic as Manifest.ToBytes, to make the output more compact.
- data = bytes.Replace(data, endImportSoloBytes, endElemSoloBytes, -1)
- return safeWriteFile(jirix, filename, data)
-}
-
func (i *Import) fillDefaults() error {
if i.Remote != "" {
- if i.Path == "" {
- i.Path = "manifest"
+ if i.Protocol == "" {
+ i.Protocol = "git"
}
- if err := i.Project.fillDefaults(); err != nil {
- return err
+ if i.RemoteBranch == "" {
+ i.RemoteBranch = "master"
}
}
return i.validate()
@@ -236,11 +238,11 @@
func (i *Import) unfillDefaults() error {
if i.Remote != "" {
- if i.Path == "manifest" {
- i.Path = ""
+ if i.Protocol == "git" {
+ i.Protocol = ""
}
- if err := i.Project.unfillDefaults(); err != nil {
- return err
+ if i.RemoteBranch == "master" {
+ i.RemoteBranch = ""
}
}
return i.validate()
@@ -268,31 +270,56 @@
return nil
}
-// remoteKey returns a key based on the remote and manifest, used for
+func (i *Import) toProject(path string) (Project, error) {
+ p := Project{
+ Name: i.Name,
+ Path: path,
+ Protocol: i.Protocol,
+ Remote: i.Remote,
+ RemoteBranch: i.RemoteBranch,
+ }
+ err := p.fillDefaults()
+ return p, err
+}
+
+// ProjectKey returns the unique ProjectKey for the imported project.
+func (i *Import) ProjectKey() ProjectKey {
+ return MakeProjectKey(i.Name, i.Remote)
+}
+
+// projectKeyFileName returns a file name based on the ProjectKey.
+func (i *Import) projectKeyFileName() string {
+ // TODO(toddw): Disallow weird characters from project names.
+ hash := fnv.New64a()
+ hash.Write([]byte(i.ProjectKey()))
+ return fmt.Sprintf("%s_%x", i.Name, hash.Sum64())
+}
+
+// cycleKey returns a key based on the remote and manifest, used for
// cycle-detection. It's only valid for new-style remote imports; it's empty
// for the old-style local imports.
-func (i *Import) remoteKey() string {
+func (i *Import) cycleKey() string {
if i.Remote == "" {
return ""
}
- // We don't join the remote and manifest with a slash, since that might not be
- // unique. E.g.
+ // We don't join the remote and manifest with a slash or any other url-safe
+ // character, since that might not be unique. E.g.
// remote: https://foo.com/a/b remote: https://foo.com/a
// manifest: c manifest: b/c
// In both cases, the key would be https://foo.com/a/b/c.
return i.Remote + " + " + i.Manifest
}
-// FileImport represents a file-based import.
-type FileImport struct {
+// LocalImport represents a local manifest import.
+type LocalImport struct {
// Manifest file to import from.
File string `xml:"file,attr,omitempty"`
- XMLName struct{} `xml:"fileimport"`
+ XMLName struct{} `xml:"localimport"`
}
-func (i *FileImport) validate() error {
+func (i *LocalImport) validate() error {
if i.File == "" {
- return fmt.Errorf("bad fileimport: must specify file: %+v", *i)
+ return fmt.Errorf("bad localimport: must specify file: %+v", *i)
}
return nil
}
@@ -398,10 +425,13 @@
}
// Same logic as Manifest.ToBytes, to make the output more compact.
data = bytes.Replace(data, endProjectSoloBytes, endElemSoloBytes, -1)
+ if !bytes.HasSuffix(data, newlineBytes) {
+ data = append(data, '\n')
+ }
return safeWriteFile(jirix, filename, data)
}
-// Key returns a unique ProjectKey for the project.
+// Key returns the unique ProjectKey for the project.
func (p Project) Key() ProjectKey {
return MakeProjectKey(p.Name, p.Remote)
}
@@ -563,7 +593,7 @@
}
// Add all tools from the current manifest to the snapshot manifest.
- _, tools, err := ReadJiriManifest(jirix)
+ _, tools, err := LoadManifest(jirix)
if err != nil {
return err
}
@@ -642,25 +672,29 @@
jirix.TimerPush("local projects")
defer jirix.TimerPop()
- latestUpdateSnapshot := jirix.UpdateHistoryLatestLink()
- latestUpdateSnapshotExists, err := jirix.NewSeq().IsFile(latestUpdateSnapshot)
+ latestSnapshot := jirix.UpdateHistoryLatestLink()
+ latestSnapshotExists, err := jirix.NewSeq().IsFile(latestSnapshot)
if err != nil {
return nil, err
}
- if scanMode == FastScan && latestUpdateSnapshotExists {
- // Fast path: Full scan was not requested, and we have a snapshot
- // containing the latest update. Check that the projects listed in the
- // snapshot exist locally. If not, then fall back on the slow path.
- manifestProjects, _, err := readManifestFile(jirix, latestUpdateSnapshot)
+ if scanMode == FastScan && latestSnapshotExists {
+ // Fast path: Full scan was not requested, and we have a snapshot containing
+ // the latest update. Check that the projects listed in the snapshot exist
+ // locally. If not, then fall back on the slow path.
+ //
+ // An error will be returned if the snapshot contains remote imports, since
+ // that would cause an infinite loop; we'd need local projects, in order to
+ // load the snapshot, in order to determine the local projects.
+ snapshotProjects, _, err := loadManifestFile(jirix, latestSnapshot, nil)
if err != nil {
return nil, err
}
- projectsExist, err := projectsExistLocally(jirix, manifestProjects)
+ projectsExist, err := projectsExistLocally(jirix, snapshotProjects)
if err != nil {
return nil, err
}
if projectsExist {
- return setProjectRevisions(jirix, manifestProjects)
+ return setProjectRevisions(jirix, snapshotProjects)
}
}
@@ -715,7 +749,7 @@
if err != nil {
return nil, err
}
- remoteProjects, _, err := ReadJiriManifest(jirix)
+ remoteProjects, _, err := LoadManifest(jirix)
if err != nil {
return nil, err
}
@@ -776,25 +810,43 @@
return update, nil
}
-// ReadJiriManifest reads and parses the .jiri_manifest file.
-func ReadJiriManifest(jirix *jiri.X) (Projects, Tools, error) {
- file, err := jirix.ResolveManifestPath(jirix.Manifest())
+// LoadManifest loads the manifest, starting with the .jiri_manifest file,
+// resolving remote and local imports. Returns the projects and tools specified
+// by the manifest.
+//
+// If the user is still using old-style manifests, it uses the old
+// ResolveManifestPath logic to determine the initial manifest file, since the
+// .jiri_manifest doesn't exist.
+func LoadManifest(jirix *jiri.X) (Projects, Tools, error) {
+ jirix.TimerPush("load manifest")
+ defer jirix.TimerPop()
+ var (
+ file string
+ localProjects Projects
+ err error
+ )
+ // TODO(toddw): Remove old manifest logic when the transition is complete.
+ if jirix.UsingOldManifests() {
+ file, err = jirix.ResolveManifestPath(jirix.Manifest())
+ } else {
+ file = jirix.JiriManifestFile()
+ localProjects, err = LocalProjects(jirix, FastScan)
+ }
if err != nil {
return nil, nil, err
}
- return readManifestFile(jirix, file)
+ return loadManifestFile(jirix, file, localProjects)
}
-// readManifestFile reads and parses the manifest with the given filename.
-func readManifestFile(jirix *jiri.X, file string) (Projects, Tools, error) {
- jirix.TimerPush("read manifest")
- defer jirix.TimerPop()
- var imp importer
- projects, tools := Projects{}, Tools{}
- if err := imp.Load(jirix, jirix.Root, file, "", projects, tools); err != nil {
+// loadManifestFile loads the manifest starting with the given file, resolving
+// remote and local imports. Local projects are used to resolve remote imports;
+// if nil, encountering any remote import will result in an error.
+func loadManifestFile(jirix *jiri.X, file string, localProjects Projects) (Projects, Tools, error) {
+ ld := newManifestLoader(localProjects, false)
+ if err := ld.Load(jirix, "", file, ""); err != nil {
return nil, nil, err
}
- return projects, tools, nil
+ return ld.Projects, ld.Tools, nil
}
// getManifestRemote returns the remote url of the origin from the manifest
@@ -811,76 +863,84 @@
}, "get manifest origin").Done()
}
-func updateManifestProjects(jirix *jiri.X) error {
- jirix.TimerPush("update manifest")
+func loadUpdatedManifest(jirix *jiri.X, localProjects Projects) (Projects, Tools, string, error) {
+ jirix.TimerPush("load updated manifest")
defer jirix.TimerPop()
if jirix.UsingOldManifests() {
- return updateManifestProjectsDeprecated(jirix)
+ projects, tools, err := loadUpdatedManifestDeprecated(jirix)
+ return projects, tools, "", err
}
- // Update the repositories corresponding to all remote imports.
- //
- // TODO(toddw): Cache local projects in jirix, so that we don't need to
- // perform multiple full scans.
- localProjects, err := LocalProjects(jirix, FullScan)
- if err != nil {
- return err
+ ld := newManifestLoader(localProjects, true)
+ if err := ld.Load(jirix, "", jirix.JiriManifestFile(), ""); err != nil {
+ return nil, nil, ld.TmpDir, err
}
- file, err := jirix.ResolveManifestPath(jirix.Manifest())
- if err != nil {
- return err
- }
- var imp importer
- return imp.Update(jirix, jirix.Root, file, "", localProjects)
+ return ld.Projects, ld.Tools, ld.TmpDir, nil
}
-func updateManifestProjectsDeprecated(jirix *jiri.X) error {
+// TODO(toddw): Remove this logic when the transition to new manifests is done.
+func loadUpdatedManifestDeprecated(jirix *jiri.X) (Projects, Tools, error) {
manifestPath := filepath.Join(jirix.Root, ".manifest")
manifestRemote, err := getManifestRemote(jirix, manifestPath)
if err != nil {
- return err
+ return nil, nil, err
}
project := Project{
- Path: manifestPath,
- Protocol: "git",
- Remote: manifestRemote,
- Revision: "HEAD",
- RemoteBranch: "master",
+ Path: manifestPath,
+ Remote: manifestRemote,
}
- return resetProject(jirix, project)
+ if err := project.fillDefaults(); err != nil {
+ return nil, nil, err
+ }
+ if err := syncProjectMaster(jirix, project); err != nil {
+ return nil, nil, err
+ }
+ file, err := jirix.ResolveManifestPath(jirix.Manifest())
+ if err != nil {
+ return nil, nil, err
+ }
+ return loadManifestFile(jirix, file, nil)
}
-// 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.
+// UpdateUniverse updates all local projects and tools to match the remote
+// counterparts identified in the manifest. Optionally, the 'gc' flag can be
+// used to indicate that local projects that no longer exist remotely should be
+// removed.
func UpdateUniverse(jirix *jiri.X, gc bool) (e error) {
jirix.TimerPush("update universe")
defer jirix.TimerPop()
- // 0. Update all manifest projects to match their remote counterparts, and
- // read the manifest file.
- if err := updateManifestProjects(jirix); err != nil {
- return err
+ // 0. Load the manifest, updating all manifest projects to match their remote
+ // counterparts.
+ scanMode := FastScan
+ if gc {
+ scanMode = FullScan
}
- remoteProjects, remoteTools, err := ReadJiriManifest(jirix)
+ localProjects, err := LocalProjects(jirix, scanMode)
if err != nil {
return err
}
s := jirix.NewSeq()
+ remoteProjects, remoteTools, tmpLoadDir, err := loadUpdatedManifest(jirix, localProjects)
+ if tmpLoadDir != "" {
+ defer collect.Error(func() error { return s.RemoveAll(tmpLoadDir).Done() }, &e)
+ }
+ if err != nil {
+ return err
+ }
// 1. Update all local projects to match their remote counterparts.
- if err := updateProjects(jirix, remoteProjects, gc); err != nil {
+ if err := updateProjects(jirix, localProjects, remoteProjects, gc); err != nil {
return err
}
// 2. Build all tools in a temporary directory.
- tmpDir, err := s.TempDir("", "tmp-jiri-tools-build")
+ tmpToolsDir, err := s.TempDir("", "tmp-jiri-tools-build")
if err != nil {
return fmt.Errorf("TempDir() failed: %v", err)
}
- defer collect.Error(func() error { return s.RemoveAll(tmpDir).Done() }, &e)
- if err := buildToolsFromMaster(jirix, remoteTools, tmpDir); err != nil {
+ defer collect.Error(func() error { return s.RemoveAll(tmpToolsDir).Done() }, &e)
+ if err := buildToolsFromMaster(jirix, remoteProjects, remoteTools, tmpToolsDir); err != nil {
return err
}
// 3. Install the tools into $JIRI_ROOT/.jiri_root/bin.
- return InstallTools(jirix, tmpDir)
+ return InstallTools(jirix, tmpToolsDir)
}
// ApplyToLocalMaster applies an operation expressed as the given function to
@@ -1003,13 +1063,8 @@
// available in the local master branch of the tools repository. Notably, this
// function does not perform any version control operation on the master
// branch.
-func buildToolsFromMaster(jirix *jiri.X, tools Tools, outputDir string) error {
- localProjects, err := LocalProjects(jirix, FastScan)
- if err != nil {
- return err
- }
+func buildToolsFromMaster(jirix *jiri.X, projects Projects, tools Tools, outputDir string) error {
failed := false
-
toolsToBuild, toolProjects := Tools{}, Projects{}
toolNames := []string{} // Used for logging purposes.
for _, tool := range tools {
@@ -1020,7 +1075,7 @@
if tool.Package == "" {
continue
}
- project, err := localProjects.FindUnique(tool.Project)
+ project, err := projects.FindUnique(tool.Project)
if err != nil {
return err
}
@@ -1263,213 +1318,263 @@
return nil
}
-// resetProject advances the local master branch of the given
-// project, which is expected to exist locally at project.Path.
-func resetProject(jirix *jiri.X, project Project) error {
- fn := func() error {
- switch project.Protocol {
- case "git":
- if project.Remote == "" {
- return fmt.Errorf("project %v does not have a remote", project.Name)
- }
- if err := gitutil.New(jirix.NewSeq()).SetRemoteUrl("origin", project.Remote); err != nil {
- return err
- }
- if err := gitutil.New(jirix.NewSeq()).Fetch("origin"); err != nil {
- return err
- }
-
- // Having a specific revision trumps everything else - once fetched,
- // always reset to that revision.
- if project.Revision != "" && project.Revision != "HEAD" {
- return gitutil.New(jirix.NewSeq()).Reset(project.Revision)
- }
-
- // If no revision, reset to the configured remote branch, or master
- // if no remote branch.
- remoteBranch := project.RemoteBranch
- if remoteBranch == "" {
- remoteBranch = "master"
- }
- return gitutil.New(jirix.NewSeq()).Reset("origin/" + remoteBranch)
- default:
- return UnsupportedProtocolErr(project.Protocol)
+// fetchProject fetches from the project remote.
+func fetchProject(jirix *jiri.X, project Project) error {
+ switch project.Protocol {
+ case "git":
+ if project.Remote == "" {
+ return fmt.Errorf("project %q does not have a remote", project.Name)
}
+ if err := gitutil.New(jirix.NewSeq()).SetRemoteUrl("origin", project.Remote); err != nil {
+ return err
+ }
+ return gitutil.New(jirix.NewSeq()).Fetch("origin")
+ default:
+ return UnsupportedProtocolErr(project.Protocol)
}
- return ApplyToLocalMaster(jirix, Projects{project.Key(): project}, fn)
}
-// importer handles importing manifest files. There are two uses: Load reads
-// full manifests into memory, while Update updates remote manifest projects.
-type importer struct {
- cycleStack []cycleInfo
+// resetProjectCurrentBranch resets the current branch to the revision and
+// branch specified on the project.
+func resetProjectCurrentBranch(jirix *jiri.X, project Project) error {
+ if err := project.fillDefaults(); err != nil {
+ return err
+ }
+ switch project.Protocol {
+ case "git":
+ // Having a specific revision trumps everything else.
+ if project.Revision != "HEAD" {
+ return gitutil.New(jirix.NewSeq()).Reset(project.Revision)
+ }
+ // If no revision, reset to the configured remote branch, or master
+ // if no remote branch.
+ return gitutil.New(jirix.NewSeq()).Reset("origin/" + project.RemoteBranch)
+ default:
+ return UnsupportedProtocolErr(project.Protocol)
+ }
+}
+
+// syncProjectMaster fetches from the project remote and resets the local master
+// branch to the revision and branch specified on the project.
+func syncProjectMaster(jirix *jiri.X, project Project) error {
+ return ApplyToLocalMaster(jirix, Projects{project.Key(): project}, func() error {
+ if err := fetchProject(jirix, project); err != nil {
+ return err
+ }
+ return resetProjectCurrentBranch(jirix, project)
+ })
+}
+
+// newManifestLoader returns a new manifest loader. The localProjects are used
+// to resolve remote imports; if nil, encountering any remote import will result
+// in an error. If update is true, remote manifest import projects that don't
+// exist locally are cloned under TmpDir, and inserted into localProjects.
+//
+// If update is true, remote changes to manifest projects will be fetched, and
+// manifest projects that don't exist locally will be created in temporary
+// directories, and added to localProjects.
+func newManifestLoader(localProjects Projects, update bool) *loader {
+ return &loader{
+ Projects: make(Projects),
+ Tools: make(Tools),
+ localProjects: localProjects,
+ update: update,
+ }
+}
+
+type loader struct {
+ Projects Projects
+ Tools Tools
+ TmpDir string
+ localProjects Projects
+ update bool
+ cycleStack []cycleInfo
}
type cycleInfo struct {
file, key string
}
-// importNoCycles checks for cycles in imports. There are two types of cycles:
+// loadNoCycles checks for cycles in imports. There are two types of cycles:
// file - Cycle in the paths of manifest files in the local filesystem.
// key - Cycle in the remote manifests specified by remote imports.
//
// Example of file cycles. File A imports file B, and vice versa.
// file=manifest/A file=manifest/B
// <manifest> <manifest>
-// <fileimport file="B"/> <fileimport file="A"/>
+// <localimport file="B"/> <localimport file="A"/>
// </manifest> </manifest>
//
// Example of key cycles. The key consists of "remote/manifest", e.g.
// https://vanadium.googlesource.com/manifest/v2/default
// In the example, key x/A imports y/B, and vice versa.
-// key=x/A key=y/B
-// <manifest> <manifest>
-// <import remote="y" manifest="B"/> <import remote="x" manifest="A"/>
-// </manifest> </manifest>
+// key=x/A key=y/B
+// <manifest> <manifest>
+// <import remote="y" manifest="B"/> <import remote="x" manifest="A"/>
+// </manifest> </manifest>
//
// The above examples are simple, but the general strategy is demonstrated. We
// keep a single stack for both files and keys, and push onto each stack before
-// running the recursive load or update function, and pop the stack when the
+// running the recursive read or update function, and pop the stack when the
// function is done. If we see a duplicate on the stack at any point, we know
-// there's a cycle. Note that we know the file for both local fileimports as
-// well as remote imports, but we only know the key for remote imports; the key
-// for local fileimports is empty.
+// there's a cycle. Note that we know the file for both local and remote
+// imports, but we only know the key for remote imports; the key for local
+// imports is empty.
//
-// A more complex case would involve a combination of local fileimports and
-// remote imports, using the "root" attribute to change paths on the local
-// filesystem. In this case the key will eventually expose the cycle.
-func (imp *importer) importNoCycles(file, key string, fn func() error) error {
- info := cycleInfo{file, key}
- for _, c := range imp.cycleStack {
- if file == c.file {
- return fmt.Errorf("import cycle detected in local manifest files: %q", append(imp.cycleStack, info))
- }
- if key != "" && key == c.key {
- return fmt.Errorf("import cycle detected in remote manifest imports: %q", append(imp.cycleStack, info))
+// A more complex case would involve a combination of local and remote imports,
+// using the "root" attribute to change paths on the local filesystem. In this
+// case the key will eventually expose the cycle.
+func (ld *loader) loadNoCycles(jirix *jiri.X, root, file, cycleKey string) error {
+ info := cycleInfo{file, cycleKey}
+ for _, c := range ld.cycleStack {
+ switch {
+ case file == c.file:
+ return fmt.Errorf("import cycle detected in local manifest files: %q", append(ld.cycleStack, info))
+ case cycleKey == c.key && cycleKey != "":
+ return fmt.Errorf("import cycle detected in remote manifest imports: %q", append(ld.cycleStack, info))
}
}
- imp.cycleStack = append(imp.cycleStack, info)
- if err := fn(); err != nil {
+ ld.cycleStack = append(ld.cycleStack, info)
+ if err := ld.load(jirix, root, file); err != nil {
return err
}
- imp.cycleStack = imp.cycleStack[:len(imp.cycleStack)-1]
+ ld.cycleStack = ld.cycleStack[:len(ld.cycleStack)-1]
return nil
}
-func (imp *importer) Load(jirix *jiri.X, root, file, key string, projects Projects, tools Tools) error {
- return imp.importNoCycles(file, key, func() error {
- return imp.load(jirix, root, file, projects, tools)
- })
+// shortFileName returns the relative path if file is relative to root,
+// otherwise returns the file name unchanged.
+func shortFileName(root, file string) string {
+ if p := root + string(filepath.Separator); strings.HasPrefix(file, p) {
+ return file[len(p):]
+ }
+ return file
}
-func (imp *importer) load(jirix *jiri.X, root, file string, projects Projects, tools Tools) error {
+func (ld *loader) Load(jirix *jiri.X, root, file, cycleKey string) error {
+ jirix.TimerPush("load " + shortFileName(jirix.Root, file))
+ defer jirix.TimerPop()
+ return ld.loadNoCycles(jirix, root, file, cycleKey)
+}
+
+func (ld *loader) load(jirix *jiri.X, root, file string) error {
m, err := ManifestFromFile(jirix, file)
if err != nil {
return err
}
- // Process all imports.
- for _, _import := range m.Imports {
- newRoot, newFile := root, ""
- if _import.Remote != "" {
- // New-style remote import
- newRoot = filepath.Join(root, _import.Root)
- newFile = filepath.Join(newRoot, _import.Path, _import.Manifest)
- } else {
- // Old-style name-based local import.
- //
- // TODO(toddw): Remove this logic when the manifest transition is done.
- if newFile, err = jirix.ResolveManifestPath(_import.Name); err != nil {
- return err
- }
- }
- if err := imp.Load(jirix, newRoot, newFile, _import.remoteKey(), projects, tools); err != nil {
- return err
- }
- }
- // Process all file imports.
- for _, fileImport := range m.FileImports {
- newFile := filepath.Join(filepath.Dir(file), fileImport.File)
- if err := imp.Load(jirix, root, newFile, "", projects, tools); err != nil {
- return err
- }
- }
- // Process all projects.
- for _, project := range m.Projects {
- project.Path = filepath.Join(root, project.Path)
- projects[project.Key()] = project
- }
- // Process all tools.
- for _, tool := range m.Tools {
- tools[tool.Name] = tool
- }
- return nil
-}
-
-func (imp *importer) Update(jirix *jiri.X, root, file, key string, localProjects Projects) error {
- return imp.importNoCycles(file, key, func() error {
- return imp.update(jirix, root, file, localProjects)
- })
-}
-
-func (imp *importer) update(jirix *jiri.X, root, file string, localProjects Projects) error {
- m, err := ManifestFromFile(jirix, file)
- if err != nil {
- return err
- }
- // Process all remote imports. This logic treats the remote import as a
- // regular project, and runs our regular create/move/update logic on it. We
- // never handle deletes here; those are handled in updateProjects.
+ // Process remote imports.
for _, remote := range m.Imports {
if remote.Remote == "" {
- // Old-style local imports handled in loop below.
+ // Old-style named imports handled in loop below.
continue
}
- newRoot := filepath.Join(root, remote.Root)
- remote.Path = filepath.Join(newRoot, remote.Path)
- newFile := filepath.Join(remote.Path, remote.Manifest)
- var localProject *Project
- if p, ok := localProjects[remote.Project.Key()]; ok {
- localProject = &p
+ nextRoot, nextFile := filepath.Join(root, remote.Root), ""
+ key := remote.ProjectKey()
+ p, ok := ld.localProjects[key]
+ if !ok {
+ if !ld.update {
+ return fmt.Errorf("can't resolve remote import: project %q not found locally", key)
+ }
+ // The remote manifest project doesn't exist locally. Clone it into a
+ // temp directory, and add it to ld.localProjects.
+ if ld.TmpDir == "" {
+ if ld.TmpDir, err = jirix.NewSeq().TempDir("", "jiri-load"); err != nil {
+ return fmt.Errorf("TempDir() failed: %v", err)
+ }
+ }
+ path := filepath.Join(ld.TmpDir, remote.projectKeyFileName())
+ if p, err = remote.toProject(path); err != nil {
+ return err
+ }
+ if err := jirix.NewSeq().MkdirAll(path, 0755).Done(); err != nil {
+ return err
+ }
+ if err := gitutil.New(jirix.NewSeq()).Clone(p.Remote, path); err != nil {
+ return err
+ }
+ ld.localProjects[key] = p
}
- // Since &remote.Project is never nil, we'll never produce a delete op.
- op := computeOp(localProject, &remote.Project, false, newRoot)
- if err := op.Test(jirix, newFsUpdates()); err != nil {
- return err
+ // Reset the project to its specified branch and load the next file. Note
+ // that we call load() recursively, so multiple files may be loaded by
+ // resetAndLoad.
+ if strings.HasPrefix(p.Path, ld.TmpDir) {
+ nextFile = filepath.Join(p.Path, remote.Manifest)
+ } else {
+ nextFile = filepath.Join(jirix.Root, nextRoot, p.Path, remote.Manifest)
}
- updateFn := func() error { return op.Run(jirix, nil) }
- if err := jirix.NewSeq().Verbose(true).Call(updateFn, "%v", op).Done(); err != nil {
- fmt.Fprintf(jirix.Stderr(), "%v\n", err)
- return err
- }
- localProjects[remote.Project.Key()] = remote.Project
- if err := imp.Update(jirix, newRoot, newFile, remote.remoteKey(), localProjects); err != nil {
+ if err := ld.resetAndLoad(jirix, nextRoot, nextFile, remote.cycleKey(), p); err != nil {
return err
}
}
- // Process all old-style local imports.
- for _, local := range m.Imports {
- if local.Remote != "" {
+ // Process old-style named imports.
+ //
+ // TODO(toddw): Remove this logic when the manifest transition is done.
+ for _, named := range m.Imports {
+ if named.Remote != "" {
// New-style remote imports handled in loop above.
continue
}
- newFile, err := jirix.ResolveManifestPath(local.Name)
+ nextFile, err := jirix.ResolveManifestPath(named.Name)
if err != nil {
return err
}
- if err := imp.Update(jirix, root, newFile, "", localProjects); err != nil {
+ if err := ld.Load(jirix, root, nextFile, ""); err != nil {
return err
}
}
- // Process all file imports.
- for _, fileImport := range m.FileImports {
- newFile := filepath.Join(filepath.Dir(file), fileImport.File)
- if err := imp.Update(jirix, root, newFile, "", localProjects); err != nil {
+ // Process local imports.
+ for _, local := range m.LocalImports {
+ // TODO(toddw): Add our invariant check that the file is in the same
+ // repository as the current remote import repository.
+ nextFile := filepath.Join(filepath.Dir(file), local.File)
+ if err := ld.Load(jirix, root, nextFile, ""); err != nil {
return err
}
}
+ // Collect projects.
+ for _, project := range m.Projects {
+ project.Path = filepath.Join(jirix.Root, root, project.Path)
+ key := project.Key()
+ if dup, ok := ld.Projects[key]; ok && dup != project {
+ // TODO(toddw): Tell the user the other conflicting file.
+ return fmt.Errorf("duplicate project %q found in %v", key, shortFileName(jirix.Root, file))
+ }
+ ld.Projects[key] = project
+ }
+ // Collect tools.
+ for _, tool := range m.Tools {
+ name := tool.Name
+ if dup, ok := ld.Tools[name]; ok && dup != tool {
+ // TODO(toddw): Tell the user the other conflicting file.
+ return fmt.Errorf("duplicate tool %q found in %v", name, shortFileName(jirix.Root, file))
+ }
+ ld.Tools[name] = tool
+ }
return nil
}
+func (ld *loader) resetAndLoad(jirix *jiri.X, root, file, cycleKey string, project Project) (e error) {
+ // Change to the project.Path directory, and revert when done.
+ pushd := jirix.NewSeq().Pushd(project.Path)
+ defer collect.Error(pushd.Done, &e)
+ // Reset the local master branch to what's specified on the project. We only
+ // fetch on updates; non-updates just perform the reset.
+ //
+ // TODO(toddw): Support "jiri update -local=p1,p2" by simply calling ld.Load
+ // for the given projects, rather than ApplyToLocalMaster(fetch+reset+load).
+ return ApplyToLocalMaster(jirix, Projects{project.Key(): project}, func() error {
+ if ld.update {
+ if err := fetchProject(jirix, project); err != nil {
+ return err
+ }
+ }
+ if err := resetProjectCurrentBranch(jirix, project); err != nil {
+ return err
+ }
+ return ld.Load(jirix, root, file, cycleKey)
+ })
+}
+
// 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(jirix *jiri.X, project Project) (e error) {
@@ -1559,18 +1664,10 @@
}
}
-func updateProjects(jirix *jiri.X, remoteProjects Projects, gc bool) error {
+func updateProjects(jirix *jiri.X, localProjects, remoteProjects Projects, gc bool) error {
jirix.TimerPush("update projects")
defer jirix.TimerPop()
- scanMode := FastScan
- if gc {
- scanMode = FullScan
- }
- localProjects, err := LocalProjects(jirix, scanMode)
- if err != nil {
- return err
- }
getRemoteHeadRevisions(jirix, remoteProjects)
ops := computeOperations(localProjects, remoteProjects, gc, "")
updates := newFsUpdates()
@@ -1595,15 +1692,15 @@
return cmdline.ErrExitCode(2)
}
- // Run all RunHook scripts.
+ // Run all RunHook scripts, apply githooks, and write the current manifest.
+ // TODO(toddw): What's the significance of exit code 2?
if !runHooks(jirix, ops) {
return cmdline.ErrExitCode(2)
}
-
- if err := writeCurrentManifest(jirix, manifest); err != nil {
- return err
+ if err := applyGitHooks(jirix, ops); err != nil {
+ return cmdline.ErrExitCode(2)
}
- return nil
+ return writeCurrentManifest(jirix, manifest)
}
// runHooks runs all hooks for the given operations. It returns true iff all
@@ -1616,7 +1713,7 @@
if op.Project().RunHook == "" {
continue
}
- if op.Kind() != "create" && op.Kind() != "update" && op.Kind() != "move" {
+ if op.Kind() != "create" && op.Kind() != "move" && op.Kind() != "update" {
continue
}
s := jirix.NewSeq()
@@ -1633,6 +1730,63 @@
return ok
}
+func applyGitHooks(jirix *jiri.X, ops []operation) error {
+ jirix.TimerPush("apply githooks")
+ defer jirix.TimerPop()
+ s := jirix.NewSeq()
+ for _, op := range ops {
+ if op.Kind() == "create" || op.Kind() == "move" {
+ // Apply exclusion for /.jiri/. Ideally we'd only write this file on
+ // create, but the remote manifest import is move from the temp directory
+ // into the final spot, so we need this to apply to both.
+ //
+ // TODO(toddw): Find a better way to do this.
+ excludeDir := filepath.Join(op.Project().Path, ".git", "info")
+ excludeFile := filepath.Join(excludeDir, "exclude")
+ excludeString := "/.jiri/\n"
+ if err := s.MkdirAll(excludeDir, 0755).WriteFile(excludeFile, []byte(excludeString), 0644).Done(); err != nil {
+ return err
+ }
+ }
+ if op.Project().GitHooks == "" {
+ continue
+ }
+ if op.Kind() != "create" && op.Kind() != "move" && op.Kind() != "update" {
+ continue
+ }
+ // Apply git hooks, overwriting any existing hooks. Jiri is in control of
+ // writing all hooks.
+ gitHooksDstDir := filepath.Join(op.Project().Path, ".git", "hooks")
+ gitHooksSrcDir := filepath.Join(jirix.Root, op.Root(), op.Project().GitHooks)
+ // Copy the specified GitHooks directory into the project's git
+ // hook directory. We walk the file system, creating directories
+ // and copying files as we encounter them.
+ copyFn := func(path string, info os.FileInfo, err error) error {
+ if err != nil {
+ return err
+ }
+ relPath, err := filepath.Rel(gitHooksSrcDir, path)
+ if err != nil {
+ return err
+ }
+ dst := filepath.Join(gitHooksDstDir, relPath)
+ if info.IsDir() {
+ return s.MkdirAll(dst, 0755).Done()
+ }
+ src, err := s.ReadFile(path)
+ if err != nil {
+ return err
+ }
+ // The file *must* be executable to be picked up by git.
+ return s.WriteFile(dst, src, 0755).Done()
+ }
+ if err := filepath.Walk(gitHooksSrcDir, copyFn); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
// writeMetadata stores the given project metadata in the directory
// identified by the given path.
func writeMetadata(jirix *jiri.X, project Project, dir string) (e error) {
@@ -1783,50 +1937,6 @@
if err := gitutil.New(jirix.NewSeq()).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.
- gitHooksDstDir := filepath.Join(tmpDir, ".git", "hooks")
- if op.project.GitHooks != "" {
- gitHooksSrcDir := filepath.Join(jirix.Root, op.root, op.project.GitHooks)
- // Copy the specified GitHooks directory into the project's git
- // hook directory. We walk the file system, creating directories
- // and copying files as we encounter them.
- copyFn := func(path string, info os.FileInfo, err error) error {
- if err != nil {
- return err
- }
- relPath, err := filepath.Rel(gitHooksSrcDir, path)
- if err != nil {
- return err
- }
- dst := filepath.Join(gitHooksDstDir, relPath)
- if info.IsDir() {
- return s.MkdirAll(dst, perm).Done()
- }
- src, err := s.ReadFile(path)
- if err != nil {
- return err
- }
- return s.WriteFile(dst, src, perm).Done()
- }
- if err := filepath.Walk(gitHooksSrcDir, copyFn); 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")
- excludeFile := filepath.Join(excludeDir, "exclude")
- if err := s.MkdirAll(excludeDir, os.FileMode(0750)).
- WriteFile(excludeFile, []byte(excludeString), perm).Done(); err != nil {
- return err
- }
-
cwd, err := os.Getwd()
if err != nil {
return err
@@ -1835,6 +1945,7 @@
if err := s.Chdir(tmpDir).Done(); err != nil {
return err
}
+ // TODO(toddw): Why call Reset here, when resetProject is called just below?
if err := gitutil.New(jirix.NewSeq()).Reset(op.project.Revision); err != nil {
return err
}
@@ -1848,7 +1959,7 @@
Rename(tmpDir, op.destination).Done(); err != nil {
return err
}
- if err := resetProject(jirix, op.project); err != nil {
+ if err := syncProjectMaster(jirix, op.project); err != nil {
return err
}
return addProjectToManifest(jirix, manifest, op.project)
@@ -1952,7 +2063,7 @@
if err := reportNonMaster(jirix, op.project); err != nil {
return err
}
- if err := resetProject(jirix, op.project); err != nil {
+ if err := syncProjectMaster(jirix, op.project); err != nil {
return err
}
if err := writeMetadata(jirix, op.project, op.project.Path); err != nil {
@@ -1996,7 +2107,7 @@
if err := reportNonMaster(jirix, op.project); err != nil {
return err
}
- if err := resetProject(jirix, op.project); err != nil {
+ if err := syncProjectMaster(jirix, op.project); err != nil {
return err
}
if err := writeMetadata(jirix, op.project, op.project.Path); err != nil {
@@ -2112,29 +2223,11 @@
func computeOp(local, remote *Project, gc bool, root string) operation {
switch {
- case local != nil && remote != nil:
- if local.Path != remote.Path {
- // moveOperation also does an update, so we don't need to check the
- // revision here.
- return moveOperation{commonOperation{
- destination: remote.Path,
- project: *remote,
- source: local.Path,
- root: root,
- }}
- }
- if local.Revision != remote.Revision {
- return updateOperation{commonOperation{
- destination: remote.Path,
- project: *remote,
- source: local.Path,
- root: root,
- }}
- }
- return nullOperation{commonOperation{
+ case local == nil && remote != nil:
+ return createOperation{commonOperation{
destination: remote.Path,
project: *remote,
- source: local.Path,
+ source: "",
root: root,
}}
case local != nil && remote == nil:
@@ -2144,13 +2237,32 @@
source: local.Path,
root: root,
}, gc}
- case local == nil && remote != nil:
- return createOperation{commonOperation{
- destination: remote.Path,
- project: *remote,
- source: "",
- root: root,
- }}
+ case local != nil && remote != nil:
+ switch {
+ case local.Path != remote.Path:
+ // moveOperation also does an update, so we don't need to check the
+ // revision here.
+ return moveOperation{commonOperation{
+ destination: remote.Path,
+ project: *remote,
+ source: local.Path,
+ root: root,
+ }}
+ case local.Revision != remote.Revision:
+ return updateOperation{commonOperation{
+ destination: remote.Path,
+ project: *remote,
+ source: local.Path,
+ root: root,
+ }}
+ default:
+ return nullOperation{commonOperation{
+ destination: remote.Path,
+ project: *remote,
+ source: local.Path,
+ root: root,
+ }}
+ }
default:
panic("jiri: computeOp called with nil local and remote")
}
@@ -2159,7 +2271,7 @@
// 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{}) (Projects, error) {
- manifestProjects, _, err := ReadJiriManifest(jirix)
+ manifestProjects, _, err := LoadManifest(jirix)
if err != nil {
return nil, err
}
diff --git a/project/project_test.go b/project/project_test.go
index 8db3887..f19cc97 100644
--- a/project/project_test.go
+++ b/project/project_test.go
@@ -579,17 +579,17 @@
// Set up the cycle .jiri_manifest -> A -> B -> A
jiriManifest := project.Manifest{
- FileImports: []project.FileImport{
+ LocalImports: []project.LocalImport{
{File: "A"},
},
}
manifestA := project.Manifest{
- FileImports: []project.FileImport{
+ LocalImports: []project.LocalImport{
{File: "B"},
},
}
manifestB := project.Manifest{
- FileImports: []project.FileImport{
+ LocalImports: []project.LocalImport{
{File: "A"},
},
}
@@ -623,23 +623,17 @@
// Set up the cycle .jiri_manifest -> remote1+A -> remote2+B -> remote1+A
jiriManifest := project.Manifest{
Imports: []project.Import{
- {Manifest: "A", Project: project.Project{
- Name: "n1", Path: "p1", Remote: remote1,
- }},
+ {Manifest: "A", Name: "n1", Remote: remote1},
},
}
manifestA := project.Manifest{
Imports: []project.Import{
- {Manifest: "B", Project: project.Project{
- Name: "n2", Path: "p2", Remote: remote2,
- }},
+ {Manifest: "B", Name: "n2", Remote: remote2},
},
}
manifestB := project.Manifest{
Imports: []project.Import{
- {Manifest: "A", Project: project.Project{
- Name: "n3", Path: "p3", Remote: remote1,
- }},
+ {Manifest: "A", Name: "n3", Remote: remote1},
},
}
if err := jiriManifest.ToFile(jirix, jirix.JiriManifestFile()); err != nil {
@@ -675,32 +669,26 @@
// Set up the cycle .jiri_manifest -> remote1+A -> remote2+B -> C -> remote1+D -> A
jiriManifest := project.Manifest{
Imports: []project.Import{
- {Manifest: "A", Root: "r1", Project: project.Project{
- Name: "n1", Path: "p1", Remote: remote1,
- }},
+ {Manifest: "A", Root: "r1", Name: "n1", Remote: remote1},
},
}
manifestA := project.Manifest{
Imports: []project.Import{
- {Manifest: "B", Root: "r2", Project: project.Project{
- Name: "n2", Path: "p2", Remote: remote2,
- }},
+ {Manifest: "B", Root: "r2", Name: "n2", Remote: remote2},
},
}
manifestB := project.Manifest{
- FileImports: []project.FileImport{
+ LocalImports: []project.LocalImport{
{File: "C"},
},
}
manifestC := project.Manifest{
Imports: []project.Import{
- {Manifest: "D", Root: "r3", Project: project.Project{
- Name: "n3", Path: "p3", Remote: remote1,
- }},
+ {Manifest: "D", Root: "r3", Name: "n3", Remote: remote1},
},
}
manifestD := project.Manifest{
- FileImports: []project.FileImport{
+ LocalImports: []project.LocalImport{
{File: "A"},
},
}
@@ -928,31 +916,45 @@
Label: "label",
Imports: []project.Import{
{
- Manifest: "manifest",
- Project: project.Project{
- Path: "manifest",
- Protocol: "git",
- Remote: "remote",
- RemoteBranch: "master",
- Revision: "HEAD",
- },
+ Manifest: "manifest1",
+ Name: "remoteimport1",
+ Protocol: "git",
+ Remote: "remote1",
+ RemoteBranch: "master",
},
- {Project: project.Project{Name: "localimport"}},
+ {
+ Manifest: "manifest2",
+ Name: "remoteimport2",
+ Protocol: "git",
+ Remote: "remote2",
+ RemoteBranch: "branch2",
+ },
+ {
+ Name: "oldimport",
+ },
},
- FileImports: []project.FileImport{
+ LocalImports: []project.LocalImport{
{File: "fileimport"},
},
Projects: []project.Project{
{
+ Name: "project1",
+ Path: "path1",
+ Protocol: "git",
+ Remote: "remote1",
+ RemoteBranch: "master",
+ Revision: "HEAD",
GerritHost: "https://test-review.googlesource.com",
GitHooks: "path/to/githooks",
RunHook: "path/to/hook",
- Name: "project",
- Path: "path",
+ },
+ {
+ Name: "project2",
+ Path: "path2",
Protocol: "git",
- Remote: "remote",
- RemoteBranch: "otherbranch",
- Revision: "rev",
+ Remote: "remote2",
+ RemoteBranch: "branch2",
+ Revision: "rev2",
},
},
Tools: []project.Tool{
@@ -965,12 +967,14 @@
},
`<manifest label="label">
<imports>
- <import manifest="manifest" remote="remote"/>
- <import name="localimport"/>
- <fileimport file="fileimport"/>
+ <import manifest="manifest1" name="remoteimport1" remote="remote1"/>
+ <import manifest="manifest2" name="remoteimport2" remote="remote2" remotebranch="branch2"/>
+ <import name="oldimport"/>
+ <localimport file="fileimport"/>
</imports>
<projects>
- <project name="project" path="path" remote="remote" remotebranch="otherbranch" revision="rev" gerrithost="https://test-review.googlesource.com" githooks="path/to/githooks" runhook="path/to/hook"/>
+ <project name="project1" path="path1" remote="remote1" gerrithost="https://test-review.googlesource.com" githooks="path/to/githooks" runhook="path/to/hook"/>
+ <project name="project2" path="path2" remote="remote2" remotebranch="branch2" revision="rev2"/>
</projects>
<tools>
<tool data="tooldata" name="tool" project="toolproject"/>
@@ -1008,25 +1012,27 @@
{
// Default fields are dropped when marshaled, and added when unmarshaled.
project.Project{
- Name: "project",
- Path: "path",
+ Name: "project1",
+ Path: "path1",
Protocol: "git",
- Remote: "remote",
+ Remote: "remote1",
RemoteBranch: "master",
Revision: "HEAD",
},
- `<project name="project" path="path" remote="remote"/>`,
+ `<project name="project1" path="path1" remote="remote1"/>
+`,
},
{
project.Project{
- Name: "project",
- Path: "path",
+ Name: "project2",
+ Path: "path2",
Protocol: "git",
- Remote: "remote",
- RemoteBranch: "otherbranch",
- Revision: "rev",
+ Remote: "remote2",
+ RemoteBranch: "branch2",
+ Revision: "rev2",
},
- `<project name="project" path="path" remote="remote" remotebranch="otherbranch" revision="rev"/>`,
+ `<project name="project2" path="path2" remote="remote2" remotebranch="branch2" revision="rev2"/>
+`,
},
}
for index, test := range tests {
@@ -1059,6 +1065,7 @@
XML string
Project project.Project
}{
+ // Make sure <Project> opening tag is accepted.
{
`<Project name="project" path="path" remote="remote"/>`,
project.Project{
@@ -1070,6 +1077,7 @@
Revision: "HEAD",
},
},
+ // Make sure <Project> opening and closing tags are accepted.
{
`<Project name="project" path="path" remote="remote"></Project>`,
project.Project{
@@ -1081,14 +1089,15 @@
Revision: "HEAD",
},
},
+ // Make sure "this_attribute_should_be_ignored" is silently ignored.
{
- `<Project this_attribute_should_be_ignored="junk" name="project" path="path" remote="remote" remotebranch="otherbranch" revision="rev"></Project>`,
+ `<Project this_attribute_should_be_ignored="junk" name="project" path="path" remote="remote" remotebranch="branch" revision="rev"></Project>`,
project.Project{
Name: "project",
Path: "path",
Protocol: "git",
Remote: "remote",
- RemoteBranch: "otherbranch",
+ RemoteBranch: "branch",
Revision: "rev",
},
},
@@ -1107,64 +1116,3 @@
}
}
}
-
-func TestImportToFile(t *testing.T) {
- jirix, cleanup := jiritest.NewX(t)
- defer cleanup()
-
- tests := []struct {
- Import project.Import
- XML string
- }{
- {
- project.Import{
- Project: project.Project{
- Name: "import",
- },
- },
- `<import name="import"/>`,
- },
- {
- // Default fields are dropped when marshaled, and added when unmarshaled.
- project.Import{
- Manifest: "manifest",
- Project: project.Project{
- Name: "import",
- Path: "manifest",
- Protocol: "git",
- Remote: "remote",
- RemoteBranch: "master",
- Revision: "HEAD",
- },
- },
- `<import manifest="manifest" name="import" remote="remote"/>`,
- },
- {
- project.Import{
- Manifest: "manifest",
- Project: project.Project{
- Name: "import",
- Path: "path",
- Protocol: "git",
- Remote: "remote",
- RemoteBranch: "otherbranch",
- Revision: "rev",
- },
- },
- `<import manifest="manifest" name="import" path="path" remote="remote" remotebranch="otherbranch" revision="rev"/>`,
- },
- }
- for index, test := range tests {
- filename := filepath.Join(jirix.Root, fmt.Sprintf("test-%d", index))
- if err := test.Import.ToFile(jirix, filename); err != nil {
- t.Errorf("%+v ToFile failed: %v", test.Import, err)
- }
- gotBytes, err := jirix.NewSeq().ReadFile(filename)
- if err != nil {
- t.Errorf("%+v ReadFile failed: %v", test.Import, err)
- }
- if got, want := string(gotBytes), test.XML; got != want {
- t.Errorf("%+v ToFile GOT\n%v\nWANT\n%v", test.Import, got, want)
- }
- }
-}
diff --git a/rebuild.go b/rebuild.go
index 3f68b9a..984ee35 100644
--- a/rebuild.go
+++ b/rebuild.go
@@ -29,7 +29,7 @@
}
func runRebuild(jirix *jiri.X, args []string) (e error) {
- _, tools, err := project.ReadJiriManifest(jirix)
+ _, tools, err := project.LoadManifest(jirix)
if err != nil {
return err
}
diff --git a/snapshot.go b/snapshot.go
index 168598f..b0e6262 100644
--- a/snapshot.go
+++ b/snapshot.go
@@ -116,9 +116,10 @@
// Execute the above function in the snapshot directory on a clean master branch.
p := project.Project{
- Path: snapshotDir,
- Protocol: "git",
- Revision: "HEAD",
+ Path: snapshotDir,
+ Protocol: "git",
+ RemoteBranch: "master",
+ Revision: "HEAD",
}
return project.ApplyToLocalMaster(jirix, project.Projects{p.Key(): p}, createFn)
}
diff --git a/upgrade.go b/upgrade.go
index 1209c4f..382e18e 100644
--- a/upgrade.go
+++ b/upgrade.go
@@ -82,65 +82,56 @@
return jirix.UsageErrorf("must specify upgrade kind")
}
kind := args[0]
- var argRemote, argRoot, argPath, argManifest string
+ var argRemote, argName, argManifest string
switch kind {
case "v23":
argRemote = "https://vanadium.googlesource.com/manifest"
- argRoot, argPath, argManifest = "", "manifest", "public"
+ argName, argManifest = "manifest", "public"
case "fuchsia":
- // TODO(toddw): Confirm these choices.
argRemote = "https://github.com/effenel/fnl-start.git"
- argRoot, argPath, argManifest = "", "manifest", "default"
+ argName, argManifest = "fnl-start", "manifest/fuchsia"
default:
return jirix.UsageErrorf("unknown upgrade kind %q", kind)
}
// Initialize manifest from .local_manifest.
- hasLocalFile := false
+ hasLocalFile := true
manifest, err := project.ManifestFromFile(jirix, localFile)
- switch {
- case err != nil && !runutil.IsNotExist(err):
- return err
- case err == nil:
- hasLocalFile = true
- seenOldImport := false
- var newImports []project.Import
- for _, oldImport := range manifest.Imports {
- if oldImport.Remote != "" {
- // This is a new-style remote import, carry it over directly.
- newImports = append(newImports, oldImport)
- continue
- }
- // This is an old-style file import, convert it to the new style.
- oldName := oldImport.Name
- if kind == "v23" && oldName == "default" {
- oldName = "public" // default no longer exists, now it's just public.
- }
- if !seenOldImport {
- // This is the first old import, update the manifest name for the remote
- // import we'll be adding later.
- argManifest = oldName
- seenOldImport = true
- } else {
- // Convert import from name="foo" to file="manifest/foo"
- manifest.FileImports = append(manifest.FileImports, project.FileImport{
- File: filepath.Join(argRoot, argPath, oldName),
- })
- }
+ if err != nil {
+ if !runutil.IsNotExist(err) {
+ return err
}
- manifest.Imports = newImports
- }
- if manifest == nil {
+ hasLocalFile = false
manifest = &project.Manifest{}
}
- // Add remote import.
- manifest.Imports = append(manifest.Imports, project.Import{
- Manifest: argManifest,
- Root: argRoot,
- Project: project.Project{
- Path: argPath,
- Remote: argRemote,
- },
- })
+ oldImports := manifest.Imports
+ manifest.Imports = nil
+ for _, oldImport := range oldImports {
+ if oldImport.Remote != "" {
+ // This is a new-style remote import, carry it over directly.
+ manifest.Imports = append(manifest.Imports, oldImport)
+ continue
+ }
+ // This is an old-style import, convert it to the new style.
+ oldName := oldImport.Name
+ switch {
+ case kind == "v23" && oldName == "default":
+ oldName = "public"
+ case kind == "fuchsia" && oldName == "default":
+ oldName = "manifest/fuchsia"
+ }
+ manifest.Imports = append(manifest.Imports, project.Import{
+ Manifest: oldName,
+ Name: argName,
+ Remote: argRemote,
+ })
+ }
+ if len(manifest.Imports) == 0 {
+ manifest.Imports = append(manifest.Imports, project.Import{
+ Manifest: argManifest,
+ Name: argName,
+ Remote: argRemote,
+ })
+ }
// Write output to .jiri_manifest file.
outFile := jirix.JiriManifestFile()
if _, err := os.Stat(outFile); err == nil {
diff --git a/upgrade_test.go b/upgrade_test.go
index f9e5733..4361c1b 100644
--- a/upgrade_test.go
+++ b/upgrade_test.go
@@ -41,7 +41,7 @@
Args: []string{"v23"},
Want: `<manifest>
<imports>
- <import manifest="public" remote="https://vanadium.googlesource.com/manifest"/>
+ <import manifest="public" name="manifest" remote="https://vanadium.googlesource.com/manifest"/>
</imports>
</manifest>
`,
@@ -56,7 +56,7 @@
`,
Want: `<manifest>
<imports>
- <import manifest="public" remote="https://vanadium.googlesource.com/manifest"/>
+ <import manifest="public" name="manifest" remote="https://vanadium.googlesource.com/manifest"/>
</imports>
</manifest>
`,
@@ -71,7 +71,7 @@
`,
Want: `<manifest>
<imports>
- <import manifest="private" remote="https://vanadium.googlesource.com/manifest"/>
+ <import manifest="private" name="manifest" remote="https://vanadium.googlesource.com/manifest"/>
</imports>
</manifest>
`,
@@ -88,9 +88,9 @@
`,
Want: `<manifest>
<imports>
- <import manifest="private" remote="https://vanadium.googlesource.com/manifest"/>
- <fileimport file="manifest/infrastructure"/>
- <fileimport file="manifest/public"/>
+ <import manifest="private" name="manifest" remote="https://vanadium.googlesource.com/manifest"/>
+ <import manifest="infrastructure" name="manifest" remote="https://vanadium.googlesource.com/manifest"/>
+ <import manifest="public" name="manifest" remote="https://vanadium.googlesource.com/manifest"/>
</imports>
</manifest>
`,
@@ -107,9 +107,9 @@
`,
Want: `<manifest>
<imports>
- <import manifest="public" remote="https://vanadium.googlesource.com/manifest"/>
- <fileimport file="manifest/infrastructure"/>
- <fileimport file="manifest/private"/>
+ <import manifest="public" name="manifest" remote="https://vanadium.googlesource.com/manifest"/>
+ <import manifest="infrastructure" name="manifest" remote="https://vanadium.googlesource.com/manifest"/>
+ <import manifest="private" name="manifest" remote="https://vanadium.googlesource.com/manifest"/>
</imports>
</manifest>
`,
@@ -124,7 +124,7 @@
Args: []string{"fuchsia"},
Want: `<manifest>
<imports>
- <import manifest="default" remote="https://github.com/effenel/fnl-start.git"/>
+ <import manifest="manifest/fuchsia" name="fnl-start" remote="https://github.com/effenel/fnl-start.git"/>
</imports>
</manifest>
`,
@@ -139,7 +139,7 @@
`,
Want: `<manifest>
<imports>
- <import manifest="default" remote="https://github.com/effenel/fnl-start.git"/>
+ <import manifest="manifest/fuchsia" name="fnl-start" remote="https://github.com/effenel/fnl-start.git"/>
</imports>
</manifest>
`,
@@ -154,7 +154,7 @@
`,
Want: `<manifest>
<imports>
- <import manifest="private" remote="https://github.com/effenel/fnl-start.git"/>
+ <import manifest="private" name="fnl-start" remote="https://github.com/effenel/fnl-start.git"/>
</imports>
</manifest>
`,
@@ -171,9 +171,9 @@
`,
Want: `<manifest>
<imports>
- <import manifest="private" remote="https://github.com/effenel/fnl-start.git"/>
- <fileimport file="manifest/infrastructure"/>
- <fileimport file="manifest/default"/>
+ <import manifest="private" name="fnl-start" remote="https://github.com/effenel/fnl-start.git"/>
+ <import manifest="infrastructure" name="fnl-start" remote="https://github.com/effenel/fnl-start.git"/>
+ <import manifest="manifest/fuchsia" name="fnl-start" remote="https://github.com/effenel/fnl-start.git"/>
</imports>
</manifest>
`,
@@ -190,9 +190,9 @@
`,
Want: `<manifest>
<imports>
- <import manifest="default" remote="https://github.com/effenel/fnl-start.git"/>
- <fileimport file="manifest/infrastructure"/>
- <fileimport file="manifest/private"/>
+ <import manifest="manifest/fuchsia" name="fnl-start" remote="https://github.com/effenel/fnl-start.git"/>
+ <import manifest="infrastructure" name="fnl-start" remote="https://github.com/effenel/fnl-start.git"/>
+ <import manifest="private" name="fnl-start" remote="https://github.com/effenel/fnl-start.git"/>
</imports>
</manifest>
`,
@@ -210,6 +210,7 @@
}
func testUpgrade(opts gosh.Opts, jiriTool string, test upgradeTestCase) error {
+ opts.PropagateChildOutput = true
sh := gosh.NewShell(opts)
defer sh.Cleanup()
jiriRoot := sh.MakeTempDir()
@@ -267,7 +268,7 @@
localData := `<manifest/>`
jiriData := `<manifest>
<imports>
- <import manifest="public" remote="https://vanadium.googlesource.com/manifest"/>
+ <import manifest="public" name="manifest" remote="https://vanadium.googlesource.com/manifest"/>
</imports>
</manifest>
`