exploratory command-line tool to make changes across multiple repositories & track their progress
words defined by this readme are written in bold
Nori is a command-line application for managing changes across multiple (usually Github) repositories. It allows you to build up a sequence of Operations to go through the process of discovering repositories to change, cloning them & making the changes, creating Pull Requests for the changes, and tracking the progress of the Pull Requests in a Github Project. The main interface for Nori is an interactive command-line wizard, which prompts you for which operations to run, and the arguments needed for each operation.
npx nori
This temporarily installs Nori and runs the interactive wizard. The first time you run it, it will prompt you for various configuration variables, which are required only for certain operations. It'll then ask you to Create a session
. Give the session a memorable name. The wizard takes you through the operations you can run. At any point you can exit by pressing Ctrl+C or selecting Done
. Your progress is saved under the name you gave when you started nori
. Next time you run it, it will display a list of previous sessions that you can resume, most recent last.
If you'll be running nori
frequently, install it globally:
npm install -g nori
nori
can run operations via the interactive prompt, or directly on the command line. Operations output a particular type of data, and some operations have one or more inputs, which are types of data that must be gathered before you can run the operation. The interactive prompt will only enable the operations you have the data for so far. When running from the command line, you can pass this data around by Piping the operations, or by using a State File.
Run nori
with the name of the operation, and any arguments it requires as double-dashed command line arguments (nori
understands --kebab-case
arguments and transforms them to camelCase
). If you're running in an interactive shell, nori
will prompt for any missing arguments.
For example, to consume repositories from repos.txt
and output the formatted list:
⟩ nori file --file repos.txt
https://github.com/financial-times-sandbox/Abandoned-Toothbrush
https://github.com/financial-times-sandbox/Western-Storm
Every operation supports the --json
flag, which outputs all data found formatted as JSON:
⟩ nori file --file repos.txt --json
[
{
"owner": "financial-times-sandbox",
"name": "Abandoned-Toothbrush"
},
{
"owner": "financial-times-sandbox",
"name": "Western-Storm"
}
]
Get a list of repositories from a text file, structured as line-separated owner/name
strings (optionally with leading https://github.com/
).
Arguments | file |
path to the text file to read, relative to the current working directory |
---|---|---|
Inputs | none | |
Output | repos |
Before executing any of the two commands below, either through npx nori
interactive mode, or directly through the command line, make sure you set the env variable BIZ_OPS_API_KEY
. To get the key, request a key with a Biz Ops API policy from https://apigateway.in.ft.com/key-form/developer.
This operation gets all the repositories from the team you have selected. It shows a list of teams in customer products (obtained from Bizops) which you can select.
Arguments | None | Teams to be selected when command is run |
---|---|---|
Inputs | none |
|
Output | repos |
This operation gets repositories by executing the file that you pass in containing your own graphql query.
Arguments | file |
path to a .graphql | .txt file containing graphql query for repositories |
---|---|---|
Inputs | none | |
Output | repos |
Filter the list of repositories by their names.
Arguments | filter |
regular expression to filter the names of the repos by |
---|---|---|
Inputs | repos |
|
Output | repos |
Clone each of the list of repositories
Arguments | none | |
---|---|---|
Inputs | repos |
|
Output | clones |
Create a branch and run a script on it. If the provided branch name already exists, Nori will append a number to it (e.g. branch
→ branch-1
).
Arguments | script |
path to the script to run, relative to the current working directory. should have executable permissions |
---|---|---|
branch |
name of the branch to create | |
Inputs | clones |
|
Output | localBranches |
The script has the responsibility to:
- Make changes to the files in a local clone of a git repository
- Add those changes to git
- Commit those changes to git
Nori will take care of creating branches. The main benefit
of this approach is that scripts do not need
nori
for you to be able to run them. This makes development,
debugging and one-off runs of a script much simpler.
Push each repository's local branch to the remote. If a branch already exists on the remote with the same name as the local branch, Nori will append a number to it (e.g. branch
→ branch-1
).
Arguments | none | |
---|---|---|
Inputs | clones , localBranches |
|
Output | remoteBranches |
If you are planning to raise PRs, please add your github personal access token to the githubAccessToken object in ~/.config/nori-workspace/config.json
. Authenticating increases the secondary rate limit of GitHub API, which increases your chance to raise multiple PRs without being blocked by the limit.
Create a Pull Request for each of the pushed branches.
Arguments | templates.title |
the title of the pull requests. you can use Javascript ${} template string syntax. available variables are repo.owner , repo.name and branch . |
---|---|---|
templates.body |
the body of the pull requests. supports templates like title |
|
Configuration | githubAccessToken |
Github personal access token with repo scope
|
Inputs | repos , branches |
|
Output | prs |
Create a Github Project.
Arguments | projectData.name |
the name of the project to create |
---|---|---|
projectData.org |
the org to create the project in. this must be the same org as every repo that you've created a PR on. | |
Configuration | githubAccessToken |
Github personal access token with repo scope
|
Inputs | none | |
Output | project |
NB we're considering what to do about repos from multiple orgs, see #62
NB the project will have To Do
, In Progress
and Done
columns, but there's currently no way to set up automatic transitions using the Github API. you'll have to set that up manually if you want the project board to reflect the state of the PRs
Get a project from Github.
Arguments | projectUrl |
URL of the Github project page |
---|---|---|
Configuration | githubAccessToken |
Github personal access token with repo scope
|
Inputs | none | |
Output | project |
Add the PRs to the project.
Arguments | noned | |
---|---|---|
Configuration | githubAccessToken |
Github personal access token with repo scope
|
Inputs | prs , project |
|
Output | cards |
When running the interactive prompt, your progress is automatically saved to a state file. It contains the list of operations you've run & the arguments given to them, and a cache of the data returned by the operations.
State files are kept in the folder ~/.config/nori-workspace
(this is also where repositories are cloned to). When you start the interactive prompt, it will list any state files already in the workspace folder, allowing you to resume previous sessions.
Individual operations can also read and save to state files with the --state-file path/to/file.json
option. When you run an operation with a path to a state file that doesn't exist, it will ask if you want to create it. When the operation completes, it'll have added itself and the data it returned to the state file.
The --state-file
option can also be used with the interactive prompt, which will skip the step asking you to create a state file or use one from the nori
workspace folder, and allow you to use a state file from any location. State files are compatible between individual operations and the interactive prompt, which lets you shuffle between the two modes.
State can also be passed between operations using shell pipes. This is equivalent to running them in sequence and reusing the same state file.
nori file --file repos.txt | nori run-script --script script.sh --branch change
Note that interactive features, such as prompting for missing arguments, won't be available when piping. If any arguments are missing, the operation will error instead. The same goes for providing a state file via the command line argument; it's an error to use --state-file
and pipe as well. To load or save a state file in a piped operation, use shell redirection:
nori file --file repos.txt < input-state.json | nori run-script --script script.sh --branch change > output-state.json
# └─────────┬────────┘ └─────────┬─────────┘
# read input from input-state.json write output to output-state.json
MIT. © 2019 Financial Times. Made with 💚 by FT.com Enabling Technologies Group