Code Monkey home page Code Monkey logo

turbolift's Introduction

turbolift

A simple tool to help apply changes across many GitHub repositories simultaneously.

Philosophy

Anyone who has had to manually make changes to many GitHub repositories knows that it's hard to beat the simplicity of just cloning the repositories and updating them locally. You can use any tools necessary to make the change, and there's a degree of immediacy in having local files to inspect, tweak or run validation.

It's dumb but it works. It doesn't scale well, though. Manually cloning and raising PRs against tens/hundreds of repositories is painful and boring.

Turbolift essentially automates the boring parts and stays out of the way when it comes to actually making the changes. It automates cloning, committing, and raising PRs en-masse, so that you can focus on the substance of the change.

Historical note: Turbolift supersedes an internal system at Skyscanner named Codelift. Codelift was a centralised batch system, requiring changes to be scripted upfront and run overnight. While Codelift was useful, we have found that a decentralised, interactive tool is far easier and quicker for people to use in practice.

This blog post gives a longer background for the thinking behind Turbolift.

Demo

This demo shows Turbolift in action, creating a simple PR in two repositories:

Screencast demo of turbolift in use

Installation

Using brew (recommended) Install turbolift using brew from Skyscanner's tap, as follows:
brew install skyscanner/tools/turbolift

Note that the GitHub CLI, gh is a dependency of Turbolift and will be installed automatically.

Downloading binaries

Pre-built binary archives can be downloaded from the Releases page.

  • Download, extract the archive, and move it onto your PATH.
  • Note that the binaries are not currently notarized for MacOS Gatekeeper. If errors are displayed, use xattr -c PATH_TO_TURBOLIFT_BINARY to un-quarantine the binary, or right-click on the binary in Finder and choose 'Open' once to allow future execution. Distribution will be improved under #43.

You must also have the GitHub CLI, gh, installed:

  • Install using brew install gh

Before using Turbolift, run gh auth login once and follow the prompts, to authenticate against github.com and/or your GitHub Enterprise server.

Basic usage:

Making changes with turbolift is split into six main phases:

  1. init - getting set up
  2. Identifying the repos to operate upon
  3. Running a mass clone of the repos (by default, it will create a fork in your user space)
  4. Making changes to every repo
  5. Committing changes to every repo
  6. Creating a PR for every repo

It is expected that you'll go through these phases in series.

The turbolift tool automates as much of the Git/GitHub heavy lifting as possible, but leaves you to use whichever tools are appropriate for making the actual changes.

Caveats

With great power comes great responsibility. We encourage Turbolift users to consider the following guidelines:

  • Don't use Turbolift to raise pointless PRs. If a reviewer might think the change is trivial or unimportant, think about whether it's actually needed.
  • If you need to make a change to a large number of repositories, we've found that it's generally better to raise PRs to a small subset at first and collect feedback. Simply comment out repositories in repos.txt to make Turbolift temporarily ignore them.
  • For complicated or potentially contentious changes, think about ways to validate them before raising PRs. This could range from working in a pair, through writing a peer-reviewed script, all the way to preparing a design document for the planned changes.
  • If you can run automated tests locally, then do (e.g. turbolift foreach to run linting and tests for each repository).
  • Raising draft PRs can be a good way to collect feedback, especially CI test results, with less pressure on reviewers. Use turbolift create-prs --draft
  • In an organisation with shared infrastructure (e.g. CI), raising many PRs in a short timeframe can cause a lot of load. Consider spacing out PR creation using the --sleep option or by commenting out chunks of repositories in repos.txt.

Detailed usage

init - getting set up

As per the installation instructions above, make sure gh is installed and authenticated before starting.

If working with repositories on a GitHub Enterprise server, ensure that you have the environment variable GH_HOST set to point to that server.

To begin working with Turbolift and create a 'campaign' to hold settings and working copies of repositories:

turbolift init --name CAMPAIGN_NAME

This creates a new turbolift 'campaign' directory ready for you to work in. Note that CAMPAIGN_NAME will be used as the branch name for any changes that are created

Next, please run:

cd CAMPAIGN_NAME

Identifying the repos to operate upon

Update repos.txt with the names of the repos that need changing (either manually or using a tool to identify the repos).

