-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Issue #2495] Set points and sprint on close #2519
Merged
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
a46cb27
feat: Adds scripts and queries for set-points-and-sprint
widal001 5b969ec
ci: Adds GitHub action to set points and sprint
widal001 7777214
docs: Adds new linter to README
widal001 8b7f209
fix: Updates script permissions
widal001 8741653
feat: Enables dry-run mode for set-points-and-sprint.sh
widal001 eefafba
ci: Adds set-points-and-sprint.sh to linter CI checks
widal001 8f85c67
ci: Updates set-points-and-sprint.sh
widal001 53189c2
ci: Adds issueType to graphql for set-points-and-sprint.sh
widal001 7ef7e02
docs: Explains what UNSET_ISSUE is in ci-project-linters.yml
widal001 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
query ( | ||
$org: String! | ||
$project: Int! | ||
$sprintField: String = "Sprint" | ||
$pointsField: String = "Points" | ||
) { | ||
organization(login: $org) { | ||
projectV2(number: $project) { | ||
sprint: field(name: $sprintField) { | ||
...iterationMetadata | ||
} | ||
points: field(name: $pointsField) { | ||
...numberMetadata | ||
} | ||
} | ||
} | ||
} | ||
|
||
fragment numberMetadata on ProjectV2Field { | ||
fieldId: id | ||
} | ||
|
||
fragment iterationMetadata on ProjectV2IterationField { | ||
fieldId: id | ||
configuration { | ||
iterations { | ||
id | ||
startDate | ||
duration | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
query ( | ||
$url: URI! | ||
$sprintField: String = "Sprint" | ||
$pointsField: String = "Points" | ||
) { | ||
resource(url: $url) { | ||
... on Issue { | ||
issueType { | ||
name | ||
} | ||
# get all of the project items associated with this issue | ||
projectItems(first: 10) { | ||
nodes { | ||
... on ProjectV2Item { | ||
# Get the project ID, number, and owner, as well as itemId | ||
...projectMetadata | ||
|
||
# Get the value of the "sprint" field, if set | ||
sprint: fieldValueByName(name: $sprintField) { | ||
... on ProjectV2ItemFieldIterationValue { | ||
iterationId | ||
} | ||
} | ||
|
||
# Get the value of the "points" field, if set | ||
points: fieldValueByName(name: $pointsField) { | ||
... on ProjectV2ItemFieldNumberValue { | ||
number | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
fragment projectMetadata on ProjectV2Item { | ||
itemId: id | ||
project { | ||
projectId: id | ||
number | ||
owner { | ||
... on Organization { | ||
login | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,218 @@ | ||
#! /bin/bash | ||
# Set default values for sprint and points when those fields are unset | ||
# Usage: | ||
# ./set-points-and-sprint.sh \ | ||
# --url "https://github.com/HHS/simpler-grants-gov/issues/123" \ | ||
# --org "HHS" \ | ||
# --project 13 \ | ||
# --sprint-field "Sprint" \ | ||
# --points-field "Points" \ | ||
# --dry-run # allows users to run in dry run mode | ||
|
||
|
||
# ####################################################### | ||
# Parse command line args with format `--option arg` | ||
# ####################################################### | ||
|
||
# see this stack overflow for more details: | ||
# https://stackoverflow.com/a/14203146/7338319 | ||
while [[ $# -gt 0 ]]; do | ||
case $1 in | ||
--dry-run) | ||
echo "Running in dry run mode" | ||
dry_run=YES | ||
shift # past argument | ||
;; | ||
--url) | ||
issue_url="$2" | ||
shift # past argument | ||
shift # past value | ||
;; | ||
--org) | ||
org="$2" | ||
shift # past argument | ||
shift # past value | ||
;; | ||
--project) | ||
project="$2" | ||
shift # past argument | ||
shift # past value | ||
;; | ||
--sprint-field) | ||
sprint_field="$2" | ||
shift # past argument | ||
shift # past value | ||
;; | ||
--points-field) | ||
points_field="$2" | ||
shift # past argument | ||
shift # past value | ||
;; | ||
-*|--*) | ||
echo "Unknown option $1" | ||
exit 1 | ||
;; | ||
*) | ||
POSITIONAL_ARGS+=("$1") # save positional arg | ||
shift # past argument | ||
;; | ||
esac | ||
done | ||
|
||
# ####################################################### | ||
# Set script-specific variables | ||
# ####################################################### | ||
|
||
mkdir -p tmp | ||
raw_data_file="tmp/closed-issue-data-raw.json" | ||
item_data_file="tmp/closed-issue-data.json" | ||
field_data_file="tmp/field-data.json" | ||
item_query=$(cat "queries/getItemMetadata.graphql") | ||
field_query=$(cat "queries/getFieldMetadata.graphql") | ||
|
||
# ####################################################### | ||
# Fetch issue metadata | ||
# ####################################################### | ||
|
||
gh api graphql \ | ||
--header 'GraphQL-Features:issue_types' \ | ||
--field url="${issue_url}" \ | ||
--field sprintField="${sprint_field}" \ | ||
--field pointsField="${points_field}" \ | ||
-f query="${item_query}" > $raw_data_file | ||
|
||
# ####################################################### | ||
# Isolate the correct project item and issue type | ||
# ####################################################### | ||
|
||
# Get project item | ||
jq ".data.resource.projectItems.nodes[] | | ||
|
||
# filter for the item in the given project | ||
select(.project.number == ${project}) | | ||
|
||
# filter for items with the sprint or points fields unset | ||
select (.sprint == null or .points == null or .points.number == 0) | | ||
|
||
# format the output | ||
{ | ||
itemId, | ||
projectId: .project.projectId, | ||
sprint: .sprint.iterationId, | ||
points: .points.number, | ||
} | ||
" $raw_data_file > $item_data_file # read from raw and write to item_data_file | ||
|
||
# Get issue type | ||
issue_type=$(jq -r ".data.resource.issueType.name" $raw_data_file) | ||
|
||
# ####################################################### | ||
# Fetch project metadata | ||
# ####################################################### | ||
|
||
# If the output file contains a record | ||
if [[ -s $item_data_file ]]; then | ||
|
||
# If issue is a Task, Bug, or Enhancement, fetch the project metadata | ||
case "${issue_type}" in | ||
"Task"|"Bug"|"Enhancement") | ||
gh api graphql \ | ||
--field org="${org}" \ | ||
--field project="${project}" \ | ||
--field sprintField="${sprint_field}" \ | ||
--field pointsField="${points_field}" \ | ||
-f query="${field_query}" \ | ||
--jq ".data.organization.projectV2 | | ||
|
||
# reformat the field metadata | ||
{ | ||
points, | ||
sprint: { | ||
fieldId: .sprint.fieldId, | ||
iterationId: .sprint.configuration.iterations[0].id, | ||
} | ||
}" > $field_data_file # write output to a file | ||
;; | ||
|
||
# If it's some other type, print a message and exit | ||
*) | ||
echo "Not updating because issue has type: ${issue_type}" | ||
exit 0 | ||
;; | ||
esac | ||
|
||
# get the itemId and the projectId | ||
item_id=$(jq -r '.itemId' "$item_data_file") | ||
project_id=$(jq -r '.projectId' "$item_data_file") | ||
|
||
# otherwise print a success message and exit | ||
else | ||
echo "Both sprint and points are set for issue: ${issue_url}" | ||
exit 0 | ||
fi | ||
|
||
# ####################################################### | ||
# Set the sprint value, if empty | ||
# ####################################################### | ||
|
||
if jq -e ".points == null or .points == 0" $item_data_file > /dev/null; then | ||
|
||
if [[ $dry_run == "YES" ]]; then | ||
echo "Would set points field to 1 for issue: ${issue_url}" | ||
else | ||
echo "Setting points field to 1 for issue: ${issue_url}" | ||
# Get fieldId from field data | ||
point_field_id=$(jq -r '.points.fieldId' "$field_data_file") | ||
# Use GitHub CLI to update field | ||
gh project item-edit \ | ||
--id "${item_id}" \ | ||
--project-id "${project_id}" \ | ||
--field-id "${point_field_id}" \ | ||
--number 1 | ||
fi | ||
|
||
else | ||
echo "Point value already set for issue: ${issue_url}" | ||
fi | ||
|
||
# ####################################################### | ||
# Set the sprint value, if empty | ||
# ####################################################### | ||
|
||
if jq -e ".sprint == null" $item_data_file > /dev/null; then | ||
|
||
# Skip actual update in dry-run mode | ||
if [[ $dry_run == "YES" ]]; then | ||
echo "Would set sprint field to current sprint for issue: ${issue_url}" | ||
else | ||
echo "Setting sprint field to current sprint for issue: ${issue_url}" | ||
# Get fieldId and iterationId from field data | ||
sprint_field_id=$(jq -r '.sprint.fieldId' "$field_data_file") | ||
iteration_id=$(jq -r '.sprint.iterationId' "$field_data_file") | ||
# Use GitHub CLI to update project field | ||
gh project item-edit \ | ||
--id "${item_id}" \ | ||
--project-id "${project_id}" \ | ||
--field-id "${sprint_field_id}" \ | ||
--iteration-id "${iteration_id}" | ||
fi | ||
|
||
else | ||
echo "Sprint value already set for issue: ${issue_url}" | ||
fi | ||
|
||
# ####################################################### | ||
# Set the sprint value, if empty | ||
# ####################################################### | ||
|
||
comment="Beep boop: Automatically setting the point and sprint values for this issue " | ||
comment+="in project ${org}/${project} because they were unset when the issue was closed." | ||
|
||
# Skip actual posting in dry-run mode | ||
if [[ $dry_run == "YES" ]]; then | ||
echo "Would post comment: ${comment}" | ||
# otherwise post the comment | ||
else | ||
gh issue comment "${issue_url}" \ | ||
--body "${comment}" | ||
fi |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
name: Lint - Set points and sprint on close | ||
|
||
on: | ||
# trigger on PRs that affect this file or a file used to run the linter | ||
issues: | ||
types: [closed] | ||
|
||
defaults: | ||
run: | ||
working-directory: ./.github/linters # ensures that this job runs from the ./linters sub-directory | ||
|
||
jobs: | ||
run-project-linters: | ||
name: Run set points and sprint values on close | ||
runs-on: ubuntu-latest | ||
env: | ||
GH_TOKEN: ${{ secrets.GH_TOKEN_PROJECT_ACCESS }} | ||
ISSUE_URL: ${{ github.event.issue.html_url }} | ||
steps: | ||
- uses: actions/checkout@v4 | ||
|
||
- name: Set default values for sprint and points if unset | ||
run: | | ||
./set-points-and-sprint.sh \ | ||
--url "$ISSUE_URL" \ | ||
--org HHS \ | ||
--project 13 \ | ||
--sprint-field "Sprint" \ | ||
--points-field "Story Points" |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you expand on what
UNSET_ISSUE
means here?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great callout! Just added some comments in this commit: docs: Explains what UNSET_ISSUE is in ci-project-linters.yml