Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rename the assign command to request. #106

Merged
merged 11 commits into from
Jan 15, 2024
47 changes: 24 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Harmony
Harmony is a small tool that helps teams keep GitHub reviews running smoothly. It takes the work out of picking someone from a pool of developers to review a new PR. Harmony does this by heuristically determining who on a particular GitHub Team has the least current/recent review workload.

Harmony offers a heuristic for PR assignments that is different than GitHub's round robin or weighted algorithms, but Harmony can also work well even if your team uses GitHub's automatic PR assignments ([see below](#deferring-to-github)).
Harmony offers a heuristic for PR review requests that is different than GitHub's round robin or weighted algorithms, but Harmony can also work well even if your team uses GitHub's automatic PR review requests ([see below](#deferring-to-github)).

## Dependencies
### Runtime
Expand Down Expand Up @@ -102,21 +102,21 @@ What repository would you like to use harmony for (ENTER for default: myrepo)?

What GitHub remote repo would you like to use harmony for (ENTER for default: origin)?

Would you like harmony to comment when it assigns reviewers? [Y/n]
Would you like harmony to assign teams in addition to individuals when it assigns reviewers? [Y/n]
Would you like harmony to comment when it requests reviewers? [Y/n]
Would you like harmony to request team reviews in addition to individuals when it requests reviewers? [Y/n]
Creating config...
```

Once configured, Harmony supports the following commands: `config`, `branch`, `pr`, `label`, `assign`, `contribute`, `whoami`, `reflect`, `list`, `graph`, `health`, and `sync`.
Once configured, Harmony supports the following commands: `config`, `branch`, `pr`, `label`, `request`, `contribute`, `whoami`, `reflect`, `list`, `graph`, `health`, and `sync`.

### Config
Running `harmony config <property>` will read the given configuration property. `harmony config <property> <value>` will set the configuration property.

Not all configuration properties can be read/set with this command.
#### Properties
- `assignTeams` (`true`/`false`) -- When picking a reviewer from a team, assign the team as a reviewer as well.
- `assignUsers` (`true`/`false`) -- When assigning a team as a reviewer, pick a user to review as well.
- `commentOnAssign` (`true`/`false`) -- When assigning a reviewer chosen by Harmony, comment on the pull request.
- `requestTeams` (`true`/`false`) -- When picking a reviewer from a team, request the team as a reviewer as well.
- `requestUsers` (`true`/`false`) -- When requesting a team as a reviewer, pick a user to review as well.
- `commentOnRequest` (`true`/`false`) -- When requesting a reviewer chosen by Harmony, comment on the pull request.
- `defaultRemote` (optional string) -- When pushing new branches, what remote destination should be used.
- `githubPAT` (optional string) -- If the `$GITHUB_PAT` environment variable is not set, this Personal Access Token is used to authenticate with GitHub.

Expand All @@ -139,40 +139,41 @@ Running `harmony label {<label>} [...]` will help you create a PR if one does no

Note that labels are _not_ prefixed with '#' for this command. There is no need to differentiate labels from other kinds of arguments to `harmony label`.

### Assign
Running `harmony assign {<team> | +<user>} [#<label>] [...]` will help you create a PR if one does not exist yet and then it will assign teams and/or users to the PR.
### Request
Running `harmony request {<team> | +<user>} [#<label>] [...]` will help you create a PR if one does not exist yet and then it will request reviews from teams and/or users.

If `harmony config assignUsers` is `True` (defualt) then harmony will pick someone to review the PR (from one of the listed teams) and assign them to the PR. If `harmony config assignTeams` is `True` then harmony will assign the teams you listed as reviewers of the PR. If `harmony config commentOnAssign` is `True` then harmony will comment on the Pull Request indicating that teams & users were "harmoniously assigned" -- this comment will @mention assigned users so it may be useful or annoying depending on the assigned user's GitHub notification settings.
If `harmony config requestUsers` is `True` (defualt) then harmony will pick someone to review the PR (from one of the listed teams). If `harmony config requestTeams` is `True` (default) then harmony will request reviews from the teams you listed. If `harmony config commentOnRequest` is `True` then harmony will comment on the Pull Request indicating that teams & users were "harmoniously requested" -- this comment will @mention requested users so it may be useful or annoying depending on the requested user's GitHub notification settings.

You can also require that specific additional users (on top of the one Harmony will pick for you) are assigned to the PR. You do this by specifying those users' logins prefixed with '+' as arguments to Harmony.
You can also require that specific additional users (on top of the one Harmony will pick for you) are requested to review the PR. You do this by specifying those users' logins prefixed with '+' as arguments to Harmony. This will request review from those specific additional users regardless of the `requestUsers` setting; that setting controls whether Harmony picks users from each Team you specify to review PRs.

You can optionally apply any number of labels to the PR at the same time as assigning reviewers by prefixing the labels with '#'.
You can optionally apply any number of labels to the PR at the same time as requesting reviewers by prefixing the labels with '#'.

#### Deferring to GitHub
If your team has GitHub set up to auto-assign individuals when a team is requested for review, you probably want to tell harmony not to also pick someone using its heuristics. You can run the following `config` commands to tell harmony to assign a team but not also pick an individual from that team:
If your team has GitHub set up to auto-request reviews from individuals when a team is requested for review, you probably want to tell harmony not to also pick someone using its heuristics. You can run the following `config` commands to tell harmony to request a team but not also pick an individual from that team:
```shell
harmony config assignTeams true
harmony config assignUsers false
harmony config requestTeams true
harmony config requestUsers false
```
This does not prevent you from requesting specific individuals with the `+<user>` syntax described above.

#### Examples
Assign the most available reviewer from the "developers" GitHub Team:
Request review from the most available reviewer from the "developers" GitHub Team:
```shell
harmony assign developers
harmony request developers
```

Assign the most available reviewer from either the "frontend" or "backend" GitHub Team:
Request review from the most available reviewer from either the "frontend" or "backend" GitHub Team:
```shell
harmony assign frontend backend
harmony request frontend backend
```

Assign the most available reviewer from the "web" team and additionally assign the users with logins "carl001" and "emmaham":
Request review from the most available reviewer from the "web" team and additionally request review from the users with logins "carl001" and "emmaham":
```shell
harmony assign web +carl001 +emmaham
harmony request web +carl001 +emmaham
```

### Contribute
Running `harmony contribute` will print the URI of the oldest non-draft PR waiting for your review. If you are not requested for review on any PRs, Harmony will suggest a PR that you are not assigned to.
Running `harmony contribute` will print the URI of the oldest non-draft PR waiting for your review. If you are not requested for review on any PRs, Harmony will suggest a PR that your review is not requested on.

You can skip PRs and retrieve the next-oldest one by passing a dash followed by the number to skip (e.g. `-2` to skip the two oldest waiting PRs).

Expand All @@ -183,7 +184,7 @@ You can simultaneously get the URI for a PR to review and checkout the branch ne
Many operating systems have an `open` command (though the name "open" is not ubiquitous); this means you can run something like `open $(harmony contribute)` to open a web browser to the PR that Harmony is suggesting.

#### Examples
Retrieve a URI for the oldest unreviewed and open PR (prioritizing PRs for which you are an assigned reviewer):
Retrieve a URI for the oldest unreviewed and open PR (prioritizing PRs for which you are a requested reviewer):
```shell
harmony contribute
```
Expand Down
2 changes: 1 addition & 1 deletion default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
};

harmonyPkg = buildIdris {
version = "3.3.0";
version = "4.0.0";
projectName = "harmony";
src = ./.;

Expand Down
2 changes: 1 addition & 1 deletion harmony.ipkg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package harmony
version = 3.3.0
version = 4.0.0
authors = "Mathew Polzin"
license = "MIT"
brief = "Harmony GitHub collaboration tool"
Expand Down
2 changes: 1 addition & 1 deletion node-packages.nix
Original file line number Diff line number Diff line change
Expand Up @@ -598,7 +598,7 @@
args = {
name = "_at_mattpolzin_slash_harmony";
packageName = "@mattpolzin/harmony";
version = "3.3.0";
version = "4.0.0";
src = ./.;
dependencies = [
sources."@kwsites/file-exists-1.1.1"
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@mattpolzin/harmony",
"version": "3.3.0",
"version": "4.0.0",
"engines" : {
"node" : ">=18.0.0"
},
Expand Down
2 changes: 1 addition & 1 deletion src/AppVersion.idr
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ module AppVersion

export
appVersion : String
appVersion = "3.3.0"
appVersion = "4.0.0"

export
printVersion : HasIO io => io ()
Expand Down
26 changes: 23 additions & 3 deletions src/BashCompletion.idr
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import Data.String
%default total

allRootCmds : List String
allRootCmds = [ "assign"
allRootCmds = [ "request"
, "assign" -- TODO 5.0.0: <- remove this alias for the deprecated assign command.
, "branch"
, "config"
, "contribute"
Expand Down Expand Up @@ -151,8 +152,8 @@ opts @{config} "pr" partialArg _ =
then hashify . slugify <$> config.repoLabels
else []

-- finally, assign auto-completes with
-- either a team slug or '+' followed by a user login:
-- TODO 5.0.0: remove all of the following that deals with the alias
-- for the deprecated assign command.
opts @{config} "assign" "--" "assign" = "--dry" :: config.teamSlugs
opts @{config} "assign" "--" _ = config.teamSlugs
opts @{config} "assign" partialArg _ =
Expand All @@ -170,6 +171,25 @@ opts @{config} "assign" partialArg _ =
then hashify . slugify <$> config.repoLabels
else config.teamSlugs

-- finally, request auto-completes with
-- either a team slug or '+' followed by a user login:
opts @{config} "request" "--" "request" = "--dry" :: config.teamSlugs
opts @{config} "request" "--" _ = config.teamSlugs
opts @{config} "request" partialArg _ =
if partialArg `isPrefixOf` "--dry"
then ["--dry"]
else slugsOrLoginsOrLabels
where
-- If the word being typed is prefixed with '+' return user logins
-- but otherwise return team slugs.
slugsOrLoginsOrLabels : List String
slugsOrLoginsOrLabels =
if "+" `isPrefixOf` partialArg
then (strCons '+') <$> config.orgMembers
else if isHashPrefix partialArg
then hashify . slugify <$> config.repoLabels
else config.teamSlugs

opts @{_} _ _ _ = []

||| The Bash Completion script calls to harmony with a special --bash-completion
Expand Down
12 changes: 6 additions & 6 deletions src/Commands.idr
Original file line number Diff line number Diff line change
Expand Up @@ -127,17 +127,17 @@ pr {isDraft} labelSlugs =
label labelSlugs
else reject "The pr command only accepts labels prefixed with '#' and the --draft flag."

||| Assign the given teams & users as reviewers when the user executes
||| `harmony assign ...`.
||| Request review from the given teams & users as reviewers when the user executes
||| `harmony request ...`.
export
assign : Config => Git => Octokit =>
(assignArgs : List String)
request : Config => Git => Octokit =>
(requestArgs : List String)
-> {default False dry : Bool}
-> Promise ()
assign args {dry} = do
request args {dry} = do
let (forcedReviewers, teamNames, labelSlugs) = partitionedArgs
if (null forcedReviewers && null teamNames)
then reject "The assign command expects one or more names of GitHub Teams or Users as arguments."
then reject "The request command expects one or more names of GitHub Teams or Users as arguments."
else do (_, openPr) <- identifyOrCreatePR !currentBranch
requestReviewers openPr teamNames forcedReviewers {dry}
when (not (null labelSlugs || dry)) $
Expand Down
60 changes: 33 additions & 27 deletions src/Config.idr
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,17 @@ addIgnoredPRs config is =
||| behavior that is likely undesirable.
checkConfigConsistency : Config -> Either (Doc AnsiStyle) ()
checkConfigConsistency config = do
checkAssignSettings config
checkRequestSettings config
-- other checks...
where
checkAssignSettings : Config -> Either (Doc AnsiStyle) ()
checkAssignSettings config =
if not (config.assignTeams || config.assignUsers)
checkRequestSettings : Config -> Either (Doc AnsiStyle) ()
checkRequestSettings config =
if not (config.requestTeams || config.requestUsers)
then Left $ (annotate (color Yellow) . hsep $ [
"`assignUsers` and `assignTeams` are both False."
, "This means `harmony assign` commands will only ever assign users that are specified with the `+<userlogin>` syntax."
, "More commonly, you want Harmony to at least assign either a team or a user from a team when you say `harmony assign teamname`;"
, "It's suggested to either `harmony config assignUsers true` or `harmony config assignTeams true` (or both)."
"`requestUsers` and `requestTeams` are both False."
, "This means `harmony request` commands will only ever request reviews from users that are specified with the `+<userlogin>` syntax."
, "More commonly, you want Harmony to at least request review for either a team or a user from a team when you say `harmony request teamname`;"
, "It's suggested to either `harmony config requestUsers true` or `harmony config requestTeams true` (or both)."
]) <+> hardline
else Right ()

Expand Down Expand Up @@ -126,11 +126,14 @@ update : Functor f => (String -> f a) -> (a -> b -> b) -> b -> String -> f b
update f g c = map (flip g c) . f

propSetter : SettableProp n h -> (Config -> String -> Maybe Config)
propSetter AssignTeams = update parseBool (\b => { assignTeams := b })
propSetter AssignUsers = update parseBool (\b => { assignUsers := b })
propSetter CommentOnAssign = update parseBool (\b => { commentOnAssign := b })
propSetter DefaultRemote = update Just (\s => { defaultRemote := s })
propSetter GithubPAT = update Just (\s => { githubPAT := Just $ hide s })
propSetter RequestTeams = update parseBool (\b => { requestTeams := b })
propSetter RequestUsers = update parseBool (\b => { requestUsers := b })
propSetter CommentOnRequest = update parseBool (\b => { commentOnRequest := b })
propSetter DefaultRemote = update Just (\s => { defaultRemote := s })
propSetter GithubPAT = update Just (\s => { githubPAT := Just $ hide s })
propSetter AssignTeams = update parseBool (\b => { requestTeams := b })
propSetter AssignUsers = update parseBool (\b => { requestUsers := b })
propSetter CommentOnAssign = update parseBool (\b => { commentOnRequest := b })

||| Attempt to set a property and value given String representations.
||| After setting, write the config and return the updated result.
Expand All @@ -147,11 +150,14 @@ setConfig @{config} prop value with (settablePropNamed prop)
writeConfig config'

propGetter : SettableProp n h -> (Config -> String)
propGetter AssignTeams = show . assignTeams
propGetter AssignUsers = show . assignUsers
propGetter CommentOnAssign = show . commentOnAssign
propGetter DefaultRemote = show . defaultRemote
propGetter GithubPAT = maybe "Not set (will use $GITHUB_PAT environment variable)" show . githubPAT
propGetter RequestTeams = show . requestTeams
propGetter RequestUsers = show . requestUsers
propGetter CommentOnRequest = show . commentOnRequest
propGetter DefaultRemote = show . defaultRemote
propGetter GithubPAT = maybe "Not set (will use $GITHUB_PAT environment variable)" show . githubPAT
propGetter AssignTeams = show . requestTeams
propGetter AssignUsers = show . requestUsers
propGetter CommentOnAssign = show . commentOnRequest

export
getConfig : Config =>
Expand Down Expand Up @@ -215,14 +221,14 @@ createConfig envGithubPAT terminalColors editor = do
putStrLn "What GitHub remote repo would you like to use harmony for\{remoteDefaultStr}?"
defaultRemote <- orIfEmpty (Just remoteGuess) . trim <$> getLine

commentOnAssign <-
yesNoPrompt "Would you like harmony to comment when it assigns reviewers?"
commentOnRequest <-
yesNoPrompt "Would you like harmony to comment when it requests reviewers?"

assignTeams <-
yesNoPrompt "Would you like harmony to assign teams when it assigns reviewers?"
requestTeams <-
yesNoPrompt "Would you like harmony to request reviews from teams when it requests reviewers?"

assignUsers <-
yesNoPrompt "Would you like harmony to assign individual users when it assigns reviewers?"
requestUsers <-
yesNoPrompt "Would you like harmony to request reviews from individual users when it requests a teams review?"

_ <- liftIO $ octokit pat
putStrLn "Creating config..."
Expand All @@ -242,9 +248,9 @@ createConfig envGithubPAT terminalColors editor = do
, repo
, defaultRemote
, mainBranch
, assignTeams
, assignUsers
, commentOnAssign
, requestTeams
, requestUsers
, commentOnRequest
, teamSlugs
, repoLabels
, orgMembers
Expand Down
Loading