gh-search is an excellent tool for performing GitHub code searches, and can output a list of repositories in a format that turbolift understands:

e.g.

$ gh-search --repos-with-matches YOUR_GITHUB_CODE_SEARCH_QUERY > repos.txt

Working on multiple repo files

Occasionally you may need to work on different repo files. For instance the repos can be divided in sub categories and the same change don't apply to them the same way. The default repo file is called repos.txt but you can override this on any command with the --repos flag.

turbolift foreach --repos repoFile1.txt sed 's/pattern1/replacement1/g'
turbolift foreach --repos repoFile2.txt sed 's/pattern2/replacement2/g'

Running a mass clone

turbolift clone

This creates a fork and clones all repositories listed in the repos.txt file (or the specified alternative repo file) into the work directory. You may wish to skip the fork and work on the upstream repository branch directly with the flag --no-fork.

NTLD: if one of the repositories in the list requires a fork to create a PR, omit the --no-fork flag and let all the repositories be forked. For now it's a all-or-nothing scenario.

Making changes

Now, make changes to the checked-out repos under the work directory. You can do this manually using an editor, using sed and similar commands, or using codemod/comby, etc.

You are free to use any tools that help get the job done.

If you wish to, you can run the same command against every repo using turbolift foreach ... (where ... is the shell command you want to run).

For example, you might choose to:

  • turbolift foreach rm somefile - to delete a particular file
  • turbolift foreach sed -i '' 's/foo/bar/g' somefile - to find/replace in a common file
  • turbolift foreach make test - for example, to run tests (using any appropriate command to invoke the tests)
  • turbolift foreach git add somefile - to stage a file that you have created

At any time, if you need to update your working copy branches from the upstream, you can run turbolift foreach git pull upstream master.

It is highly recommended that you run tests against affected repos, if it will help validate the changes you have made.

Committing changes

When ready to commit changes across all repos, run:

turbolift commit --message "Your commit message"

This command is a no-op on any repos that do not have any changes. Note that the commit will be run with the --all flag set, meaning that it is not necessary to stage changes using git add/rm for changed files. Newly created files will still need to be staged using git add.

Repeat if you want to make multiple commits.

Creating PRs

Edit the PR title and description in README.md.

Next, to push and raise PRs against changed repos, run:

turbolift create-prs

Use turbolift create-prs --sleep 30s to, for example, force a 30s pause between creation of each PR. This can be helpful in reducing load on shared infrastructure.

Important: if raising many PRs, you may generate load on shared infrastucture such as CI. It is highly recommended that you:

  • slow the rate of PR creation by making Turbolift sleep in between PRs
  • create PRs in batches, for example by commenting out repositories in repos.txt
  • Use the --draft flag to create the PRs as Draft

Working with multiple PR description files

Occasionally you may want to work with more than one PR title and description. When this is the case, use the flag --description to specify an alternative file when creating prs. The first line of the file chosen will be used as the PR title and the rest as the description body.

turbolift create-prs --repos repoFile1.txt --description prDescriptionFile1.md
turbolift create-prs --repos repoFile2.txt --description prDescriptionFile2.md

After creating PRs

Viewing status

While it's simple to search for PRs in GitHub search, turbolift pr-status can be used to view PR status in the terminal. For example:

Viewing a summary of PRs:

$ turbolift pr-status
...
State        Count
Merged       139
Open         53
Closed       29
Skipped      0
No PR Found  1

Viewing a detailed list of status per repo:

$ turbolift pr-status --list
...
Repository                                                State   Reviews           Build status    URL
redacted/redacted                                         OPEN    REVIEW_REQUIRED   SUCCESS         https://github.redacted/redacted/redacted/pull/262
redacted/redacted                                         OPEN    REVIEW_REQUIRED   SUCCESS         https://github.redacted/redacted/redacted/pull/515
redacted/redacted                                         OPEN    REVIEW_REQUIRED   SUCCESS         https://github.redacted/redacted/redacted/pull/342
redacted/redacted                                         MERGED  APPROVED          SUCCESS         https://github.redacted/redacted/redacted/pull/407
redacted/redacted                                         MERGED  REVIEW_REQUIRED   SUCCESS         https://github.redacted/redacted/redacted/pull/220
redacted/redacted                                         OPEN    REVIEW_REQUIRED   FAILURE         https://github.redacted/redacted/redacted/pull/105
redacted/redacted                                         MERGED  APPROVED          SUCCESS         https://github.redacted/redacted/redacted/pull/532
redacted/redacted                                         MERGED  APPROVED          SUCCESS         https://github.redacted/redacted/redacted/pull/268
redacted/redacted                                         OPEN    REVIEW_REQUIRED   FAILURE         https://github.redacted/redacted/redacted/pull/438
...

