From 3d224a507f335c8807678eea2b4e1b8352d3be69 Mon Sep 17 00:00:00 2001 From: yoko <25644062+sidemt@users.noreply.github.com> Date: Fri, 19 Apr 2024 19:57:49 +0900 Subject: [PATCH] feat: assign translator or reviewer according to slash command (#313) * feat: assign commenter as a translator * feat: assgin reviewer, organize scripts into separate files * fix: simplify message --- .github/workflows/slash-command-action.yml | 100 +++++++++++++++------ scripts/assignReviewer.js | 64 +++++++++++++ scripts/assignTranslator.js | 47 ++++++++++ 3 files changed, 184 insertions(+), 27 deletions(-) create mode 100644 scripts/assignReviewer.js create mode 100644 scripts/assignTranslator.js diff --git a/.github/workflows/slash-command-action.yml b/.github/workflows/slash-command-action.yml index a0af441..6ae6305 100644 --- a/.github/workflows/slash-command-action.yml +++ b/.github/workflows/slash-command-action.yml @@ -5,39 +5,41 @@ name: Issue Comments permissions: issues: write jobs: - check_comments: - name: Check comments for commands + validate_command: + name: Validate command runs-on: ubuntu-latest - - # Run the jobs only if the new comment starts with '/' + # Run the job only if the new comment starts with '/' if: | !github.event.issue.pull_request && startsWith(github.event.comment.body, '/') + outputs: + valid_command: ${{ steps.command_check.outputs.valid_command }} + command: ${{ steps.command_check.outputs.command }} + target_status: ${{ steps.command_check.outputs.target_status }} # Which status to move the card to steps: - name: Validate command id: command_check run: | if [[ "${{ github.event.comment.body }}" == /translate* ]]; then - echo "COMMAND=translate" >> $GITHUB_ENV - echo "TARGET_STATUS=in Translation" >> $GITHUB_ENV - echo "VALID_COMMAND=true" >> $GITHUB_ENV + echo "::set-output name=command::translate" + echo "::set-output name=target_status::in Translation" + echo "::set-output name=valid_command::true" elif [[ "${{ github.event.comment.body }}" == /review* ]]; then - echo "COMMAND=review" >> $GITHUB_ENV - echo "TARGET_STATUS=in Review" >> $GITHUB_ENV - echo "VALID_COMMAND=true" >> $GITHUB_ENV + echo "::set-output name=command::review" + echo "::set-output name=target_status::in Review" + echo "::set-output name=valid_command::true" else echo "::warning::Invalid command. The comment must start with /translate or /review." - echo "VALID_COMMAND=false" >> $GITHUB_ENV + echo "::set-output name=valid_command::false" fi # Reply to the user if the command was invalid - - name: Comment on issue # Notify user if the command was invalid - if: env.VALID_COMMAND == 'false' + - name: Feedback for invalid command + if: steps.command_check.outputs.valid_command != 'true' uses: actions/github-script@v7 with: script: | const issueComment = ` @${{ github.event.comment.user.login }} The command was invalid. The comment must start with /translate or /review. - If you are not sure how to use the command, reach out to your language lead. Thank you for contributing! `; github.rest.issues.createComment({ issue_number: context.issue.number, @@ -47,14 +49,18 @@ jobs: }); github-token: ${{ secrets.GITHUB_TOKEN }} - # Update the status according to the command - - name: Act on the command # Process valid commands - if: env.VALID_COMMAND == 'true' - run: | - echo "The command is '${{ env.COMMAND }}'" - echo "github.event.issue.node_id is ${{ github.event.issue.node_id }}" + get_project_card_info: + name: Get project card information + needs: validate_command + if: needs.validate_command.outputs.valid_command == 'true' + runs-on: ubuntu-latest + outputs: + cardId: ${{ steps.get_card.outputs.cardId }} + projectUrl: ${{ steps.get_card.outputs.projectUrl }} + projectName: ${{ steps.get_card.outputs.projectName }} + steps: - name: Get project card - if: env.VALID_COMMAND == 'true' + id: get_card uses: actions/github-script@v7 env: ISSUE_NODE_ID: ${{ github.event.issue.node_id }} @@ -69,6 +75,7 @@ jobs: node { id url + title items(first: 100) { edges { node { @@ -93,20 +100,59 @@ jobs: }; const result = await github.graphql(query, variables); console.log(`Result: ${JSON.stringify(result)}`); + const cardId = result.node.projectsV2.edges[0].node.items.edges.find(edge => edge.node.content.id === process.env.ISSUE_NODE_ID).node.id; const projectUrl = result.node.projectsV2.edges[0].node.url; + const projectName = result.node.projectsV2.edges[0].node.title; + console.log(`Card ID: ${cardId}`); console.log(`Project URL: ${projectUrl}`); + console.log(`Project Name: ${projectName}`); + core.setOutput('cardId', cardId); core.setOutput('projectUrl', projectUrl); + core.setOutput('projectName', projectName); github-token: ${{ secrets.MOVE_CARDS_TOKEN }} - id: get_card - - name: Update project - if: env.VALID_COMMAND == 'true' + + assign_issue: + name: Assign issue + needs: [validate_command, get_project_card_info] + if: needs.validate_command.outputs.valid_command == 'true' + runs-on: ubuntu-latest + steps: + - name: Checkout # Prepare to run the following scripts + uses: actions/checkout@v3 + - name: Assign issue to the commenter as translator + if: needs.validate_command.outputs.command == 'translate' + uses: actions/github-script@v7 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + script: | + const script = require('./scripts/assignTranslator.js'); + await script({github, context, core}); + - name: Assign issue to the reviewer + if: needs.validate_command.outputs.command == 'review' + uses: actions/github-script@v7 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + script: | + const script = require('./scripts/assignReviewer.js'); + const projectName = '${{ needs.get_project_card_info.outputs.projectName }}'; + await script({github, context, core, projectName}); + + update_project_card: + name: Update project card + needs: [validate_command, get_project_card_info, assign_issue] + if: needs.validate_command.outputs.valid_command == 'true' + runs-on: ubuntu-latest + steps: + - name: Update project card status uses: titoportas/update-project-fields@v0.1.0 with: - project-url: ${{ steps.get_card.outputs.projectUrl }} + project-url: ${{ needs.get_project_card_info.outputs.projectUrl }} github-token: ${{ secrets.MOVE_CARDS_TOKEN }} - item-id: ${{ steps.get_card.outputs.cardId }} + item-id: ${{ needs.get_project_card_info.outputs.cardId }} field-keys: Status - field-values: ${{ env.TARGET_STATUS }} + field-values: ${{ needs.validate_command.outputs.target_status }} diff --git a/scripts/assignReviewer.js b/scripts/assignReviewer.js new file mode 100644 index 0000000..6141430 --- /dev/null +++ b/scripts/assignReviewer.js @@ -0,0 +1,64 @@ +module.exports = async ({github, context, core, projectName}) => { + let issueComment = ''; + + // List of the language leads + const languageLeads = { + spanish: 'RafaelDavisH', + portuguese: 'DanielRosa74', + italian: 'Dario-DC', + japanese: 'sidemt', + ukrainian: 'anastasiiauk', + chinese: 'miyaliu666', + korean: 'AlisonYoon', + 'test project': 'sidemt' // for testing + } + + try { + // Expecting the project name to be in "[NEWS I18N] - Spanish" format + const language = projectName.split('-')[1].trim().toLowerCase(); + const languageLead = languageLeads[language]; + if (!languageLead) { + throw new Error(`Language lead not found for language: ${language}`); + } + + // Make sure that the current user is the translator for this article, + // then assign it to the language lead for review + const issue = await github.rest.issues.get({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number + }); + + if (issue.data.assignees.some(assignee => assignee.login === context.actor)) { + const response = await github.rest.issues.addAssignees({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + assignees: [languageLead] + }); + if (response.status.toString().startsWith('2')) { + issueComment = `@${ languageLead } This article is ready for review.`; + } else { + console.error('addAssignees returned HTTP status:', response.status); + issueComment = `@${ context.actor } Something went wrong while assigning the issue to the reviewer. (HTTP status: ${ response.status }) + Please contact your language lead if the problem persists.`; + core.setFailed('addAssignees returned an unexpected HTTP status.'); + } + } else { + issueComment = `@${ context.actor } Could not assign this issue to the reviewer. + Check if you have translated this article as an assignee.`; + core.setFailed('The commenter was not the translator of this issue.'); + } + } catch (error) { + console.error('An error has occurred while assigning the issue:', error); + issueComment = `@${ context.actor } An error has occurred while assigning the issue to the reviewer. + Please contact your language lead if the problem persists.`; + core.setFailed('An error has occurred while assigning the issue.'); + } + await github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: issueComment + }); +} diff --git a/scripts/assignTranslator.js b/scripts/assignTranslator.js new file mode 100644 index 0000000..228cebc --- /dev/null +++ b/scripts/assignTranslator.js @@ -0,0 +1,47 @@ +module.exports = async ({github, context, core}) => { + let issueComment = ''; + + // Check that no one else is assigned yet before assigning the current user + try { + const issue = await github.rest.issues.get({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number + }); + + if (issue.data.assignees.length === 0) { + const response = await github.rest.issues.addAssignees({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + assignees: [context.actor] + }); + if (response.status.toString().startsWith('2')) { + issueComment = `@${ context.actor } We have assigned this article to you.`; + } else { + console.error('addAssignees returned HTTP status:', response.status); + issueComment = `@${ context.actor } Something went wrong while assigning the issue. (HTTP status: ${ response.status }) + Please contact your language lead if the problem persists.`; + core.setFailed('addAssignees returned an unexpected HTTP status.'); + } + } else if (issue.data.assignees.length === 1 && issue.data.assignees[0].login === context.actor) { + issueComment = `@${ context.actor } The article is already assigned to you.`; + core.setFailed('The article is already assigned to the commenter.'); + } else { + issueComment = `@${ context.actor } The article is already assigned to someone else. + Please choose a different article to translate.`; + core.setFailed('The article is already assigned to other contributor.'); + } + } catch (error) { + console.error('An error has occurred while assigning the issue:', error); + issueComment = `@${ context.actor } An error has occurred while assigning the issue to you. + Please contact your language lead if the problem persists.`; + core.setFailed('An error has occurred while assigning the issue.'); + } + await github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: issueComment + }); +}