-
Notifications
You must be signed in to change notification settings - Fork 10.8k
Script to cascade the Area field from an issue to subissues to help build projects #17252
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
Conversation
Summary of ChangesHello @jacob314, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces a new shell script designed to streamline the management of GitHub ProjectV2 boards. The script automates the process of inheriting specific field values, such as 'Area,' from a parent issue to all its associated sub-issues and tracked issues. Additionally, it ensures that these descendant issues are consistently marked with a 'Ready' status and 'Task' type, enhancing project consistency and reducing manual effort in project curation. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
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.
Code Review
This pull request introduces a new bash script, scripts/cascade_area.sh, to automate the process of cascading a project field value from a parent issue to its descendants. The script is well-structured and handles argument parsing and error conditions appropriately.
However, I've identified two major performance issues that should be addressed:
- Critical Performance Bottleneck: The script makes a large number of sequential API calls within a loop to update each descendant issue. This will be very slow and may hit API rate limits for issue trees of even moderate size. I've recommended refactoring this to use GraphQL query aliasing to batch-read data for all descendants in a single API call.
- Inefficient Search Algorithm: The Breadth-First Search (BFS) used to find descendants uses an O(N) array search to check for already visited issues, leading to an overall O(N^2) complexity. I've suggested using a bash associative array for an O(1) lookup, which will significantly speed up this part of the script.
Addressing these points will make the script much more scalable and robust.
| for ISSUE_ID in "${DESCENDANTS[@]}"; do | ||
| # We need the Project Item ID for this specific issue, and check its Type | ||
| ITEM_DATA=$(gh api graphql -f issueId="$ISSUE_ID" -f query=' | ||
| query($issueId: ID!) { | ||
| node(id: $issueId) { | ||
| ... on Issue { | ||
| number | ||
| projectItems(first: 10) { | ||
| nodes { | ||
| id | ||
| project { id } | ||
| fieldValueByName(name: "Type") { | ||
| ... on ProjectV2ItemFieldSingleSelectValue { | ||
| name | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| ') | ||
|
|
||
| ITEM_ID=$(echo "$ITEM_DATA" | jq -r --arg pid "$PROJECT_ID" '.data.node.projectItems.nodes[]? | select(.project.id == $pid) | .id // empty') | ||
| ISSUE_NUM=$(echo "$ITEM_DATA" | jq -r '.data.node.number') | ||
|
|
||
| # Check if Type (Issue Type) is already set | ||
| # We check the issueType field on the issue | ||
| CURRENT_ISSUE_TYPE=$(gh api graphql -f issueId="$ISSUE_ID" -f query=' | ||
| query($issueId: ID!) { | ||
| node(id: $issueId) { | ||
| ... on Issue { | ||
| issueType { name } | ||
| } | ||
| } | ||
| } | ||
| ') | ||
| HAS_TASK_TYPE=$(echo "$CURRENT_ISSUE_TYPE" | jq -r '.data.node.issueType.name // empty') | ||
|
|
||
| if [ -z "$ITEM_ID" ]; then | ||
| echo "➕ Adding Issue #$ISSUE_NUM to project board..." | ||
| ADD_ITEM_DATA=$(gh api graphql -f projectId="$PROJECT_ID" -f contentId="$ISSUE_ID" -f query=' | ||
| mutation($projectId: ID!, $contentId: ID!) { | ||
| addProjectV2ItemById(input: {projectId: $projectId, contentId: $contentId}) { | ||
| item { id } | ||
| } | ||
| } | ||
| ') | ||
| ITEM_ID=$(echo "$ADD_ITEM_DATA" | jq -r '.data.addProjectV2ItemById.item.id // empty') | ||
| fi | ||
|
|
||
| if [ -n "$ITEM_ID" ]; then | ||
| echo "✏️ Updating Issue #$ISSUE_NUM (Item: $ITEM_ID)..." | ||
|
|
||
| # Update Area | ||
| gh api graphql -f projectId="$PROJECT_ID" -f itemId="$ITEM_ID" -f fieldId="$FIELD_ID" -f optionId="$PARENT_OPTION_ID" -f query=' | ||
| mutation($projectId: ID!, $itemId: ID!, $fieldId: ID!, $optionId: String!) { | ||
| updateProjectV2ItemFieldValue(input: { | ||
| projectId: $projectId | ||
| itemId: $itemId | ||
| fieldId: $fieldId | ||
| value: { singleSelectOptionId: $optionId } | ||
| }) { clientMutationId } | ||
| } | ||
| ' > /dev/null | ||
|
|
||
| # Update Status to Ready if we found the IDs | ||
| if [ -n "$STATUS_FIELD_ID" ] && [ -n "$READY_OPTION_ID" ]; then | ||
| echo " └─ Setting Status to Ready..." | ||
| gh api graphql -f projectId="$PROJECT_ID" -f itemId="$ITEM_ID" -f fieldId="$STATUS_FIELD_ID" -f optionId="$READY_OPTION_ID" -f query=' | ||
| mutation($projectId: ID!, $itemId: ID!, $fieldId: ID!, $optionId: String!) { | ||
| updateProjectV2ItemFieldValue(input: { | ||
| projectId: $projectId | ||
| itemId: $itemId | ||
| fieldId: $fieldId | ||
| value: { singleSelectOptionId: $optionId } | ||
| }) { clientMutationId } | ||
| } | ||
| ' > /dev/null | ||
| fi | ||
|
|
||
| # Update Issue Type to Task if empty | ||
| if [ -n "$TASK_TYPE_ID" ] && [ -z "$HAS_TASK_TYPE" ]; then | ||
| echo " └─ Setting Issue Type to 'Task'..." | ||
| gh api graphql -f issueId="$ISSUE_ID" -f issueTypeId="$TASK_TYPE_ID" -f query=' | ||
| mutation($issueId: ID!, $issueTypeId: ID!) { | ||
| updateIssue(input: {id: $issueId, issueTypeId: $issueTypeId}) { | ||
| issue { issueType { name } } | ||
| } | ||
| } | ||
| ' > /dev/null | ||
| fi | ||
| else | ||
| echo "❌ Failed to add/find Issue #$ISSUE_NUM on the project board." | ||
| fi | ||
| done |
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.
This loop iterates through all descendant issues and makes multiple sequential API calls for each one. For an issue tree with N descendants, this can result in up to 6*N API calls (getItem, getType, addItem, updateArea, updateStatus, updateType). This is highly inefficient, will be very slow for a large number of issues, and increases the risk of hitting GitHub API rate limits.
To make this more efficient, you should batch these operations. The GitHub GraphQL API is well-suited for this:
-
Batch Read Operations: After collecting all
DESCENDANTSIDs, use a single GraphQL query with aliases to fetch all the necessary information (projectItems,issueType, etc.) for all descendants at once. This reduces N or 2N read calls to a single call.Example of a batched query:
query getIssueData($id1: ID!, $id2: ID!) { issue1: node(id: $id1) { ... on Issue { number projectItems(first:10){...} } } issue2: node(id: $id2) { ... on Issue { number projectItems(first:10){...} } } }
-
Process Data: Process the single large JSON response in your script to determine which issues need to be added to the project and which fields need updating.
-
Execute Mutations: While mutations are harder to batch, you will still have significantly reduced the total number of API calls by batching the reads. You can then loop through and execute the necessary mutations.
This architectural change is critical for the script to be scalable and reliable.
| for child in $CHILD_IDS; do | ||
| if [ -z "$child" ] || [ "$child" == "null" ]; then continue; fi | ||
| # Check if seen (simple duplicate check) | ||
| if [[ ! " ${SEEN[@]} " =~ " ${child} " ]]; then |
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.
The current method to check if a child issue has been seen ([[ ! " ${SEEN[@]} " =~ " ${child} " ]]) has a time complexity of O(N), where N is the number of items in the SEEN array. Since this check is inside a loop, the overall complexity for finding unique descendants becomes O(M^2) where M is the total number of descendants. This can become very slow if the issue tree is large.
For better performance, consider using a bash associative array (available in bash 4.0+) for the SEEN set. This provides an average time complexity of O(1) for lookups.
Here's how you could refactor it:
- Initialize
SEENas an associative array (this would modify line 185):
declare -A SEEN
SEEN["$ROOT_NODE_ID"]=1- Update the check and addition within the loop (this would replace lines 219-226):
if [[ -z "${SEEN[$child]}" ]]; then
SEEN["$child"]=1
QUEUE+=("$child")
DESCENDANTS+=("$child")
# Extract number just for logging
CHILD_NUM=$(echo "$CHILDREN_JSON" | jq -r --arg cid "$child" '((.data.node.trackedIssues.nodes[]? | select(.id == $cid) | .number) // (.data.node.subIssues.nodes[]? | select(.id == $cid) | .number))')
echo " Found descendant: #$CHILD_NUM"
fiThis change will significantly improve the script's performance for projects with many interconnected issues. Note that this requires bash version 4.0 or newer.
|
Size Change: -2 B (0%) Total Size: 23.2 MB ℹ️ View Unchanged
|
|
Hi there! Thank you for your contribution to Gemini CLI. To improve our contribution process and better track changes, we now require all pull requests to be associated with an existing issue, as announced in our recent discussion and as detailed in our CONTRIBUTING.md. This pull request is being closed because it is not currently linked to an issue. Once you have updated the description of this PR to link an issue (e.g., by adding How to link an issue: Thank you for your understanding and for being a part of our community! |
Summary
Script to add the correct area label and type to all sub-issues of an issue.
This recursively
Details
Example usage:
scripts/cascade_area.sh google-gemini 33 14411Also fills out the type field if it is missing defaulting to Task as that is a reasonable default for sub-issues that are missing a type.