Updating PRs

Use the update-prs command to update PRs after creating them. Current options for updating PRs are:

Update PR titles and descriptions with --amend-description

turbolift update-prs --amend-description [--yes]

By default, turbolift will read a revised PR Title and Description from README.md. The title is taken from the first heading line, and the description is the remainder of the file contents.

As with creating PRs, if you need Turbolift to read these values from an alternative file, use the flag --description PATH_TO_FILE.

turblift update-prs --amend-description --description prDescriptionFile1.md

Close PRs with the --close flag

turbolift update-prs --close [--yes]

If the flag --yes is not passed with an update-prs command, a confirmation prompt will be presented to the user.

As always, use the --repos flag to specify an alternative repo file to repos.txt.

Status: Preview

This tool is fully functional, but we have improvements that we'd like to make, and would appreciate feedback.

Contributing

See CONTRIBUTING.md

Local development

To build locally:

make build

To run tests locally:

make test

turbolift's People

Contributors

dan7-7-7 avatar dependabot[bot] avatar jesusfcr avatar jorgedc93 avatar kevinrobayna avatar kurtmckee avatar lanwen avatar logan-porelle avatar macavirus avatar rnorth avatar sledigabel avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

turbolift's Issues

clone - allow alternative repos file to be provided as argument

eg:

turbolift clone --repos allrepos.txt

this would enable the following workflow:

turbolift clone --repos allrepos.txt

