code.vegaprotocol.io/vega@v0.79.0/.github/workflows/project_management.yml (about)

     1  # yamllint disable rule:line-length
     2  
     3  ---
     4  
     5  
     6  name: "Project Board Automation"
     7  
     8  "on":
     9    pull_request:
    10      branches: [develop, master]
    11      types: [synchronize, opened, reopened, labeled, unlabeled, ready_for_review, review_requested, converted_to_draft, closed]
    12    pull_request_review:
    13      types: [submitted]
    14  
    15  env:
    16    ORGANIZATION: vegaprotocol
    17    PROJECT_NUMBER: 106
    18    PR_URL: ${{ github.event.pull_request.html_url }}
    19    GH_TOKEN: ${{ secrets.PROJECT_MANAGE_ACTION }}
    20    EXCLUDE_LABEL: 'no-issue'
    21    IN_PROGRESS_COLUMN_NAME: '"In Progress"'
    22    REVIEW_REQUIRED_COLUMN_NAME: '"Waiting Review"'
    23    IN_REVIEW_COLUMN_NAME: '"In Review"'
    24    APPROVED_COLUMN_NAME: '"Approved"'
    25    MERGED_COLUMN_NAME: '"Merged"'
    26    DONE_COLUMN_NAME: '"Done"'
    27    ITERATION_FIELD_NAME: 'Sprint'
    28    USER: ${{ github.actor }}
    29  
    30  concurrency:
    31    group: ${{ github.workflow }}-${{ github.ref }}
    32    cancel-in-progress: true
    33  
    34  jobs:
    35    project-board-automation:
    36      name: "Linked Issues & Project Board Automation"
    37      runs-on: ubuntu-latest
    38      permissions: write-all
    39      steps:
    40  
    41        - name: "Get linked issue id and state"
    42          id: linked-issue
    43          env:
    44            GH_TOKEN: ${{ secrets.PROJECT_MANAGE_ACTION }}
    45          run: |
    46            gh api graphql -f query='
    47              query($pr_url: URI!) {
    48                resource(url: $pr_url) {
    49                  ... on PullRequest {
    50                    closingIssuesReferences(last: 1) {
    51                      nodes {
    52                        id
    53                        state
    54                      }
    55                    }
    56                  }
    57                }
    58              }'  -f pr_url=$PR_URL > data.json
    59            echo 'LINKED_ISSUE_STATE='$(jq '.data.resource.closingIssuesReferences.nodes[] | .state' data.json) >> $GITHUB_ENV
    60            echo 'LINKED_ISSUE_ID='$(jq '.data.resource.closingIssuesReferences.nodes[] | .id' data.json) >> $GITHUB_ENV
    61        - name: "Check if PR raised by bot"
    62          id: bot-pr
    63          if: |
    64            ((startsWith(github.head_ref, 'renovate/') == true || startsWith(github.head_ref, 'dependabot/') == true)
    65             || (github.actor == 'dependabot[bot]' || github.actor == 'renovate[bot]'))
    66          run: |
    67            echo 'BOT_PR=true' >> $GITHUB_ENV
    68        - name: "Check for linked issue"
    69          id: linked
    70          if: |
    71            env.LINKED_ISSUE_ID != '' &&
    72             contains(github.event.pull_request.labels.*.name, env.EXCLUDE_LABEL) != true
    73          uses: actions/github-script@v6.4.1
    74          with:
    75            github-token: ${{secrets.GITHUB_TOKEN}}
    76            script: |
    77              console.log("Linked Issue Found!");
    78        - name: "Check for linked issue exclusion label"
    79          id: exclude-linked
    80          if: |
    81            steps.bot-pr.outcome == 'success' || env.LINKED_ISSUE_ID == '' &&
    82             contains(github.event.pull_request.labels.*.name, env.EXCLUDE_LABEL) == true
    83          uses: actions/github-script@v6.4.1
    84          with:
    85            github-token: ${{secrets.GITHUB_TOKEN}}
    86            script: |
    87              console.log("Exclusion label added, or a bot-PR no linked issue required!");
    88        - name: "Fail if no linked issue or exclusion label"
    89          id: exclude-linked-error
    90          if: steps.linked.outcome == 'skipped' && steps.exclude-linked.outcome == 'skipped'
    91          uses: actions/github-script@v6.4.1
    92          with:
    93            github-token: ${{secrets.GITHUB_TOKEN}}
    94            script: |
    95              console.log("No linked issue or exclusion label!");
    96              core.setFailed("Link an issue and rerun, or, add the exclusion label!");
    97        - name: "Fail if linked issue AND exclusion label"
    98          id: linked-and-nochangelog
    99          if: steps.linked.outcome == 'success' && steps.exclude-linked.outcome == 'success'
   100          uses: actions/github-script@v6.4.1
   101          with:
   102            github-token: ${{secrets.GITHUB_TOKEN}}
   103            script: |
   104              console.log("Remove exclusion label, linked issue found!");
   105              core.setFailed("Remove exclusion label, linked issue found!");
   106        - name: "Get project related data"
   107          id: project-data
   108          run: |
   109            gh api graphql -f query='
   110              query($org: String!, $number: Int!) {
   111                organization(login: $org){
   112                  projectV2(number: $number) {
   113                    id
   114                    fields(first:20) {
   115                      nodes {
   116                        ... on ProjectV2Field {
   117                          id
   118                          name
   119                        }
   120                        ... on ProjectV2SingleSelectField {
   121                          id
   122                          name
   123                          options {
   124                            id
   125                            name
   126                          }
   127                        }
   128                        ... on ProjectV2IterationField {
   129                          name
   130                          id
   131                          configuration {
   132                            iterations {
   133                              id
   134                              title
   135                            }
   136                          }
   137                        }
   138                      }
   139                    }
   140                  }
   141                }
   142              }' -f org=$ORGANIZATION -F number=$PROJECT_NUMBER > data.json
   143            echo 'PROJECT_ID='$(jq '.data.organization.projectV2.id' data.json) >> $GITHUB_ENV
   144            echo 'STATUS_FIELD_ID='$(jq '.data.organization.projectV2.fields.nodes[] | select(.name== "Status") | .id' data.json) >> $GITHUB_ENV
   145            echo 'IN_PROGRESS_COLUMN='$(jq --arg in_progress ${{ env.IN_PROGRESS_COLUMN_NAME }} '.data.organization.projectV2.fields.nodes[] | select(.name== "Status") | .options[] | select(.name==$in_progress) | .id' data.json) >> $GITHUB_ENV
   146            echo 'REVIEW_REQUIRED_COLUMN='$(jq --arg review_required ${{ env.REVIEW_REQUIRED_COLUMN_NAME }} '.data.organization.projectV2.fields.nodes[] | select(.name== "Status") | .options[] | select(.name==$review_required) | .id' data.json) >> $GITHUB_ENV
   147            echo 'IN_REVIEW_COLUMN='$(jq --arg in_review ${{ env.IN_REVIEW_COLUMN_NAME }} '.data.organization.projectV2.fields.nodes[] | select(.name== "Status") | .options[] | select(.name==$in_review) | .id' data.json) >> $GITHUB_ENV
   148            echo 'APPROVED_COLUMN='$(jq --arg approved ${{ env.APPROVED_COLUMN_NAME }} '.data.organization.projectV2.fields.nodes[] | select(.name== "Status") | .options[] | select(.name==$approved) | .id' data.json) >> $GITHUB_ENV
   149            echo 'MERGED_COLUMN='$(jq --arg merged ${{ env.MERGED_COLUMN_NAME }} '.data.organization.projectV2.fields.nodes[] | select(.name== "Status") | .options[] | select(.name==$merged) | .id' data.json) >> $GITHUB_ENV
   150            echo 'DONE_COLUMN='$(jq --arg done ${{ env.DONE_COLUMN_NAME }} '.data.organization.projectV2.fields.nodes[] | select(.name== "Status") | .options[] | select(.name==$done) | .id' data.json) >> $GITHUB_ENV
   151            echo 'ITERATION_FIELD_ID='$(jq --arg iteration_field_name $ITERATION_FIELD_NAME '.data.organization.projectV2.fields.nodes[] | select(.name==$iteration_field_name) | .id' data.json) >> $GITHUB_ENV
   152            echo 'CURRENT_ITERATION='$(jq --arg iteration_field_name $ITERATION_FIELD_NAME '.data.organization.projectV2.fields.nodes[] | select(.name==$iteration_field_name) | .configuration.iterations[0] | .id' data.json) >> $GITHUB_ENV
   153  
   154        - name: "Get PR state, review decision and author"
   155          id: pr-status
   156          run: |
   157            gh api graphql -f query='
   158              query($pr_url: URI!) {
   159                resource(url: $pr_url) {
   160                  ... on PullRequest {
   161                    reviewThreads(last: 100) {
   162                      edges {
   163                        node {
   164                          isResolved
   165                        }
   166                      }
   167                    }
   168                    reviewDecision
   169                    id
   170                    isDraft
   171                    state
   172                    author{
   173                      login
   174                    }
   175                    reviews(last: 1) {
   176                      nodes {
   177                        state
   178                      }
   179                    }
   180                  }
   181                }
   182              }'  -f pr_url=$PR_URL > data.json
   183            echo 'REVIEW_DECISION='$(jq '.data.resource | .reviewDecision' data.json) >> $GITHUB_ENV
   184            echo 'NUM_REVIEWS='$(jq '.data.resource.reviews.nodes | length' data.json) >> $GITHUB_ENV
   185            echo 'LATEST_REVIEW_STATE='$(jq '.data.resource.reviews.nodes[] | .state' data.json) >> $GITHUB_ENV
   186            echo 'IS_PR_DRAFT='$(jq '.data.resource | .isDraft' data.json) >> $GITHUB_ENV
   187            echo 'PR_STATE='$(jq '.data.resource | .state' data.json) >> $GITHUB_ENV
   188            echo 'PR_ID='$(jq '.data.resource | .id' data.json) >> $GITHUB_ENV
   189            echo 'AUTHOR_NAME='$(jq '.data.resource.author | .login' data.json) >> $GITHUB_ENV
   190        - name: "Get PR author id"
   191          id: pr-author
   192          if: steps.pr-status.outcome == 'success'
   193          run: |
   194            gh api graphql -f query='
   195              query($author_name: String!) {
   196                search (query: $author_name, type: USER, first: 1){
   197                  edges {
   198                    node {
   199                      ... on User {
   200                        id
   201                      }
   202                    }
   203                  }
   204                }
   205              }'  -f author_name=$AUTHOR_NAME > data.json
   206            echo 'AUTHOR_ID='$(jq '.data.search.edges[] | .node | .id' data.json) >> $GITHUB_ENV
   207        - name: "Add linked issue to the project"
   208          id: issue-to-project
   209          if: |
   210            env.LINKED_ISSUE_ID != '' &&
   211             contains(github.event.pull_request.labels.*.name, env.EXCLUDE_LABEL) != true
   212          run: |
   213            issue_item_id="$( gh api graphql -f query='
   214              mutation($user:String!, $project:ID!, $issue:ID!) {
   215                addProjectV2ItemById(input: {clientMutationId: $user, projectId: $project, contentId: $issue}) {
   216                  item {
   217                    id
   218                  }
   219                }
   220              }' -f project=$PROJECT_ID -f issue=$LINKED_ISSUE_ID -f user=$USER --jq '.data.addProjectV2ItemById.item.id')"
   221            echo 'BOARD_ITEM_ID='$issue_item_id >> $GITHUB_ENV
   222        - name: "Add exclusion labeled PR to the project"
   223          id: pr-to-project
   224          if: |
   225            steps.issue-to-project.outcome == 'skipped' &&
   226             contains(github.event.pull_request.labels.*.name, env.EXCLUDE_LABEL) == true
   227          run: |
   228            pr_item_id="$( gh api graphql -f query='
   229              mutation($user:String!, $project:ID!, $pr:ID!) {
   230                addProjectV2ItemById(input: {clientMutationId: $user, projectId: $project, contentId: $pr}) {
   231                  item {
   232                    id
   233                  }
   234                }
   235              }' -f project=$PROJECT_ID -f pr=$PR_ID  -f user=$USER --jq '.data.addProjectV2ItemById.item.id')"
   236            echo 'BOARD_ITEM_ID='$pr_item_id >> $GITHUB_ENV
   237        - name: "Add BOT PR to the project"
   238          id: bot-pr-to-project
   239          if: steps.bot-pr.outcome == 'success'
   240          run: |
   241            pr_item_id="$( gh api graphql -f query='
   242              mutation($user:String!, $project:ID!, $pr:ID!) {
   243                addProjectV2ItemById(input: {clientMutationId: $user, projectId: $project, contentId: $pr}) {
   244                  item {
   245                    id
   246                  }
   247                }
   248              }' -f project=$PROJECT_ID -f pr=$PR_ID  -f user=$USER --jq '.data.addProjectV2ItemById.item.id')"
   249            echo 'BOARD_ITEM_ID='$pr_item_id >> $GITHUB_ENV
   250        - name: "Reopen if the linked issue closed"
   251          id: reopen-issue
   252          if: steps.issue-to-project.outcome == 'success' && env.LINKED_ISSUE_STATE == '"CLOSED"' && env.PR_STATE != '"MERGED"'
   253          run: |
   254            gh api graphql -f query='
   255              mutation($clientMutationId:String!, $issueId:ID!) {
   256                reopenIssue(input: {clientMutationId:$clientMutationId, issueId:$issueId}) {
   257                  issue{
   258                    id
   259                  }
   260                }
   261              }' -f clientMutationId=$AUTHOR_ID -f issueId=$LINKED_ISSUE_ID
   262  
   263        - run: env
   264  
   265        - name: "Set draft work item for progress column"
   266          id: draft-pr
   267          if: github.event.pull_request.draft == 'true' || env.IS_PR_DRAFT == 'true'
   268          run: |
   269            echo 'ITEM_ID='${{  env.BOARD_ITEM_ID }} >> $GITHUB_ENV
   270            echo 'CURRENT_ITERATION='$CURRENT_ITERATION >> $GITHUB_ENV
   271            echo 'CURRENT_STATUS='${{ env.IN_PROGRESS_COLUMN }} >> $GITHUB_ENV
   272        - name: "Set work item for review required column"
   273          id: review-required
   274          if: |
   275            github.event.action == 'ready_for_review' ||
   276             env.IS_PR_DRAFT != 'true' && env.NUM_REVIEWS == 0 && env.REVIEW_DECISION == '"REVIEW_REQUIRED"'
   277          run: |
   278            echo 'ITEM_ID='${{  env.BOARD_ITEM_ID }} >> $GITHUB_ENV
   279            echo 'CURRENT_STATUS='${{ env.REVIEW_REQUIRED_COLUMN }} >> $GITHUB_ENV
   280        - name: "Set work item for in review column"
   281          id: changes-requested
   282          if: |
   283            (steps.draft-pr.outcome == 'skipped' && env.NUM_REVIEWS > 0 &&
   284             (env.REVIEW_DECISION == '"CHANGES_REQUESTED"' || env.LATEST_REVIEW_STATE == '"COMMENTED"'
   285             || env.LATEST_REVIEW_STATE == '"DISMISSED"'))
   286          run: |
   287            echo 'ITEM_ID='${{  env.BOARD_ITEM_ID }} >> $GITHUB_ENV
   288            echo 'CURRENT_STATUS='${{ env.IN_REVIEW_COLUMN }} >> $GITHUB_ENV
   289        - name: "Set work item for approved column"
   290          id: approved
   291          if: |
   292            (steps.draft-pr.outcome == 'skipped' && (env.REVIEW_DECISION == '"APPROVED"'
   293             || env.LATEST_REVIEW_STATE == '"APPROVED"'))
   294          run: |
   295            echo 'ITEM_ID='${{  env.BOARD_ITEM_ID }} >> $GITHUB_ENV
   296            echo 'CURRENT_STATUS='${{ env.APPROVED_COLUMN }} >> $GITHUB_ENV
   297        - name: "Set work item for merged column"
   298          id: merged
   299          if: env.PR_STATE == '"MERGED"' || github.event.pull_request.merged == true
   300          run: |
   301            echo 'ITEM_ID='${{  env.BOARD_ITEM_ID }} >> $GITHUB_ENV
   302            echo 'CURRENT_STATUS='${{ env.MERGED_COLUMN }} >> $GITHUB_ENV
   303        - name: "Close linked issue when PR merged"
   304          id: close-issue
   305          if: steps.merged.outcome == 'success' && steps.linked.outcome == 'success'
   306          run: |
   307            gh api graphql -f query='
   308              mutation($clientMutationId:String!, $issueId:ID!) {
   309                closeIssue(input: {clientMutationId:$clientMutationId, issueId:$issueId}) {
   310                  issue{
   311                    id
   312                  }
   313                }
   314              }' -f clientMutationId=$AUTHOR_ID -f issueId=$LINKED_ISSUE_ID
   315        - name: "Set closed PR for done column"
   316          id: closed-pr
   317          if: steps.exclude-linked.outcome == 'success' && github.event.pull_request.closed == true
   318          run: |
   319            echo 'ITEM_ID='${{  env.BOARD_ITEM_ID }} >> $GITHUB_ENV
   320            echo 'CURRENT_STATUS='${{ env.DONE_COLUMN }} >> $GITHUB_ENV
   321        - name: "Set closed PR issue to in progress column"
   322          id: closed-pr-with-issue
   323          if: steps.linked.outcome == 'success' && github.event.pull_request.merged == true && env.PR_STATE != '"MERGED"'
   324          run: |
   325            echo 'ITEM_ID='${{  env.BOARD_ITEM_ID }} >> $GITHUB_ENV
   326            echo 'CURRENT_STATUS='${{ env.IN_PROGRESS_COLUMN }} >> $GITHUB_ENV
   327  
   328        - name: Move project board item
   329          run: |
   330            gh api graphql -f query='
   331              mutation (
   332                $project: ID!
   333                $item: ID!
   334                $status_field: ID!
   335                $status_value: String!
   336                $iteration_field: ID!
   337                $iteration_value: String!
   338              ) {
   339                set_status: updateProjectV2ItemFieldValue(input: {
   340                  projectId: $project
   341                  itemId: $item
   342                  fieldId: $status_field
   343                  value: {
   344                    singleSelectOptionId: $status_value
   345                    }
   346                }) {
   347                  projectV2Item {
   348                    id
   349                    }
   350                }
   351                set_iteration: updateProjectV2ItemFieldValue(input: {
   352                  projectId: $project
   353                  itemId: $item
   354                  fieldId: $iteration_field
   355                  value: {
   356                    iterationId: $iteration_value
   357                    }
   358                }) {
   359                  projectV2Item {
   360                    id
   361                  }
   362                }
   363              }' -f project=$PROJECT_ID \
   364                -f item=$ITEM_ID \
   365                -f status_field=$STATUS_FIELD_ID \
   366                -f status_value=${{ env.CURRENT_STATUS }} \
   367                -f iteration_field=$ITERATION_FIELD_ID \
   368                -f iteration_value=${{ env.CURRENT_ITERATION }}
   369    verify-changelog-updated:
   370      name: "Verify CHANGELOG Updated"
   371      needs: project-board-automation
   372      runs-on: ubuntu-latest
   373      if: |
   374        github.event.pull_request.draft != true
   375         && contains(github.event.pull_request.labels.*.name, 'no-changelog') != true
   376      steps:
   377        - name: "Checkout"
   378          id: checkout
   379          uses: actions/checkout@v3.5.3
   380        - name: "Check changelog entry"
   381          id: check-changelog
   382          uses: Zomzog/changelog-checker@v1.3.0
   383          with:
   384            fileName: CHANGELOG.md
   385            noChangelogLabel: "no-changelog"
   386            checkNotification: Simple
   387          env:
   388            GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
   389  # yamllint enable rule:line-length