Archive old Done items from Org Kanban #7
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: Archive old Done items from Org Kanban | |
| on: | |
| schedule: | |
| # Every Monday at 9am UTC | |
| - cron: '0 9 * * 1' | |
| workflow_dispatch: | |
| inputs: | |
| dry_run: | |
| description: 'Dry run (log without archiving)' | |
| required: false | |
| default: 'true' | |
| type: boolean | |
| env: | |
| GH_TOKEN: ${{ secrets.ACTIONS_TOKEN }} | |
| ORG_KANBAN_PROJECT_ID: PVT_kwDOAA7NZM4AqQF8 | |
| DONE_OPTION_ID: "98236657" | |
| # Items in Done older than this many days get archived | |
| STALE_DAYS: "180" | |
| jobs: | |
| archive-old-done: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Archive Done items older than 6 months | |
| run: | | |
| DRY_RUN="${{ github.event.inputs.dry_run || 'false' }}" | |
| CUTOFF=$(date -d "-${STALE_DAYS} days" +%s 2>/dev/null || date -v "-${STALE_DAYS}d" +%s) | |
| echo "Fetching Done items from Org Kanban..." | |
| echo "Cutoff: items not updated since $(date -d @$CUTOFF 2>/dev/null || date -r $CUTOFF)" | |
| echo "Dry run: $DRY_RUN" | |
| echo "---" | |
| ARCHIVED=0 | |
| CURSOR="" | |
| while true; do | |
| if [ -n "$CURSOR" ]; then | |
| RESULT=$(gh api graphql -f query=' | |
| query($project: ID!, $cursor: String!) { | |
| node(id: $project) { | |
| ... on ProjectV2 { | |
| items(first: 100, after: $cursor) { | |
| nodes { | |
| id | |
| updatedAt | |
| fieldValueByName(name: "Status") { | |
| ... on ProjectV2ItemFieldSingleSelectValue { | |
| optionId | |
| } | |
| } | |
| content { | |
| ... on Issue { number title } | |
| ... on DraftIssue { title } | |
| } | |
| } | |
| pageInfo { hasNextPage endCursor } | |
| } | |
| } | |
| } | |
| }' -f project="$ORG_KANBAN_PROJECT_ID" -f cursor="$CURSOR") | |
| else | |
| RESULT=$(gh api graphql -f query=' | |
| query($project: ID!) { | |
| node(id: $project) { | |
| ... on ProjectV2 { | |
| items(first: 100) { | |
| nodes { | |
| id | |
| updatedAt | |
| fieldValueByName(name: "Status") { | |
| ... on ProjectV2ItemFieldSingleSelectValue { | |
| optionId | |
| } | |
| } | |
| content { | |
| ... on Issue { number title } | |
| ... on DraftIssue { title } | |
| } | |
| } | |
| pageInfo { hasNextPage endCursor } | |
| } | |
| } | |
| } | |
| }' -f project="$ORG_KANBAN_PROJECT_ID") | |
| fi | |
| HAS_NEXT=$(echo "$RESULT" | jq -r '.data.node.items.pageInfo.hasNextPage') | |
| CURSOR=$(echo "$RESULT" | jq -r '.data.node.items.pageInfo.endCursor') | |
| # Process each item | |
| while IFS= read -r item; do | |
| ITEM_ID=$(echo "$item" | jq -r '.id') | |
| OPTION_ID=$(echo "$item" | jq -r '.fieldValueByName.optionId // ""') | |
| UPDATED_AT=$(echo "$item" | jq -r '.updatedAt') | |
| TITLE=$(echo "$item" | jq -r '.content.title // "(no title)"') | |
| ISSUE_NUM=$(echo "$item" | jq -r '.content.number // ""') | |
| # Skip items not in Done | |
| [ "$OPTION_ID" != "$DONE_OPTION_ID" ] && continue | |
| # Check age | |
| UPDATED_TS=$(date -d "$UPDATED_AT" +%s 2>/dev/null || date -j -f "%Y-%m-%dT%H:%M:%SZ" "$UPDATED_AT" +%s) | |
| [ "$UPDATED_TS" -gt "$CUTOFF" ] && continue | |
| LABEL="" | |
| [ -n "$ISSUE_NUM" ] && LABEL="#$ISSUE_NUM: " | |
| echo "Would archive: ${LABEL}${TITLE} (last updated: $UPDATED_AT)" | |
| if [ "$DRY_RUN" != "true" ]; then | |
| gh api graphql -f query=' | |
| mutation($project: ID!, $item: ID!) { | |
| archiveProjectV2Item(input: { projectId: $project, itemId: $item }) { | |
| item { id } | |
| } | |
| }' -f project="$ORG_KANBAN_PROJECT_ID" -f item="$ITEM_ID" > /dev/null | |
| echo " → Archived." | |
| ARCHIVED=$((ARCHIVED + 1)) | |
| fi | |
| done < <(echo "$RESULT" | jq -c '.data.node.items.nodes[]') | |
| [ "$HAS_NEXT" != "true" ] && break | |
| done | |
| echo "---" | |
| if [ "$DRY_RUN" == "true" ]; then | |
| echo "Dry run complete. Run with dry_run=false to apply." | |
| else | |
| echo "Archived $ARCHIVED item(s)." | |
| fi |