Skip to content

Commit

Permalink
Support updating linked issues
Browse files Browse the repository at this point in the history
  • Loading branch information
jarmak-nv committed Dec 20, 2023
1 parent a125a60 commit 0be7ac1
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 15 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/project-get-item-id.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ jobs:
# Use jq to do the actual filtering
item_project_id=$(jq -r '.data.node.projectItems.nodes[] |
select(.project.id == "${{ inputs.PROJECT_ID }}") |
.id' project_data.json)
select(.project.id == "${{ inputs.PROJECT_ID }}") |
.id' project_data.json)
echo "ITEM_PROJECT_ID=$item_project_id" >> $GITHUB_OUTPUT
continue-on-error: true
44 changes: 31 additions & 13 deletions .github/workflows/project-get-set-iteration-field.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,29 @@ on:
type: string
required: true

ITERATION_FIELD_NAME:
description: "The name of the iteration field"
type: string
required: true

ITERATION_FIELD_ID:
description: "The graphQL node ID of the iteration field"
type: string
required: true

ITEM_PROJECT_ID:
description: "The issue or PR's graphQL node ID"
description: "The issue or PR's graphQL project-specific ID"
type: string
required: true

# Optional fields, used if UPDATE_ITEM is set to true
UPDATE_ITEM:
description: "Whether to update the item's iteration field"
default: false
type: boolean

# Optional fields, used if UPDATE_ITEM is set to true
ITEM_NODE_ID:
description: "The issue or PR's graphQL node ID"
description: "The issue or PR's graphQL node ID, only needed if updating linked issues"
default: null
type: string

Expand Down Expand Up @@ -60,38 +65,38 @@ jobs:
env:
GH_TOKEN: ${{ secrets.PROJECT_MANAGEMENT_SECRET }}
run: |
# Get current sprint iteration id
# The current sprint is always the first iteration in the list
# Get current iteration iteration id
# The current iteration is always the first element in the returned list
gh api graphql -f query='
query {
node(id: "${{ inputs.PROJECT_ID }}") {
... on ProjectV2 {
id
field(name: "Sprint") {
field(name: "${{ inputs.ITERATION_FIELD_NAME }}") {
... on ProjectV2IterationField {
id
name
configuration {
iterations {
id
iterations {
id
}
}
}
}
}
}
}' > sprint_option_data.json
current_sprint_option_id=$(jq -r '.data.node.field.configuration.iterations[0].id' sprint_option_data.json)
echo "ITERATION_OPTION_ID=$current_sprint_option_id" >> "$GITHUB_OUTPUT"
}' > iteration_option_data.json
current_iteration_option_id=$(jq -r '.data.node.field.configuration.iterations[0].id' iteration_option_data.json)
echo "ITERATION_OPTION_ID=$current_iteration_option_id" >> "$GITHUB_OUTPUT"
- name: Update item iteration field
id: update_item_iteration_field
if: ${{ inputs.UPDATE_ITEM == true }}
env:
GH_TOKEN: ${{ secrets.PROJECT_MANAGEMENT_SECRET }}
run: |
# Set the sprint based on the query above
# This overwrites whatever was in it before
# Set the iteration based on the query above
# This overwrites whatever was in it before, we may want to make an "OVERWRITE" option
gh api graphql -f query='
mutation {
updateProjectV2ItemFieldValue(
Expand All @@ -110,3 +115,16 @@ jobs:
}
}'
continue-on-error: true

update_linked_issues:
if: ${{ inputs.UPDATE_LINKED_ISSUES == true }}
uses: ./.github/workflows/project-update-linked-issues.yaml
needs: get_set_iteration_option_id
with:
PROJECT_ID: ${{ inputs.PROJECT_ID }}
PR_PROJECT_ID: ${{ inputs.ITEM_PROJECT_ID }}
PR_NODE_ID: ${{ inputs.ITEM_NODE_ID }}
UPDATE_FIELD_TYPE: "iteration"
UPDATE_FIELD_ID: ${{ inputs.ITERATION_FIELD_ID }}
UPDATE_FIELD_VALUE: ${{ needs.get_set_iteration_option_id.outputs.ITERATION_OPTION_ID }}
secrets: inherit
142 changes: 142 additions & 0 deletions .github/workflows/project-update-linked-issues.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
name: Project - Update Linked Issues
# This workflow takes a PR and updates the linked issues to match the PR
# Issues do not have a connection back to the PRs, so this workflow can only be called by the PR
# It's flexible what fields you update
# This workflow will primarily be called by the 'get-set' workflows

on:
workflow_call:
inputs:
PROJECT_ID:
description: "The Project's graphQL node ID"
type: string
required: true

PR_PROJECT_ID:
description: "The PR's graphQL project-specific ID "
type: string
required: true

PR_NODE_ID:
description: "The PR's graphQL node ID"
default: null
type: string

UPDATE_FIELD_TYPE:
description: "The type of field to update - [text, number, date, single_select, iteration]"
type: string
required: true

UPDATE_FIELD_ID:
description: "The graphQL node ID of the iteration field"
type: string
required: true

UPDATE_FIELD_VALUE:
description: "The value to set the field to"
type: string
required: true

secrets:
PROJECT_MANAGEMENT_SECRET:
description: "Project Access Token"
required: true


jobs:
get_set_iteration_option_id:
runs-on: ubuntu-latest
permissions:
contents: read

steps:
- name: Sync Linked Issues
id: sync_linked_issues
env:
GITHUB_TOKEN: ${{ secrets.PROJECT_MANAGEMENT_SECRET }}
run: |
# Find the linked issues to the PR
# If an issue is passed in, the json will return null and the for loop won't trigger
# Potential future improvement could be some nicer error messaging on incorrect input
gh api graphql -f query='
query {
node(id: "${{ inputs.PR_NODE_ID }}") {
... on PullRequest {
closingIssuesReferences(first: 10) {
nodes {
projectItems(first: 10) {
nodes {id, project{id}}
}
}
}
}
}
}' > linked_issues.json
issue_ids=$(jq -r '.data.node.closingIssuesReferences.nodes[].projectItems.nodes[] |
select(.project.id == "${{ inputs.PROJECT_ID }}") | .id' linked_issues.json)
for issue_id in $issue_ids; do
# Each field type has a different `value` that is needed by the mutation.
# We could do this a little "smarter" but I like it this way in case API changes happen
# to a specific field type. I've also condensed the mutation formatting for brevity.
if [ "${{ inputs.UPDATE_FIELD_TYPE }}" == "iteration" ]; then
gh api graphql -f query='
mutation {
updateProjectV2ItemFieldValue(
input: {
projectId: "${{ inputs.PROJECT_ID }}"
itemId: "${{ inputs.PR_PROJECT_ID }}"
fieldId: "${{ inputs.UPDATE_FIELD_ID }}"
value: {iterationId: "${{ inputs.UPDATE_FIELD_VALUE }}"}})
{projectV2Item {id}}}'
elif [ "${{ inputs.UPDATE_FIELD_TYPE }}" == "single_select" ]; then
gh api graphql -f query='
mutation {
updateProjectV2ItemFieldValue(
input: {
projectId: "${{ inputs.PROJECT_ID }}"
itemId: "${{ inputs.PR_PROJECT_ID }}"
fieldId: "${{ inputs.UPDATE_FIELD_ID }}"
value: {singleSelectOptionId: "${{ inputs.UPDATE_FIELD_VALUE }}"}})
{projectV2Item {id}}}'
elif [ "${{ inputs.UPDATE_FIELD_TYPE }}" == "date" ]; then
gh api graphql -f query='
mutation {
updateProjectV2ItemFieldValue(
input: {
projectId: "${{ inputs.PROJECT_ID }}"
itemId: "${{ inputs.PR_PROJECT_ID }}"
fieldId: "${{ inputs.UPDATE_FIELD_ID }}"
value: {date: "${{ inputs.UPDATE_FIELD_VALUE }}"}})
{projectV2Item {id}}}'
elif [ "${{ inputs.UPDATE_FIELD_TYPE }}" == "text" ]; then
gh api graphql -f query='
mutation {
updateProjectV2ItemFieldValue(
input: {
projectId: "${{ inputs.PROJECT_ID }}"
itemId: "${{ inputs.PR_PROJECT_ID }}"
fieldId: "${{ inputs.UPDATE_FIELD_ID }}"
value: {text: "${{ inputs.UPDATE_FIELD_VALUE }}"}})
{projectV2Item {id}}}'
elif [ "${{ inputs.UPDATE_FIELD_TYPE }}" == "number" ]; then
gh api graphql -f query='
mutation {
updateProjectV2ItemFieldValue(
input: {
projectId: "${{ inputs.PROJECT_ID }}"
itemId: "${{ inputs.PR_PROJECT_ID }}"
fieldId: "${{ inputs.UPDATE_FIELD_ID }}"
value: {number: "${{ inputs.UPDATE_FIELD_VALUE }}"}})
{projectV2Item {id}}}'
else
echo "Invalid field type"
fi
done

0 comments on commit 0be7ac1

Please sign in to comment.