Skip to content

πŸ™ exploratory command-line tool to make changes across multiple repositories & track their progress

License

Notifications You must be signed in to change notification settings

Financial-Times/nori

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

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.

nori screencast

Usage

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"
  }
]

Operations

File

nori file --file <file path>

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

Get repositories through Bizops

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.

nori team-repos

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
nori graphql-repos --file <file path>

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 repository name

nori filter-repo-name --filter <repo name>

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

nori clone

Clone each of the list of repositories

Arguments none
Inputs repos
Output clones

Run Script

nori run-script --script <file path> --branch <branch name>

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 Branches

nori push-branches

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

Pull Requests

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.

nori prs --templates.title <PR title> --templates.body <PR body>

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 Project

nori create-project --project-data.name <name> --project-data.org <org>

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 Project

nori get-project --project-url <projectURL>

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 to Project

nori add-to-project

Add the PRs to the project.

Arguments noned
Configuration githubAccessToken Github personal access token with repo scope
Inputs prs, project
Output cards

State Files

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.

Piping

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

Licence

MIT. Β© 2019 Financial Times. Made with πŸ’š by FT.com Enabling Technologies Group