Merge "jiri: Use lookpath package consistently."
diff --git a/README.md b/README.md
index 2123c0a..29336ce 100644
--- a/README.md
+++ b/README.md
@@ -9,8 +9,72 @@
* restoring local project state from a snapshot, and
* facilitating sending change lists to [Gerrit][gerrit].
+Jiri has an extensible plugin model, making it easy to create new sub-commands.
+
Jiri is open-source. See the contributor guidelines [here][contributing].
+## Jiri Basics
+Jiri organizes a set of repositories on your local filesystem according to a
+[manifest][manifests]. These repositories are referred to as "projects", and
+are all contained within a single directory called the "jiri root" which is
+assumed to be set in the `JIRI_ROOT` environment variable.
+
+The manifest file specifies the relative location of each project within the
+jiri root, and also includes other metadata about the project such as its
+remote url, the remote branch it should track, and more.
+
+The `jiri update` command syncs the master branch of all local projects to the
+revision and remote branch specified in the manifest for each project. Jiri
+will create the project locally if it does not exist, and if run with the `-gc`
+flag, jiri will "garbage collect" any projects that are not listed in the
+manifest by deleting them locally.
+
+The `.jiri_manifest` file in the jiri root describes which project jiri should
+sync. Typically the `.jiri_manifest` file will import other manifests, but it
+can also contain a list of projects.
+
+For example, here is a simple `.jiri_manifest` with just two projects, "foo"
+and "bar", which are hosted on github and bitbucket respectively.
+```
+<?xml version="1.0" encoding="UTF-8"?>
+<manifest>
+ <projects>
+ <project name="foo-project"
+ remote="https://github.com/my-org/foo"
+ path="foo"/>
+ <project name="bar"
+ remote="https://bitbucket.com/other-org/bar"
+ path="bar"/>
+ </projects>
+</manifest>
+```
+When you run `jiri update` for the first time, the "foo" and "bar" repos will
+be cloned into `$JIRI_ROOT/foo` and `$JIRI_ROOT/bar` respectively. Running
+`jiri update` again will sync the master branch of these repos with the remote
+master branch.
+
+Note that the project paths do not need to be immediate children of the jiri
+root. We could have decided to set the `path` attribute for the "bar" project
+to "third_party/bar", or even nest "bar" inside the "foo" project by setting
+the `path` to "foo/bar" (assuming no files in the foo repo conflict with bar).
+
+Because manifest files also need to be kept in sync between various team
+members, it often makes sense to keep your team's manifests in a version
+controlled repository.
+
+Jiri makes it easy to "import" a remote manifest from your local
+`.jiri_manifest` file with the `jiri import` command. For example, running the
+following command will create a `.jiri_manifest` file (or append to an existing
+one) with an `import` tag that imports the minimal manifest from the
+https://github.com/vanadium/manifest repo.
+
+ ```
+ jiri import -name="manifest" minimal https://github.com/vanadium/manifest
+```
+
+The next time you run `jiri update`, jiri will sync all projects listed in the
+Vanadium minimal manifest.
+
## Quickstart
This section explains how to get started with jiri.
@@ -169,17 +233,17 @@
## Filesystem
-TODO(nlacasse): There's a pretty good description of the filesystem layout at
-`jiri help filesystem`. Do we want to keep those docs there, or move them
-here? Maybe figure out a way to include them in both places but only have a
-single copy in source control.
+<!-- TODO(nlacasse): Figure out a way to keep the canonical documentation in
+one place but mirror it to the README and cmdline docs. -->
+
+See the jiri [filesystem godocs](https://godoc.org/v.io/jiri/cmd/jiri#hdr-Jiri_filesystem___Description_of_jiri_file_system_layout).
## Manifests<a name="manifests"></a>
-TODO(nlacasse): There's a brief description of manifests in `jiri help
-manifest`, but it needs a lot of improvement. Do we want to keep those docs
-there, or move them here? Maybe figure out a way to include them in both
-places but only have a single copy in source control.
+<!-- TODO(nlacasse): Figure out a way to keep the canonical documentation in
+one place but mirror it to the README and cmdline docs. -->
+
+See the jiri [manifest godocs](https://godoc.org/v.io/jiri/cmd/jiri#hdr-Jiri_manifest___Description_of_manifest_files).
## Snapshots
@@ -220,10 +284,10 @@
the remote master via the Gerrit code review system. The change can then be
merged into the local master branch with `jiri update`.
-TODO(nlacasse): dje is changing this behavior. The plan is that "master" will
-be the default reserved branch for each repo, but that can be overridden with
-the `localbranch` attribute in the manifest. Update this section once this
-change lands.
+<!-- TODO(nlacasse): dje is changing this behavior. The plan is that "master"
+will be the default reserved branch for each repo, but that can be overridden
+with the `localbranch` attribute in the manifest. Update this section once
+this change lands. -->
### Creating a new CL
@@ -343,9 +407,39 @@
has not been merged into master yet. For this reason, we recommend using `jiri
cl cleanup` to delete the feature branch safely.
-## FAQ
+### Dependent CLs
+If you have changes A and B, and B depends on A, you can still submit distinct
+CLs for A and B that can be reviewed and submitted independently (although A
+must be submitted before B).
-TODO(nlacasse): Answer these.
+First, create your feature branch for A, make your change, and upload the CL
+for review according to the instructions above.
+
+Then, while still on the feature branch for A, create your feature branch for B.
+```
+jiri cl new feature-B
+```
+Then make your change and upload the CL for review according to the
+instructions above.
+
+You can respond to review comments by submitting new patch sets as normal.
+
+After the CL for A has been submitted, make sure to clean up A's feature branch
+and upload a new patch set for feature B.
+```
+jiri update # fetch update that includes feature A
+git checkout feature-B
+jiri cl cleanup feature-A
+git merge master # merge feature A into feature B branch
+jiri cl mail # send new patch set for feature B
+```
+The CL for feature B can now be submitted.
+
+This process can be extended for more than 2 CLs. You must keep two things in mind:
+* always create the dependent feature branch with `jiri cl new` from the parent feature branch, and
+* after a parent feature has been submitted, cleanup the parent feature branch with `jiri cl cleanup`, and merge master into all dependent CLs and upload new patch sets.
+
+## FAQ
### Why the name "jiri"?
[Jiří][jiri-wiki] is a very popular boys name in the Czech Republic.
@@ -355,11 +449,16 @@
The actual Czech name [Jiří][jiri-wiki] is pronounced something like "yirzhee".
-### Why not repo/gclient/etc?
-
### Why can't I commit to my master branch?
+Jiri keeps the master branch of each project in the state described in the
+manifest. Any changes that are made to the master branch would be lost during
+the next `jiri update`.
+
+<!-- TODO(nlacasse): Answer these.
+### Why not repo/gclient/etc?
### How can I test changes to a manifest without pushing it upstream?
+-->
[android repo]: https://source.android.com/source/using-repo.html "Repo command reference"
[bootstrap_jiri]: scripts/bootstrap_jiri "bootstrap_jiri"
diff --git a/cmd/jiri/cmd.go b/cmd/jiri/cmd.go
index 0d4d2ee..a5d8ab5 100644
--- a/cmd/jiri/cmd.go
+++ b/cmd/jiri/cmd.go
@@ -111,48 +111,123 @@
logic. This is the reason the shim script is recommended over running the
binary directly.
-The binary is located at [root]/.jiri_root/bin/jiri
+The jiri binary is located at [root]/.jiri_root/bin/jiri
`,
}
-// TODO(toddw): Update the description of manifest files.
var topicManifest = cmdline.Topic{
Name: "manifest",
Short: "Description of manifest files",
Long: `
-Jiri manifests are revisioned and stored in a "manifest" repository, that is
-available locally in $JIRI_ROOT/.manifest. The manifest uses the following XML
-schema:
+Jiri manifest files describe the set of projects that get synced and tools that
+get built when running "jiri update".
- <manifest>
- <imports>
- <import name="default"/>
- ...
- </imports>
- <projects>
- <project name="release.go.jiri"
- path="release/go/src/v.io/jiri"
- protocol="git"
- name="https://vanadium.googlesource.com/release.go.jiri"
- revision="HEAD"/>
- ...
- </projects>
- <tools>
- <tool name="jiri" package="v.io/jiri"/>
- ...
- </tools>
- </manifest>
+The first manifest file that jiri reads is in $JIRI_ROOT/.jiri_manifest. This
+manifest **must** exist for the jiri tool to work.
-The <import> element can be used to share settings across multiple
-manifests. Import names are interpreted relative to the $JIRI_ROOT/.manifest/v2
-directory. Import cycles are not allowed and if a project or a tool is specified
-multiple times, the last specification takes effect. In particular, the elements
-<project name="foo" exclude="true"/> and <tool name="bar" exclude="true"/> can
-be used to exclude previously included projects and tools.
+Usually the manifest in $JIRI_ROOT/.jiri_manifest will import other manifests
+from remote repositories via <import> tags, but it can contain its own list of
+projects and tools as well.
-The tool identifies which manifest to use using the following algorithm. If the
-$JIRI_ROOT/.local_manifest file exists, then it is used. Otherwise, the
-$JIRI_ROOT/.manifest/v2/<manifest>.xml file is used, where <manifest> is the
-value of the -manifest command-line flag, which defaults to "default".
+Manifests have the following XML schema:
+
+<manifest>
+ <imports>
+ <import remote="https://vanadium.googlesource.com/manifest"
+ manifest="public"
+ name="manifest"
+ />
+ <localimport file="/path/to/local/manifest"/>
+ ...
+ </imports>
+ <projects>
+ <project name="my-project"
+ path="path/where/project/lives"
+ protocol="git"
+ remote="https://github.com/myorg/foo"
+ revision="ed42c05d8688ab23"
+ remotebranch="my-branch"
+ gerrithost="https://myorg-review.googlesource.com"
+ githooks="path/to/githooks-dir"
+ runhook="path/to/runhook-script"
+ />
+ ...
+ </projects>
+ <tools>
+ <tool name="jiri"
+ package="v.io/jiri"
+ project="release.go.jiri"
+ />
+ ...
+ </tools>
+</manifest>
+
+The <import> and <localimport> tags can be used to share common projects and
+tools across multiple manifests.
+
+A <localimport> tag should be used when the manifest being imported and the
+importing manifest are both in the same repository, or when neither one is in a
+repository. The "file" attribute is the path to the manifest file being
+imported. It can be absolute, or relative to the importing manifest file.
+
+If the manifest being imported and the importing manifest are in different
+repositories then an <import> tag must be used, with the following attributes:
+
+* remote (required) - The remote url of the repository containing the
+manifest to be imported
+
+* manifest (required) - The path of the manifest file to be imported,
+relative to the repository root.
+
+* name (optional) - The name of the project corresponding to the manifest
+repository. If your manifest contains a <project> with the same remote as
+the manifest remote, then the "name" attribute of on the <import> tag should
+match the "name" attribute on the <project>. Otherwise, jiri will clone the
+manifest repository on every update.
+
+The <project> tags describe the projects to sync, and what state they should
+sync to, accoring to the following attributes:
+
+* name (required) - The name of the project.
+
+* path (required) - The location where the project will be located, relative to
+the jiri root.
+
+* remote (required) - The remote url of the project repository.
+
+* protocol (optional) - The protocol to use when cloning and syncing the repo.
+Currently "git" is the default and only supported protocol.
+
+* remotebranch (optional) - The remote branch that the project will sync to.
+Defaults to "master". The "remotebranch" attribute is ignored if "revision"
+is specified.
+
+* revision (optional) - The specific revision (usually a git SHA) that the
+project will sync to. If "revision" is specified then the "remotebranch"
+attribute is ignored.
+
+* gerrithost (optional) - The url of the Gerrit host for the project. If
+specified, then running "jiri cl mail" will upload a CL to this Gerrit host.
+
+* githooks (optional) - The path (relative to $JIRI_ROOT) of a directory
+containing git hooks that will be installed in the projects .git/hooks
+directory during each update.
+
+* runhook (optional) - The path (relate to $JIRI_ROOT) of a script that will be
+run during each update.
+
+The <tool> tags describe the tools that will be compiled and installed in
+$JIRI_ROOT/.jiri_root/bin after each update. The tools must be written in go,
+and are identified by their package name and the project that contains their
+code. They are configured via the following attributes:
+
+* name (required) - The name of the binary that will be installed in
+ JIRI_ROOT/.jiri_root/bin
+
+* package (required) - The name of the Go package that will be passed to "go
+ build".
+
+* project (required) - The name of the project that contains the source code
+ for the tool.
`,
}
diff --git a/cmd/jiri/doc.go b/cmd/jiri/doc.go
index 3bd4684..a1e9106 100644
--- a/cmd/jiri/doc.go
+++ b/cmd/jiri/doc.go
@@ -864,43 +864,119 @@
logic. This is the reason the shim script is recommended over running the
binary directly.
-The binary is located at [root]/.jiri_root/bin/jiri
+The jiri binary is located at [root]/.jiri_root/bin/jiri
Jiri manifest - Description of manifest files
-Jiri manifests are revisioned and stored in a "manifest" repository, that is
-available locally in $JIRI_ROOT/.manifest. The manifest uses the following XML
-schema:
+Jiri manifest files describe the set of projects that get synced and tools that
+get built when running "jiri update".
- <manifest>
- <imports>
- <import name="default"/>
- ...
- </imports>
- <projects>
- <project name="release.go.jiri"
- path="release/go/src/v.io/jiri"
- protocol="git"
- name="https://vanadium.googlesource.com/release.go.jiri"
- revision="HEAD"/>
- ...
- </projects>
- <tools>
- <tool name="jiri" package="v.io/jiri"/>
- ...
- </tools>
- </manifest>
+The first manifest file that jiri reads is in $JIRI_ROOT/.jiri_manifest. This
+manifest **must** exist for the jiri tool to work.
-The <import> element can be used to share settings across multiple manifests.
-Import names are interpreted relative to the $JIRI_ROOT/.manifest/v2 directory.
-Import cycles are not allowed and if a project or a tool is specified multiple
-times, the last specification takes effect. In particular, the elements <project
-name="foo" exclude="true"/> and <tool name="bar" exclude="true"/> can be used to
-exclude previously included projects and tools.
+Usually the manifest in $JIRI_ROOT/.jiri_manifest will import other manifests
+from remote repositories via <import> tags, but it can contain its own list of
+projects and tools as well.
-The tool identifies which manifest to use using the following algorithm. If the
-$JIRI_ROOT/.local_manifest file exists, then it is used. Otherwise, the
-$JIRI_ROOT/.manifest/v2/<manifest>.xml file is used, where <manifest> is the
-value of the -manifest command-line flag, which defaults to "default".
+Manifests have the following XML schema:
+
+<manifest>
+ <imports>
+ <import remote="https://vanadium.googlesource.com/manifest"
+ manifest="public"
+ name="manifest"
+ />
+ <localimport file="/path/to/local/manifest"/>
+ ...
+ </imports>
+ <projects>
+ <project name="my-project"
+ path="path/where/project/lives"
+ protocol="git"
+ remote="https://github.com/myorg/foo"
+ revision="ed42c05d8688ab23"
+ remotebranch="my-branch"
+ gerrithost="https://myorg-review.googlesource.com"
+ githooks="path/to/githooks-dir"
+ runhook="path/to/runhook-script"
+ />
+ ...
+ </projects>
+ <tools>
+ <tool name="jiri"
+ package="v.io/jiri"
+ project="release.go.jiri"
+ />
+ ...
+ </tools>
+</manifest>
+
+The <import> and <localimport> tags can be used to share common projects and
+tools across multiple manifests.
+
+A <localimport> tag should be used when the manifest being imported and the
+importing manifest are both in the same repository, or when neither one is in a
+repository. The "file" attribute is the path to the manifest file being
+imported. It can be absolute, or relative to the importing manifest file.
+
+If the manifest being imported and the importing manifest are in different
+repositories then an <import> tag must be used, with the following attributes:
+
+* remote (required) - The remote url of the repository containing the manifest
+to be imported
+
+* manifest (required) - The path of the manifest file to be imported, relative
+to the repository root.
+
+* name (optional) - The name of the project corresponding to the manifest
+repository. If your manifest contains a <project> with the same remote as the
+manifest remote, then the "name" attribute of on the <import> tag should match
+the "name" attribute on the <project>. Otherwise, jiri will clone the manifest
+repository on every update.
+
+The <project> tags describe the projects to sync, and what state they should
+sync to, accoring to the following attributes:
+
+* name (required) - The name of the project.
+
+* path (required) - The location where the project will be located, relative to
+the jiri root.
+
+* remote (required) - The remote url of the project repository.
+
+* protocol (optional) - The protocol to use when cloning and syncing the repo.
+Currently "git" is the default and only supported protocol.
+
+* remotebranch (optional) - The remote branch that the project will sync to.
+Defaults to "master". The "remotebranch" attribute is ignored if "revision" is
+specified.
+
+* revision (optional) - The specific revision (usually a git SHA) that the
+project will sync to. If "revision" is specified then the "remotebranch"
+attribute is ignored.
+
+* gerrithost (optional) - The url of the Gerrit host for the project. If
+specified, then running "jiri cl mail" will upload a CL to this Gerrit host.
+
+* githooks (optional) - The path (relative to $JIRI_ROOT) of a directory
+containing git hooks that will be installed in the projects .git/hooks directory
+during each update.
+
+* runhook (optional) - The path (relate to $JIRI_ROOT) of a script that will be
+run during each update.
+
+The <tool> tags describe the tools that will be compiled and installed in
+$JIRI_ROOT/.jiri_root/bin after each update. The tools must be written in go,
+and are identified by their package name and the project that contains their
+code. They are configured via the following attributes:
+
+* name (required) - The name of the binary that will be installed in
+ JIRI_ROOT/.jiri_root/bin
+
+* package (required) - The name of the Go package that will be passed to "go
+ build".
+
+* project (required) - The name of the project that contains the source code
+ for the tool.
*/
package main
diff --git a/jiri/.api b/jiri/.api
index 948dd2d..f4648d7 100644
--- a/jiri/.api
+++ b/jiri/.api
@@ -1,4 +1,5 @@
pkg jiri, const JiriManifestFile ideal-string
+pkg jiri, const PreservePathEnv ideal-string
pkg jiri, const ProfilesDBDir ideal-string
pkg jiri, const ProfilesRootDir ideal-string
pkg jiri, const ProjectMetaDir ideal-string
diff --git a/jiri/x.go b/jiri/x.go
index ea92c70..fc59598 100644
--- a/jiri/x.go
+++ b/jiri/x.go
@@ -28,6 +28,11 @@
ProfilesDBDir = RootMetaDir + string(filepath.Separator) + "profile_db"
ProfilesRootDir = RootMetaDir + string(filepath.Separator) + "profiles"
JiriManifestFile = ".jiri_manifest"
+
+ // PreservePathEnv is the name of the environment variable that, when set to a
+ // non-empty value, causes jiri tools to use the existing PATH variable,
+ // rather than mutating it.
+ PreservePathEnv = "JIRI_PRESERVE_PATH"
)
// X holds the execution environment for the jiri tool and related tools. This
@@ -49,24 +54,25 @@
if err != nil {
return nil, err
}
-
- // Prepend $JIRI_ROOT/.jiri_root/bin to the PATH, so execing a binary will
- // invoke the one in that directory, if it exists. This is crucial for
- // jiri subcommands, where we want to invoke the binary that jiri
- // installed, not whatever is in the user's PATH.
- //
- // Note that we must modify the actual os env variable with os.SetEnv and
- // also the ctx.env, so that execing a binary through the os/exec package
- // and with ctx.Run both have the correct behavior.
x := &X{
Context: ctx,
Root: root,
Usage: env.UsageErrorf,
}
- newPath := envvar.PrependUniqueToken(ctx.Env()["PATH"], string(os.PathListSeparator), x.BinDir())
- ctx.Env()["PATH"] = newPath
- if err := os.Setenv("PATH", newPath); err != nil {
- return nil, err
+ if ctx.Env()[PreservePathEnv] == "" {
+ // Prepend $JIRI_ROOT/.jiri_root/bin to the PATH, so execing a binary will
+ // invoke the one in that directory, if it exists. This is crucial for jiri
+ // subcommands, where we want to invoke the binary that jiri installed, not
+ // whatever is in the user's PATH.
+ //
+ // Note that we must modify the actual os env variable with os.SetEnv and
+ // also the ctx.env, so that execing a binary through the os/exec package
+ // and with ctx.Run both have the correct behavior.
+ newPath := envvar.PrependUniqueToken(ctx.Env()["PATH"], string(os.PathListSeparator), x.BinDir())
+ ctx.Env()["PATH"] = newPath
+ if err := os.Setenv("PATH", newPath); err != nil {
+ return nil, err
+ }
}
return x, nil
}
diff --git a/profiles/profilescmdline/manager_test.go b/profiles/profilescmdline/manager_test.go
index 6d24908..bec1096 100644
--- a/profiles/profilescmdline/manager_test.go
+++ b/profiles/profilescmdline/manager_test.go
@@ -256,7 +256,7 @@
dir, sh := buildInstallers(t), gosh.NewShell(t)
createProfilesDB(t, fake.X)
sh.Vars["JIRI_ROOT"] = fake.X.Root
- sh.Vars["PATH"] = envvar.PrependUniqueToken(os.Getenv("PATH"), ":", dir)
+ sh.Vars["PATH"] = envvar.PrependUniqueToken(sh.Vars["PATH"], ":", dir)
i1 := filepath.Join(fake.X.ProfilesDBDir(), "i1")
i2 := filepath.Join(fake.X.ProfilesDBDir(), "i2")