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