Add support for <overrides>
The first manifest file (e.g., .jiri_manifest) can contain an
<overrides> tag that replaces project definitions from the <projects>
tag (including from transitively imported manifests.
IN-682 #done
Change-Id: I8e97e329333355eff2a34851a64917e91f2cc487
diff --git a/manifest.md b/manifest.md
index 2d9a7d6..f3581d0 100644
--- a/manifest.md
+++ b/manifest.md
@@ -2,7 +2,8 @@
Jiri manifest files describe the set of projects that get synced when running "jiri update".
-The first manifest file that jiri reads is in [root]/.jiri\_manifest. This manifest **must** exist for the jiri tool to work.
+The first manifest file that jiri reads is in [root]/.jiri\_manifest. This root manifest
+**must** exist for the jiri tool to work.
Usually the manifest in [root]/.jiri\_manifest will import other manifests from remote repositories via <import> tags, but it can contain its own list of projects as well.
@@ -29,6 +30,9 @@
/>
...
</projects>
+ <overrides>
+ <project ... />
+ </overrides>
<hooks>
<hook name="update"
project="mojo/public"
@@ -70,6 +74,10 @@
* githooks (optional) - The path (relative to [root]) of a directory containing git hooks that will be installed in the projects .git/hooks directory during each update.
+The projects in the <overrides> tag replace existing projects defined by in the <projects> tag (and from transitively imported lt;projects> tags).
+Only the root manifest can contain overrides and repositories referenced using the
+<import> tag (including from transitive imports) cannot be overridden.
+
The <hook> tag describes the hooks that must be executed after every 'jiri update' They are configured via the following attributes:
* name (required) - The name of the of the hook to identify it
diff --git a/project/loader.go b/project/loader.go
index e2f916c..dd24649 100644
--- a/project/loader.go
+++ b/project/loader.go
@@ -308,6 +308,28 @@
ld.Projects[key] = project
}
+ // Apply overrides.
+ if parentImport == "" {
+ for _, override := range m.Overrides {
+ // Make paths absolute by prepending <root>.
+ override.absolutizePaths(filepath.Join(jirix.Root, root))
+ override.Name = filepath.Join(root, override.Name)
+ key := override.Key()
+
+ if _, ok := ld.importProjects[key]; ok {
+ return fmt.Errorf("cannot override project %q because the project contains an imported manifest", key)
+ }
+
+ if _, ok := ld.Projects[key]; !ok {
+ return fmt.Errorf("failed to override %q found in %q. Original project not found in manifest", key, shortFileName(jirix.Root, repoPath, file, ref))
+ }
+
+ ld.Projects[key] = override
+ }
+ } else if len(m.Overrides) != 0 {
+ return fmt.Errorf("manifest %q contains overrides but was imported by %q. Overrides are allowed only in the root manifest.", shortFileName(jirix.Root, repoPath, file, ref), parentImport)
+ }
+
for _, hook := range m.Hooks {
if hook.ActionPath == "" {
return fmt.Errorf("invalid hook %q for project %q. Please make sure you are importing project %q and this hook is in the manifest which directly/indirectly imports that project.", hook.Name, hook.ProjectName, hook.ProjectName)
diff --git a/project/manifest.go b/project/manifest.go
index ade2c96..d86a482 100644
--- a/project/manifest.go
+++ b/project/manifest.go
@@ -30,6 +30,7 @@
Imports []Import `xml:"imports>import"`
LocalImports []LocalImport `xml:"imports>localimport"`
Projects []Project `xml:"projects>project"`
+ Overrides []Project `xml:"overrides>project"`
Hooks []Hook `xml:"hooks>hook"`
XMLName struct{} `xml:"manifest"`
}
@@ -73,6 +74,7 @@
newlineBytes = []byte("\n")
emptyImportsBytes = []byte("\n <imports></imports>\n")
emptyProjectsBytes = []byte("\n <projects></projects>\n")
+ emptyOverridesBytes = []byte("\n <overrides></overrides>\n")
emptyHooksBytes = []byte("\n <hooks></hooks>\n")
endElemBytes = []byte("/>\n")
@@ -92,6 +94,7 @@
x.Imports = append([]Import(nil), m.Imports...)
x.LocalImports = append([]LocalImport(nil), m.LocalImports...)
x.Projects = append([]Project(nil), m.Projects...)
+ x.Overrides = append([]Project(nil), m.Overrides...)
x.Hooks = append([]Hook(nil), m.Hooks...)
return x
}
@@ -110,6 +113,7 @@
// elements, or produce short empty elements, so we post-process the data.
data = bytes.Replace(data, emptyImportsBytes, newlineBytes, -1)
data = bytes.Replace(data, emptyProjectsBytes, newlineBytes, -1)
+ data = bytes.Replace(data, emptyOverridesBytes, newlineBytes, -1)
data = bytes.Replace(data, emptyHooksBytes, newlineBytes, -1)
data = bytes.Replace(data, endImportBytes, endElemBytes, -1)
data = bytes.Replace(data, endLocalImportBytes, endElemBytes, -1)
@@ -163,6 +167,11 @@
return err
}
}
+ for index := range m.Overrides {
+ if err := m.Overrides[index].fillDefaults(); err != nil {
+ return err
+ }
+ }
return nil
}
@@ -182,6 +191,11 @@
return err
}
}
+ for index := range m.Overrides {
+ if err := m.Overrides[index].unfillDefaults(); err != nil {
+ return err
+ }
+ }
return nil
}