GraphQL to create / edit an iteration in Projects V2 #157957
Replies: 3 comments 2 replies
-
|
💬 Your Product Feedback Has Been Submitted 🎉 Thank you for taking the time to share your insights with us! Your feedback is invaluable as we build a better GitHub experience for all our users. Here's what you can expect moving forward ⏩
Where to look to see what's shipping 👀
What you can do in the meantime 💻
As a member of the GitHub community, your participation is essential. While we can't promise that every suggestion will be implemented, we want to emphasize that your feedback is instrumental in guiding our decisions and priorities. Thank you once again for your contribution to making GitHub even better! We're grateful for your ongoing support and collaboration in shaping the future of our platform. ⭐ |
Beta Was this translation helpful? Give feedback.
-
|
Any update on this? This is blocking us. Now we have to use a puppeteer to do this, so there must be a private API that allows a |
Beta Was this translation helpful? Give feedback.
-
|
This is pretty needed, we're able to automate moving issues forward in iterations and other things to take the load off managing the project, but having to manually go into the settings of the project every once in a a while to add a bunch of iterations is pretty dumb. My original expectation was that unless you'd set breaks, the next iteration would automatically get created based on your Even if you get the current iterations and then combine them with your new ones for an update, it blows up all the current iteration assignments because the new one with the same date has a different ID. Example Script#!/usr/bin/env bash
set -e
OWNER=""
PROJECT_NUMBER=
ITERATION_DURATION=14 # days
# get project ID and iterations
# shellcheck disable=SC2016 # no expansion intended, graphql parameters
PROJECT_DATA=$(gh api graphql -f query='
query($owner: String!, $number: Int!) {
organization(login: $owner) {
projectV2(number: $number) {
id
field(name: "Iteration") {
... on ProjectV2IterationField {
id
configuration {
iterations {
id
title
startDate
duration
}
}
}
}
}
}
}
' -f owner="$OWNER" -F number="$PROJECT_NUMBER")
PROJECT_ID=$(echo "$PROJECT_DATA" | jq -r '.data.organization.projectV2.id')
FIELD_ID=$(echo "$PROJECT_DATA" | jq -r '.data.organization.projectV2.field.id')
ITERATIONS=$(echo "$PROJECT_DATA" | jq -r '.data.organization.projectV2.field.configuration.iterations')
echo "Project ID: $PROJECT_ID"
echo "Iteration Field ID: $FIELD_ID"
echo "Current iterations:"
echo "$ITERATIONS" | jq -r '.[] | " \(.title): \(.startDate) (\(.duration) days)"'
TODAY=$(date +%Y-%m-%d)
echo "Today: $TODAY"
# find current iteration (fixed jq: need parens around mktime before adding duration)
CURRENT_ITERATION=$(echo "$ITERATIONS" | jq -r --arg today "$TODAY" '
.[] | select(
.startDate <= $today and
(((.startDate | strptime("%Y-%m-%d") | mktime) + (.duration * 86400)) | strftime("%Y-%m-%d")) > $today
) | .startDate
' | head -1)
# find the latest iteration end date
LATEST_END=$(echo "$ITERATIONS" | jq -r '
[.[] | {
end: (((.startDate | strptime("%Y-%m-%d") | mktime) + (.duration * 86400)) | strftime("%Y-%m-%d")),
start: .startDate
}] | sort_by(.end) | last | .end
')
echo "Current iteration starts: $CURRENT_ITERATION"
echo "Latest iteration ends: $LATEST_END"
# calculate how many iterations we need to ensure current + next exist
ITERATIONS_NEEDED=0
if [ -z "$CURRENT_ITERATION" ] || [ "$CURRENT_ITERATION" = "null" ]; then
echo "No current iteration found - need to create one"
ITERATIONS_NEEDED=2 # current + next
# find the Monday on or before today for the start date
DAY_OF_WEEK=$(date +%u) # 1=Monday, 7=Sunday
DAYS_SINCE_MONDAY=$((DAY_OF_WEEK - 1))
NEXT_START=$(date -d "$TODAY - $DAYS_SINCE_MONDAY days" +%Y-%m-%d 2>/dev/null || date -v-${DAYS_SINCE_MONDAY}d +%Y-%m-%d)
else
# check if next iteration exists
CURRENT_END=$(echo "$ITERATIONS" | jq -r --arg start "$CURRENT_ITERATION" '
.[] | select(.startDate == $start) |
(((.startDate | strptime("%Y-%m-%d") | mktime) + (.duration * 86400)) | strftime("%Y-%m-%d"))
')
NEXT_ITERATION=$(echo "$ITERATIONS" | jq -r --arg current_end "$CURRENT_END" '
.[] | select(.startDate == $current_end) | .startDate
' | head -1)
echo "Current iteration ends: $CURRENT_END"
echo "Next iteration starts: $NEXT_ITERATION"
if [ -z "$NEXT_ITERATION" ] || [ "$NEXT_ITERATION" = "null" ]; then
echo "No next iteration found - need to create one"
ITERATIONS_NEEDED=1
NEXT_START="$CURRENT_END"
else
echo "Both current and next iterations exist"
fi
fi
# create missing iterations
if [ "$ITERATIONS_NEEDED" -gt 0 ]; then
echo "Creating $ITERATIONS_NEEDED new iteration(s)..."
# NEXT_START is set above:
# - Monday of current week (if no current iteration exists)
# - CURRENT_END (if current exists but next doesn't)
# - fallback to LATEST_END (shouldn't happen, but just in case)
if [ -z "$NEXT_START" ]; then
NEXT_START="$LATEST_END"
fi
# build list of new iterations to add
NEW_ITERATIONS="[]"
for _ in $(seq 1 $ITERATIONS_NEEDED); do
# format title based on start date (e.g., "Jan 19 - Feb 1")
START_FORMATTED=$(date -d "$NEXT_START" +"%b %-d" 2>/dev/null || date -j -f "%Y-%m-%d" "$NEXT_START" +"%b %-d")
END_DATE=$(date -d "$NEXT_START + $((ITERATION_DURATION - 1)) days" +%Y-%m-%d 2>/dev/null || date -j -v+$((ITERATION_DURATION - 1))d -f "%Y-%m-%d" "$NEXT_START" +%Y-%m-%d)
END_FORMATTED=$(date -d "$END_DATE" +"%b %-d" 2>/dev/null || date -j -f "%Y-%m-%d" "$END_DATE" +"%b %-d")
TITLE="$START_FORMATTED - $END_FORMATTED"
echo " Adding iteration: $TITLE (starts $NEXT_START, $ITERATION_DURATION days)"
NEW_ITERATIONS=$(echo "$NEW_ITERATIONS" | jq \
--arg start "$NEXT_START" \
--arg title "$TITLE" \
--argjson duration "$ITERATION_DURATION" \
'. + [{startDate: $start, title: $title, duration: $duration}]')
# move to next iteration start
NEXT_START=$(date -d "$NEXT_START + $ITERATION_DURATION days" +%Y-%m-%d 2>/dev/null || date -j -v+${ITERATION_DURATION}d -f "%Y-%m-%d" "$NEXT_START" +%Y-%m-%d)
done
# combine existing iterations with new ones
# input type only accepts startDate, duration, title (no id field)
EXISTING_ITERATIONS=$(echo "$ITERATIONS" | jq '[.[] | {startDate, duration, title}]')
ALL_ITERATIONS=$(echo "$EXISTING_ITERATIONS" "$NEW_ITERATIONS" | jq -sc 'add')
echo "Updating iteration field with all iterations..."
# get the earliest start date from all iterations, the mutation requires that it's passed in iteration configuration
FIRST_START=$(echo "$ALL_ITERATIONS" | jq -r '[.[].startDate] | sort | first')
# construct full GraphQL payload as JSON and send via --input
# this avoids gh's argument parsing issues with JSON arrays
PAYLOAD=$(jq -n \
--arg fieldId "$FIELD_ID" \
--argjson iterations "$ALL_ITERATIONS" \
--argjson duration "$ITERATION_DURATION" \
--arg startDate "$FIRST_START" \
'{
query: "mutation($fieldId: ID!, $iterations: [ProjectV2Iteration!]!, $duration: Int!, $startDate: Date!) { updateProjectV2Field(input: { fieldId: $fieldId, iterationConfiguration: { startDate: $startDate, duration: $duration, iterations: $iterations } }) { projectV2Field { ... on ProjectV2IterationField { id } } } }",
variables: {
fieldId: $fieldId,
iterations: $iterations,
duration: $duration,
startDate: $startDate
}
}')
# use updateProjectV2Field which replaces all iterations
# by including existing iterations with their IDs, we preserve issue assignments
echo "$PAYLOAD" | gh api graphql --input -
echo "Iterations created successfully"
fiI can try getting all the issues assigned to any of the existing iterations, and updating all of them to the newly created IDs, but even if that works, this is a wild amount of work and potential brittleness to accomplish a very basic project management function. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Select Topic Area
Product Feedback
Feature Area
Projects
Body
The following GraphQL can replace all iterations:
Since its a replace, all existing issues in that iteration will loose their iteration. We need to be able to add or edit an existing iteration. Since
startDateis a unique key this should do a find and edit maintaining the same id, and thus same associations to issues. If thestartDateis not an appropriate key then allow ProjectV2Iteration to pass in anid.We just migrated from Pivotal Tracker, which uses XP style iterations. Since Github does not provided this I have written Github Action that runs nightly to do this. BUT without the ability to edit iterations the entire thing becomes unfeasible as you would have to download the entire dataset, store the old iterations associations, create one new iteration, and then update all the issues to point to the new iterations. This is critical enough that may have to use a different provider entirely.
Guidelines
Beta Was this translation helpful? Give feedback.
All reactions