diff --git a/docs/AUTOMATIC_CARD_MOVEMENT.md b/docs/AUTOMATIC_CARD_MOVEMENT.md index af032d03..e502db9f 100644 --- a/docs/AUTOMATIC_CARD_MOVEMENT.md +++ b/docs/AUTOMATIC_CARD_MOVEMENT.md @@ -12,8 +12,9 @@ The following table summarizes triggers and the card movements those cause. | Trigger | Action | | :--- | :--- | | Branch with `{issueNumber}-some-description` created | Issue moves to `IN_PROGRESS` column and is assigned to branch author | -| Draft PR referencing issue created | Issue moves to `IN_PROGRESS` column | -| PR is marked as ready-for-review | PR and linked issue move to `IN_REVIEW` column | -| Non-draft PR is created | PR and linked issue move to `IN_REVIEW` column | +| Collaborator PR is created | PR and linked issue move to `IN_REVIEW` or `IN_PROGRESS` (if PR is draft) column | +| Collaborator PR is marked as draft | PR and linked issue move to `IN_PROGRESS` column | +| Collaborator PR is marked as ready-for-review | PR and linked issue move to `IN_REVIEW` column | +| Collaborator PR receives `changes_requested` review | PR moves to `IN_PROGRESS` column | | PR is merged | PR and linked issue move to `DONE` column | | PR is closed unmerged | PR moves to `DONE` column, linked issue stays where it is | diff --git a/docs/CONFIG.md b/docs/CONFIG.md index 97930e04..542532ee 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -57,6 +57,12 @@ Several aspects of [wuffle](https://wuffle.dev) are configured via environment v | `DISABLE_BACKGROUND_SYNC` | | | +### Behavior + +| Parameter | Required? | Description | +| :--- | :---: | :--- | +| `AUTO_ASSIGN_PULLS` | | If set, assign newly created collaborator PRs to the PR author | + ### Misc | Parameter | Required? | Description | diff --git a/packages/app/lib/apps/automatic-dev-flow.js b/packages/app/lib/apps/automatic-dev-flow.js index 02a3da05..b1a5ba31 100644 --- a/packages/app/lib/apps/automatic-dev-flow.js +++ b/packages/app/lib/apps/automatic-dev-flow.js @@ -74,16 +74,26 @@ module.exports = function(webhookEvents, githubIssues, columns) { pull_request } = context.payload; + const external = isExternal(pull_request); + const draft = isDraft(pull_request); + const newState = - isExternal(pull_request) ? EXTERNAL_CONTRIBUTION : ( - isDraft(pull_request) ? IN_PROGRESS : IN_REVIEW + external ? EXTERNAL_CONTRIBUTION : ( + draft ? IN_PROGRESS : IN_REVIEW ); const column = columns.getByState(newState); + const author = pull_request.user; + + const newAssignee = ( + process.env.AUTO_ASSIGN_PULLS && !external && + author && author.type === 'User' && author.login + ); + await Promise.all([ - githubIssues.moveIssue(context, pull_request, column), - githubIssues.moveReferencedIssues(context, pull_request, column) + githubIssues.moveIssue(context, pull_request, column, newAssignee), + githubIssues.moveReferencedIssues(context, pull_request, column, newAssignee) ]); }); diff --git a/packages/app/lib/apps/events-sync.js b/packages/app/lib/apps/events-sync.js index ddca98e9..cb77472c 100644 --- a/packages/app/lib/apps/events-sync.js +++ b/packages/app/lib/apps/events-sync.js @@ -101,6 +101,7 @@ function EventsSync(webhookEvents, store, logger) { 'pull_request.unlabeled', 'pull_request.edited', 'pull_request.ready_for_review', + 'pull_request.converted_to_draft', 'pull_request.assigned', 'pull_request.unassigned', 'pull_request.synchronize', diff --git a/packages/app/lib/apps/github-issues/GithubIssues.js b/packages/app/lib/apps/github-issues/GithubIssues.js index 5b6958e5..3e658398 100644 --- a/packages/app/lib/apps/github-issues/GithubIssues.js +++ b/packages/app/lib/apps/github-issues/GithubIssues.js @@ -7,6 +7,8 @@ const { CLOSES } = linkTypes; +const gql = require('fake-tag'); + /** * @constructor @@ -76,6 +78,20 @@ function GithubIssues(logger, config, columns) { }; } + function getDraftUpdate(issue, newColumn) { + if ('draft' in issue) { + const draft = columns.getByState('IN_PROGRESS') === newColumn; + + if (draft !== issue.draft) { + return { + draft + }; + } + } + + return {}; + } + function findIssue(context, issue_number) { const params = context.repo({ issue_number }); @@ -131,12 +147,51 @@ function GithubIssues(logger, config, columns) { })); } + function updateDraftState(context, pullRequest, draft) { + + const convertToDraftQuery = gql` + mutation ConvertToDraft($pull_id: ID!) { + convertPullRequestToDraft(input: { pullRequestId: $pull_id }) { + pullRequest { + updatedAt + } + } + } + `; + + const markReadyForReviewQuery = gql` + mutation MarkReadyForReview($pull_id: ID!) { + markPullRequestReadyForReview(input: { pullRequestId: $pull_id }) { + pullRequest { + updatedAt + } + } + } + `; + + const ctx = context.repo({ + issue_number: pullRequest.number, + draft + }); + + log.info(ctx, 'set draft'); + + return context.octokit.graphql( + draft ? convertToDraftQuery : markReadyForReviewQuery, + { + pull_id: pullRequest.pull_request_node_id + } + ).catch(error => log.error(error, 'failed to set draft', ctx)); + } + function moveIssue(context, issue, newColumn, newAssignee) { const { number: issue_number } = issue; + const draftUpdate = getDraftUpdate(issue, newColumn); + const update = { ...getAssigneeUpdate(issue, newAssignee), ...getStateUpdate(issue, newColumn) @@ -163,6 +218,14 @@ function GithubIssues(logger, config, columns) { ); } + if (hasKeys(draftUpdate)) { + const { draft } = draftUpdate; + + invocations.push( + updateDraftState(context, issue, draft) + ); + } + if (hasKeys(update)) { const params = context.repo({ diff --git a/packages/app/test.js b/packages/app/test.js new file mode 100644 index 00000000..ccd0050c --- /dev/null +++ b/packages/app/test.js @@ -0,0 +1,35 @@ +const gql = require('fake-tag'); + +const { graphql } = require('@octokit/graphql'); + +const authenticatedGraphql = graphql.defaults({ + headers: { + authorization: `token ${process.env.TOKEN}` + } +}); + +const convertToDraftQuery = gql` + mutation ConvertToDraft($pull_id: ID!) { + convertPullRequestToDraft(input: { pullRequestId: $pull_id }) { + pullRequest { + updatedAt + } + } + } +`; + +const markReadyForReviewQuery = gql` + mutation MarkReadyForReview($pull_id: ID!) { + markPullRequestReadyForReview(input: { pullRequestId: $pull_id }) { + pullRequest { + updatedAt + } + } + } +`; + +authenticatedGraphql(markReadyForReviewQuery, {"pull_id": "MDExOlB1bGxSZXF1ZXN0MzI3NTkzNTc2" }).then(result => { + console.log(result); +}).catch(err => { + console.error(err); +}); \ No newline at end of file diff --git a/packages/board/src/CollaboratorLinks.svelte b/packages/board/src/CollaboratorLinks.svelte index a73dafa7..bdefb62c 100644 --- a/packages/board/src/CollaboratorLinks.svelte +++ b/packages/board/src/CollaboratorLinks.svelte @@ -102,7 +102,7 @@ margin: 0; font-size: 14px; position: relative; - display: inline-block; + display: flex; text-align: center; border-radius: 2px;