Set default fields on new project items #473
This file contains hidden or 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
| name: Set default fields on new project items | |
| on: | |
| schedule: | |
| - cron: '0 */2 * * *' | |
| workflow_dispatch: | |
| inputs: | |
| lookback_hours: | |
| description: 'Hours to look back (default: 3)' | |
| required: false | |
| default: '3' | |
| jobs: | |
| set-defaults: | |
| runs-on: ubuntu-latest | |
| env: | |
| GH_TOKEN: ${{ secrets.PAT }} | |
| PROJECT_ID: "PVT_kwDODLylHc4A8S01" | |
| # Field IDs | |
| CONTAINER_VERSION_FIELD_ID: "PVTSSF_lADODLylHc4A8S01zgwVmS4" | |
| SWQA_FIELD_ID: "PVTSSF_lADODLylHc4A8S01zgxJjL4" | |
| DOCUMENTATION_STAGE_FIELD_ID: "PVTSSF_lADODLylHc4A8S01zgxJjRA" | |
| VDR_FIELD_ID: "PVTSSF_lADODLylHc4A8S01zhAFQUA" | |
| # Default option IDs | |
| CONTAINER_BACKLOG_OPTION_ID: "9cc7169c" | |
| SWQA_DEFAULT_OPTION_ID: "ec62cb4d" | |
| DOCS_NOT_STARTED_OPTION_ID: "36e5dca1" | |
| VDR_DEFAULT_OPTION_ID: "894a1a7c" | |
| steps: | |
| - name: Find recently added items with unset fields and set defaults | |
| env: | |
| LOOKBACK_HOURS: ${{ github.event.inputs.lookback_hours || '3' }} | |
| run: | | |
| set_field() { | |
| local item_id="$1" field_id="$2" option_id="$3" | |
| gh api graphql -f query=" | |
| mutation { | |
| updateProjectV2ItemFieldValue(input: { | |
| projectId: \"$PROJECT_ID\", itemId: \"$item_id\", | |
| fieldId: \"$field_id\", | |
| value: { singleSelectOptionId: \"$option_id\" } | |
| }) { projectV2Item { id } } | |
| } | |
| " | |
| } | |
| # Only process items added in the lookback window (default 3 hours covers 2-hour cron with buffer) | |
| HOURS="${LOOKBACK_HOURS:-3}" | |
| CUTOFF=$(date -u -d "$HOURS hours ago" '+%Y-%m-%dT%H:%M:%SZ') | |
| echo "Processing items created after $CUTOFF" | |
| # Fetch all project items with their field values, paginating through results | |
| CURSOR="" | |
| ALL_ITEMS="[]" | |
| while true; do | |
| if [ -z "$CURSOR" ]; then | |
| AFTER_ARG="" | |
| else | |
| AFTER_ARG=", after: \"$CURSOR\"" | |
| fi | |
| RESULT=$(gh api graphql -f query=" | |
| query { | |
| node(id: \"$PROJECT_ID\") { | |
| ... on ProjectV2 { | |
| items(first: 100${AFTER_ARG}) { | |
| pageInfo { hasNextPage endCursor } | |
| nodes { | |
| id | |
| createdAt | |
| containerVersion: fieldValueByName(name: \"Container Version\") { | |
| ... on ProjectV2ItemFieldSingleSelectValue { optionId } | |
| } | |
| swqa: fieldValueByName(name: \"SWQA\") { | |
| ... on ProjectV2ItemFieldSingleSelectValue { optionId } | |
| } | |
| docs: fieldValueByName(name: \"Documentation Stage\") { | |
| ... on ProjectV2ItemFieldSingleSelectValue { optionId } | |
| } | |
| vdr: fieldValueByName(name: \"VDR\") { | |
| ... on ProjectV2ItemFieldSingleSelectValue { optionId } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| ") | |
| NODES=$(echo "$RESULT" | jq '.data.node.items.nodes') | |
| ALL_ITEMS=$(echo "$ALL_ITEMS $NODES" | jq -s '.[0] + .[1]') | |
| HAS_NEXT=$(echo "$RESULT" | jq -r '.data.node.items.pageInfo.hasNextPage') | |
| if [ "$HAS_NEXT" != "true" ]; then | |
| break | |
| fi | |
| CURSOR=$(echo "$RESULT" | jq -r '.data.node.items.pageInfo.endCursor') | |
| done | |
| TOTAL=$(echo "$ALL_ITEMS" | jq 'length') | |
| echo "Fetched $TOTAL total project items" | |
| # Filter to only recently created items | |
| RECENT_ITEMS=$(echo "$ALL_ITEMS" | jq -c --arg cutoff "$CUTOFF" '[.[] | select(.createdAt > $cutoff)]') | |
| RECENT_COUNT=$(echo "$RECENT_ITEMS" | jq 'length') | |
| echo "Found $RECENT_COUNT items created after $CUTOFF" | |
| if [ "$RECENT_COUNT" -eq 0 ]; then | |
| echo "Nothing to update" | |
| exit 0 | |
| fi | |
| # Process each recent item — only set fields that are currently null | |
| UPDATED=0 | |
| while read -r ITEM; do | |
| ITEM_ID=$(echo "$ITEM" | jq -r '.id') | |
| CV=$(echo "$ITEM" | jq '.containerVersion') | |
| SWQA_VAL=$(echo "$ITEM" | jq '.swqa') | |
| DOCS=$(echo "$ITEM" | jq '.docs') | |
| VDR_VAL=$(echo "$ITEM" | jq '.vdr') | |
| # Skip items where all fields are already set | |
| if [ "$CV" != "null" ] && [ "$SWQA_VAL" != "null" ] && [ "$DOCS" != "null" ] && [ "$VDR_VAL" != "null" ]; then | |
| continue | |
| fi | |
| echo "Item $ITEM_ID — cv=$CV swqa=$SWQA_VAL docs=$DOCS vdr=$VDR_VAL" | |
| # Set each null field individually to avoid overwriting existing values | |
| if [ "$CV" = "null" ]; then | |
| echo " Setting Container Version to Backlog" | |
| set_field "$ITEM_ID" "$CONTAINER_VERSION_FIELD_ID" "$CONTAINER_BACKLOG_OPTION_ID" | |
| fi | |
| if [ "$SWQA_VAL" = "null" ]; then | |
| echo " Setting SWQA default" | |
| set_field "$ITEM_ID" "$SWQA_FIELD_ID" "$SWQA_DEFAULT_OPTION_ID" | |
| fi | |
| if [ "$DOCS" = "null" ]; then | |
| echo " Setting Documentation Stage to Not Started" | |
| set_field "$ITEM_ID" "$DOCUMENTATION_STAGE_FIELD_ID" "$DOCS_NOT_STARTED_OPTION_ID" | |
| fi | |
| if [ "$VDR_VAL" = "null" ]; then | |
| echo " Setting VDR default" | |
| set_field "$ITEM_ID" "$VDR_FIELD_ID" "$VDR_DEFAULT_OPTION_ID" | |
| fi | |
| UPDATED=$((UPDATED + 1)) | |
| done < <(echo "$RECENT_ITEMS" | jq -c '.[]') | |
| echo "Done. Updated $UPDATED items with missing defaults." |