# identify repos that require the change
grep -r ... work/* > repos.txt

# now do the work on just that subset of repos in repos.txt 
turbolift foreach ...

Consider setting up Homebrew distribution via a tap

The installation instructions could be simplified greatly if we use Homebrew for distribution. Per discussions, it would be good to set up a private tap and configure goreleaser to keep it up to date.

We need to ensure that MacOS distribution works without Gatekeeper problems; I don't think that signing/notarizing the binaries should be required for brew installs, but this needs verification.

Add CodeQL in CI

CodeQL is a convenient code parser.
It can be added as a CI check and provide some relevant feedback on code style and best practices.

Would be good to add it as a check in Github Actions.

Enable optional Parallel runs

at the moment all prs commits etc are sequential.
For large numbers, adding parallel processing could improve the user experience by accelerating the feedback loop.

Introduce a step-by-step git commiter

On large repo lists, one often has to run this kind of workflow:

turbolift clone
turbolift forearch <insert here the command to change the repo>

then, to validate the changes, they would run something like this:

turbolift foreach git diff
<frantically scroll through thousands of lines to figure out whether all repositories are successfully changed>
<then, trusting your eye sight, you stage and commit>
turbolift commit

It would be nice to have a way to commit those step-by-step with a visual validation from the user, introducing a --incremental.

The workflow would look like:

turbolift commit -i
-- Repository: REPOSITORY_NAME_1
<git diff on this specific repo>
<prompt for the user to confirm with a y/N>
-- Repository: REPOSITORY_NAME_2
...

We will log in the end the repo list that WASN'T updated, the user can decide what to do with it then.

Support PR description from specified file

turbolift create-prs expects the PR description to come from README.md

Would be nice to support a user-specified file instead so that README.md can be used for other purposes (eg: describing the process how to use turbolift)

'turbolift update-prs' feature

I've recently had a couple of situations where new information has come to light only after mass-raising a bunch of PRs. That new information would be useful to include in the PR description for the benefit of those that haven't yet go around to reviewing the PR, but turbolift (as far as I can tell) doesn't keep a track of the PRs it has raised. This isn't necessarily a problem, but it does mean that the only interaction I have with the PR stage is just the creation stage and I have to live with whatever description I had for ever more.

I'm thinking there could be a different option, or perhaps a flag, that executes code similar to that for create-pr, but is able to update the description of the PR given the content of local README.md file. The logic might be something like:

  1. For repo in repos.txt, try to create PR. If there's no PR, no problem, job done.
  2. If it fails because a PR already exists, try and update the description. I don't think it even has to be clever enough to decide if it needs to, but that might be nice.

foreach sed fails with 'unknown shorthand flag' error

I get the following error when running foreach sed with turbolift 2:

`$ turbolift foreach sed -i '' 's/foo/bar/g' README.md

Error: unknown shorthand flag: 'i' in -i
Usage:
turbolift foreach -- SHELL_COMMAND [flags]
Flags:
-h, --help help for foreach
Global Flags:
-v, --verbose verbose output
2021/05/21 11:49:08 unknown shorthand flag: 'i' in -i`

A temporary workaround is to encapsulate the sed command in double quotes, but I think this is a bug in the way parameters are handled. (also some of the README examples do not work either)

Print out links to the pull requests created

At the end of a PR run, it would be great if the links were printed from the PRs that were created. So the results could look like this:

$ turbolift create-prs
  OK   Reading campaign data
  OK   Pushing changes in org/repo-1 to origin
  OK   Creating PR in org/repo-1
  OK   PR created: https://github.com/org/repo-1/pull/163
  OK   Pushing changes in org/repo-2 to origin
  OK   Creating PR in org/repo-2
  OK   PR created: https://github.com/org/repo-2/pull/163
  OK   Pushing changes in org/repo-3 to origin
  OK   Creating PR in org/repo-3
  OK   PR created: https://github.com/org/repo-3/pull/163
  OK   turbolift create-prs completed (3 OK, 0 skipped)

Love the tool, and find it very useful!

Implement `turbolift init`

This feature is to be ported from the existing internal implementation of the tool. See the README and internal codebase to help maintain parity.

This feature creates a working directory for a 'campaign'. The user will run:

turbolift init --name some_campaign_name

And as a result a directory named some_campaign_name will be created with some skeleton contents:

  • A README.md file, which will serve as a readme for the campaign and will also be used as the title/description for created PRs
  • A .gitignore file covering a work/ directory
  • An empty work directory
  • A repos.txt file for the user to populate
  • A .turbolift file containing a version number, v2.

Functionality to cleanup forks after a campaign is complete

As-is, turbolift users will end up with large numbers of forks in their personal org. This is annoying, and cleaning these up is a chore.

We should have a turbolift cleanup feature to delete a user's forks.

We should discuss safety aspects, e.g. should we avoid deleting forks if:

  • the user's PR is still open for this campaign
  • the user has other PRs open from their fork to upstream
  • ... other reasons?

Some combination of warnings/prompts may help.

Auto-merge flag during PR creation

Description

It would be very helpful to add --auto-merge flag to turbolift create-prs command.

Motivation

In case of huge amount of repos it would be very convenient to enable auto-merge (if the repo has such function of course), so if all of the checks are green, then PR would be merged automatically.

Move logging on a singleton pattern

As part of #75 I saw some limitations in the logging because the logger is instantiated in the command itself.
That limits for instance the use of logs within functions (unless we're injecting the logger dependency within the function signature).

It would be nice to move to a global singleton logger instead.

Implement `turbolift create-prs`

This feature is to be ported from the existing internal implementation of the tool. See the README and internal codebase to help maintain parity.

This feature applies gh create-pr for each working copy. The user will run:

turbolift create-prs

And as a result gh create-pr will be run for each working copy.

Note that:

  • The README.md file should be used to derive the PR title (first line heading) and description (rest of file)
  • If a working copy has no changes compared with upstream, the gh command will fail. This is OK. A future enhancement could be to track reasons for failures to display to the user, but as-is we should keep it simple.
  • We should rate limit creation of PRs to avoid generating too much load (particularly load on shared CI systems)

The current state of repos.txt is the authoritative list - this command should only act on repos that are listed there and which are not commented out.

New files are ignored by the commit command

That's actually a feature of a git commit -a, however it's unexpected for a turbolift user, as it states that you don't have to do anything. Currently workaround is to turbolift foreach -- git add -A, but maybe that could be part of the commit command by default?

Repos file from command line

When running large campaigns it's possible you need to experiment changes with a subsection of your repositories. Currently you need to delete or comment out the repositories you don't want to be taken into account before running any turbolift command.
It would be useful to provide a command line parameter to specify the file to be used as a repository list instead of copy pasting the content into the repos.txt file.
The repos.txt file would continue being the default value.

turbolift clone --repos=test-repos.txt
turbolift foreach --repos=test-repos.txt rm somefile

or

turbolift --repos=test-repos.txt clone
turbolift --repos=test-repos.txt foreach rm somefile

Implement `turbolift clone`

This feature is to be ported from the existing internal implementation of the tool. See the README and internal codebase to help maintain parity.

This feature clones all repositories that are listed in repos.txt

turbolift clone

And as a result all listed repos will be cloned into a work/ORG_NAME/REPO_NAME directory. Any lines in the repos.txt file prefixed by a # should be considered as a comment and ignored.

For now we should wrap the gh command to create a fork of the original repository into the user's personal org and then clone it.

The current internal implementation of the tool uses git worktree for local space efficiency, but we should probably avoid this for simplicity's sake: just a regular fork+clone should suffice.

Rate limit generation of PRs

Pushing to git and PR creation can both cause CI workflows to start (both are part of the create-prs subcommand). Particularly in an enterprise context, this could put pressure on shared infrastructure.

We should put in place reasonable measures to prevent this, e.g. one or more of:

  • opt-in rate limiting (e.g. a fixed pause between creation)
  • automatic rate limiting of PR creation when a certain number of repos is involved.
  • using check status (number of PRs with pending checks) as a signal to slow down rate of creation. This would be a fairly involved feature, which may be too much for an initial implementation.

Apparent incompatibility with `gh` 2.18.0

https://github.com/cli/cli/releases/tag/v2.18.0, released yesterday, seems to behave slightly differently and breaks turbolift create-prs.

I get:

...
  OK   Pushing changes in myorg/myrepo to origin
 FAIL  Creating PR in myorg/myrepo: Error: exit status 1. Stderr: aborted: you must first push the current branch to a remote, or use the --head flag

Downgrading gh to 2.17.0 seems to be a temporary workaround and works flawlessly.

Collect errors and display nicely

Turbolift currently runs multiple commands in series over the list of repositories, displaying stdout/stderr to the user but ignoring exit codes.

This can make it difficult for the user - they have to scour logs for failures.

We should collect errors (i.e. exit code != 0) and display a suitably formatted list at the end of execution.

Prevent unnecessary clones

This feature request adds a lot of complexity to this project but here it is anyway

PROBLEM:
Suppose you have a constant list of 10 repos you want to use Turbolift for. Each and every campaign you create will result in a new clone of the repos. This takes up unnecessary space on a dev's local drive, plus it's slow to download them over and over.

FEATURE REQUEST:
Allow to define a localpath in repos.txt which indicates a local clone of a repository to be worked on. Use this clone instead of creating a new one in the work directory for every new campaign

ALTERNATIVE APPROACH:
Allow to define a repos.txt (or core_repos.txt) in the root of a turbolift directory (this also invents this concept). These core_repos are what you expect to work on most of the time, and thus shouldn't re-clone everytime. There would also be a work directory beside the core_repos.txt.

The file structure would look something like this:

/turbolift_project
--core_repos.txt
--/work
----/repo1
----/repo2
--/campaign_1
----repos.txt
----/work
------/repo3
--/capmpaign_2
----/repos.txt
----/work
------/repo4

Implement `turbolift commit`

This feature is to be ported from the existing internal implementation of the tool. See the README and internal codebase to help maintain parity.

This feature performs a git commit for each working copy that has been cloned by #2. The user will run:

turbolift commit --message "some message"

And as a result git commit will be run with the given message. Note that only working copies with staged changes should be affected - i.e. no empty commits should be created, and no files should be committed that the user hasn't explicitly staged (using git add) already.

The current state of repos.txt is the authoritative list - this command should only act on repos that are listed there and which are not commented out.

Do not create forks if not necessary

Turbolift creates a fork for each project it needs to interact with.
Being Turbolift an excellent tool to handle maintenance tasks in the projects where you work usually it doesn't make much sense to fork these repositories where you already have write access.
It would be better if Turbolift could use the original repository if possible and fallback to a fork if not.

`turbolift status` command

One thing we could think about doing is turbolift status.

Some of the useful things is could log would be:

  • How many repos have had a PR created
  • How many PRs are in each of the open/closed/merged states
  • How many PRs have checks failures or have review rejections

As well as listing numbers, we could also consider a --verbose or --list flag which lists each the PRs in each category. The listing would ideally include a URL pointing to the each PR.

Allow for 'dry running' commands

Before committing to a change (pun absolutely intended), it is sometimes useful to validate that the changes that were made actually came out sensibly in practise.

I would like to see a configuration/environment option to commands like turbolift commit and turbolift create-prs (i.e. any write-like commands) to run them in a dry run mode. For example, the dry run commit could be a report of each git diff, and for create-prs it might be a summary of the git diff against the latest default branch.

Refresh from upstream after cloning

Personal forks of repos can become seriously outdated in between uses of the tool, meaning that it's easy to accidentally create PRs against very old versions of the target repos. This can result in merge conflicts when creating PRs.

I believe that the simplest way to prevent this would be to amend turbolift clone so that it effectively does:

  • Create a fork
  • Clone the forked repo locally
  • Create a new local branch
  • New: Pull changes from upstream/main or upstream/master into the local branch, so that any changes made by the user are against the latest available version of upstream

PR description file from command line

Currently the PR description is taken from the README file of the project. When running large campaigns it's possible you need to create PRs with different descriptions.
It would be useful to provide a command line parameter to specify the file to be used for the description instead of copy pasting the content in the REDME file.
The REDME file would continue being the default value.

turbolift create-prs --description prs/description1.md

Implement `turbolift foreach`

This feature is to be ported from the existing internal implementation of the tool. See the README and internal codebase to help maintain parity.

This feature runs a shell command against every working copy. e.g.:

turbolift foreach echo "$USER" >> foo.txt

Would append the user's name to a file named foo.txt in each working copy. A shell should be used for execution so that placeholder substitution etc work properly.

We might want to consider a more unix-native syntax, with -- separating the turbolift command from the subcommand. e.g.:

turbolift foreach -- echo "$USER" >> foo.txt

We should only consider serial execution at this time. We should also ignore the exit code (though we might want to do something with it in a future enhancement).

The current state of repos.txt is the authoritative list - this command should only act on repos that are listed there and which are not commented out.

Decouple dependency on CLI `gh`

For expediency the tool currently execs gh as a subprocess for several operations with GitHub.

We should break this dependency so that turbolift has no dependencies on external programs.

Option for shallow clone for repos with large commit histories

It would be nice to have an option to do a shallow clone for quicker clones. This would allow us to grab for example, the last 30 commits per repo instead of the entire commit history

# maybe --shallow-clone which could default to 50 last commits
turbolift clone --shallow-clone
# provide the clone depth which could default to 50 or -1 to account for the default deep clone
turbolift clone --depth=50

https://www.perforce.com/blog/vcs/git-beyond-basics-using-shallow-clones
https://github.blog/2020-12-21-get-up-to-speed-with-partial-clone-and-shallow-clone/

Example

git clone -–depth [depth] [remote-url]

Close all created PRs

Use case: You created a campaign, file lots of PRs, find that this was not successful, and want to cancel everything.
Feature: be able to close all PRs generated by a campaign.

Enable a simple feedback system for PR usefulness

It would be good to help users understand how useful their PRs were.

To do this, we could take advantage of the 👍/👎 reactions on PRs:

  • as part of the PR description boilerplate, encourage PR receivers to set a reaction
  • as part of #18, collate the counts of each kind of reaction and include this in the status output

turbolift clone/create-prs does not work in windows

When doing a turbolift clone in a windows environment, it reports the following error:

Executing: git [checkout -b C:\src\turbolift\campaignx] in work/xxx/xxxxxx fatal: 'C:\src\turbolift\campaignx' is not a valid branch name.
It does actually manage to clone the repositories, but when then trying turbolift create-prs it reports the following error:

Executing: git [push -u origin C:\src\turbolift\campaign] in work/xxx/xxxxxxx fatal: invalid refspec 'C:\src\turbolift\campaign'

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.