Skip to content

Commit

Permalink
feat!(clone): Use better cloning policy (#148)
Browse files Browse the repository at this point in the history
* feat(github): add IsPushable function and tests

Added a new function IsPushable to check if a user has push permissions
based on the viewerPermission API query.

Added new util file in the github package to include helper functions
and facilitate unit testing on command outputs.

* fix(clone): changing the test to be more reflective

* feat(clone): add force fork option to clone command

- Deprecated the `nofork` flag in favour of a `fork` one
- Default Clone behaviour is branching instead of forking
- Modified tests to reflect the changes in the clone command.

* fix(code): removed unused library

* docs: update README for turbolift clone usage

Updated the README to clarify the usage of the `turbolift clone` command.

* fix(clone): correct fork and clone logic

This commit fixes the logic for forking and cloning repositories.

Also changed the testing logic to include the type of call for GH.
This is because the testing was just on the argument and was missing
context when running the unit test.

* fix(clone): update IsPushable to use full repo name

Updated the IsPushable function to use the full repository name instead
of the repository directory path.

The gh command now uses a repository as a parameter, since the clone has
not taken place yet.

* fix(clone): test if permission check fail works

Added a new test to verify behavior when permission check fails.

Updated the log message to correctly display the repository name when
determining push permissions.

* refactor(tests): remove commented-out fakeGitHub instances

* Update cmd/clone/clone.go

Co-authored-by: Richard North <[email protected]>

---------

Co-authored-by: Richard North <[email protected]>
  • Loading branch information
sledigabel and rnorth authored Nov 4, 2024
1 parent 55ca75f commit b4a9e55
Show file tree
Hide file tree
Showing 10 changed files with 375 additions and 99 deletions.
14 changes: 8 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ 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)
3. Running a mass `clone` of the repos
4. Making changes to every repo
5. Committing changes to every repo
6. Creating a PR for every repo
Expand Down Expand Up @@ -115,15 +115,17 @@ 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```
`turbolift clone` clones all repositories listed in the `repos.txt` file into the `work` directory.
By default the cloning policy is to create a branch to the target repository. If you do not have permissions to push a branch on the target repository, `turbolift` will fork it.

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`.
If you do want to fork all the repositories instead of letting turbolift deciding for you, use the `--fork` flag.

> 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.
Usage:
```console
turbolift clone
```

### Making changes

Expand Down
38 changes: 27 additions & 11 deletions cmd/clone/clone.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ var (
)

var (
nofork bool
repoFile string
forceFork bool
repoFile string
)

func NewCloneCmd() *cobra.Command {
Expand All @@ -45,7 +45,7 @@ func NewCloneCmd() *cobra.Command {
Run: run,
}

cmd.Flags().BoolVar(&nofork, "no-fork", false, "Will not fork, just clone and create a branch.")
cmd.Flags().BoolVar(&forceFork, "fork", false, "Force forking, instead of turbolift choosing whether to fork/branch based on permissions")
cmd.Flags().StringVar(&repoFile, "repos", "repos.txt", "A file containing a list of repositories to clone.")

return cmd
Expand All @@ -66,13 +66,30 @@ func run(c *cobra.Command, _ []string) {

var doneCount, skippedCount, errorCount int
for _, repo := range dir.Repos {
orgDirPath := path.Join("work", repo.OrgName) // i.e. work/org
orgDirPath := path.Join("work", repo.OrgName) // i.e. work/org
repoDirPath := path.Join(orgDirPath, repo.RepoName) // i.e. work/org/repo

var cloneActivity *logging.Activity
if nofork {
cloneActivity = logger.StartActivity("Cloning %s into %s/%s", repo.FullRepoName, orgDirPath, repo.RepoName)

// Determine whether we need to fork or clone
var fork bool

if forceFork {
fork = true
} else {
res, err := gh.IsPushable(logger.Writer(), repo.FullRepoName)
if err != nil {
logger.Warnf("Unable to determine if we can push to %s: %s", repo.FullRepoName, err)
fork = true
} else {
fork = !res
}
}

if fork {
cloneActivity = logger.StartActivity("Forking and cloning %s into %s/%s", repo.FullRepoName, orgDirPath, repo.RepoName)
} else {
cloneActivity = logger.StartActivity("Cloning %s into %s/%s", repo.FullRepoName, orgDirPath, repo.RepoName)
}

err := os.MkdirAll(orgDirPath, os.ModeDir|0o755)
Expand All @@ -82,18 +99,17 @@ func run(c *cobra.Command, _ []string) {
break
}

repoDirPath := path.Join(orgDirPath, repo.RepoName) // i.e. work/org/repo
// skip if the working copy is already cloned
if _, err = os.Stat(repoDirPath); !os.IsNotExist(err) {
cloneActivity.EndWithWarningf("Directory already exists")
skippedCount++
continue
}

if nofork {
err = gh.Clone(cloneActivity.Writer(), orgDirPath, repo.FullRepoName)
} else {
if fork {
err = gh.ForkAndClone(cloneActivity.Writer(), orgDirPath, repo.FullRepoName)
} else {
err = gh.Clone(cloneActivity.Writer(), orgDirPath, repo.FullRepoName)
}

if err != nil {
Expand All @@ -114,7 +130,7 @@ func run(c *cobra.Command, _ []string) {
}
createBranchActivity.EndWithSuccess()

if !nofork {
if fork {
pullFromUpstreamActivity := logger.StartActivity("Pulling latest changes from %s", repo.FullRepoName)
var defaultBranch string
defaultBranch, err = gh.GetDefaultBranchName(pullFromUpstreamActivity.Writer(), repoDirPath, repo.FullRepoName)
Expand Down
Loading

0 comments on commit b4a9e55

Please sign in to comment.