|
| 1 | +#!/bin/bash |
| 2 | + |
| 3 | +# Script to add label to issues in GitHub ProjectsV2 based on Status field |
| 4 | +# Usage: ./add-label.sh "PVT_xxx,PVT_yyy" ["Need To Verify"] ["kind/need-to-verify"] |
| 5 | + |
| 6 | +set -e |
| 7 | + |
| 8 | +PROJECT_IDS="${1:-}" |
| 9 | +STATUS_VALUE="${2:-Need To Verify}" |
| 10 | +LABEL_TO_ADD="${3:-kind/need-to-verify}" |
| 11 | + |
| 12 | +if [ -z "$PROJECT_IDS" ]; then |
| 13 | + echo "Usage: $0 <project_ids> [status_value] [label_to_add]" |
| 14 | + echo " project_ids: Comma-separated list of ProjectV2 IDs (required)" |
| 15 | + echo " status_value: Status field value to match (default: 'Need To Verify')" |
| 16 | + echo " label_to_add: Label to add (default: 'kind/need-to-verify')" |
| 17 | + echo "" |
| 18 | + echo "Example: $0 'PVT_kwDOAjmOms4BMOXr,PVT_kwDOAjmOms4A4UrU'" |
| 19 | + exit 1 |
| 20 | +fi |
| 21 | + |
| 22 | +echo "Projects: $PROJECT_IDS" |
| 23 | +echo "Status: $STATUS_VALUE" |
| 24 | +echo "Label: $LABEL_TO_ADD" |
| 25 | +echo "" |
| 26 | + |
| 27 | +# Get Status field info for each project |
| 28 | +FIELD_QUERY='query($id: ID!) { |
| 29 | + node(id: $id) { |
| 30 | + ... on ProjectV2 { |
| 31 | + id |
| 32 | + title |
| 33 | + fields(first: 30) { |
| 34 | + nodes { |
| 35 | + ... on ProjectV2Field { |
| 36 | + name |
| 37 | + } |
| 38 | + ... on ProjectV2SingleSelectField { |
| 39 | + name |
| 40 | + options { name } |
| 41 | + } |
| 42 | + } |
| 43 | + } |
| 44 | + } |
| 45 | + } |
| 46 | +}' |
| 47 | + |
| 48 | +# Fetch field info for each project |
| 49 | +declare -A STATUS_FIELD_IDS |
| 50 | +declare -A PROJECT_TITLES |
| 51 | + |
| 52 | +echo "Fetching project fields..." |
| 53 | +for project_id in $(echo "$PROJECT_IDS" | tr ',' ' '); do |
| 54 | + project_data=$(gh api graphql -f query="$FIELD_QUERY" -F id="$project_id" --jq '.data.node') |
| 55 | + |
| 56 | + if [ "$project_data" = "null" ]; then |
| 57 | + echo "Warning: Could not fetch project $project_id, skipping" |
| 58 | + continue |
| 59 | + fi |
| 60 | + |
| 61 | + project_title=$(echo "$project_data" | jq -r '.title') |
| 62 | + PROJECT_TITLES[$project_id]="$project_title" |
| 63 | + |
| 64 | + status_field=$(echo "$project_data" | jq -r '.fields.nodes[] | select(.name == "Status")') |
| 65 | + |
| 66 | + if [ "$status_field" = "null" ] || [ -z "$status_field" ]; then |
| 67 | + echo "Warning: Project $project_title has no Status field, skipping" |
| 68 | + continue |
| 69 | + fi |
| 70 | + |
| 71 | + status_option=$(echo "$status_field" | jq -r ".options[] | select(.name == \"$STATUS_VALUE\")") |
| 72 | + |
| 73 | + if [ "$status_option" = "null" ] || [ -z "$status_option" ]; then |
| 74 | + echo "Warning: Project $project_title has no Status option '$STATUS_VALUE', skipping" |
| 75 | + continue |
| 76 | + fi |
| 77 | + |
| 78 | + STATUS_FIELD_IDS[$project_id]="present" |
| 79 | + |
| 80 | + echo "Project: $project_title - OK" |
| 81 | +done |
| 82 | + |
| 83 | +if [ ${#STATUS_FIELD_IDS[@]} -eq 0 ]; then |
| 84 | + echo "Error: No valid projects found" |
| 85 | + exit 1 |
| 86 | +fi |
| 87 | + |
| 88 | +echo "" |
| 89 | +echo "=== Processing issues ===" |
| 90 | + |
| 91 | +total_labeled=0 |
| 92 | + |
| 93 | +for project_id in "${!STATUS_FIELD_IDS[@]}"; do |
| 94 | + project_title="${PROJECT_TITLES[$project_id]}" |
| 95 | + |
| 96 | + echo "" |
| 97 | + echo "=== Checking project: $project_title ===" |
| 98 | + |
| 99 | + # Query items in the project with pagination |
| 100 | + ITEMS_QUERY='query($id: ID!, $cursor: String) { |
| 101 | + node(id: $id) { |
| 102 | + ... on ProjectV2 { |
| 103 | + items(first: 100, after: $cursor) { |
| 104 | + pageInfo { |
| 105 | + hasNextPage |
| 106 | + endCursor |
| 107 | + } |
| 108 | + nodes { |
| 109 | + content { |
| 110 | + ... on Issue { |
| 111 | + number |
| 112 | + title |
| 113 | + state |
| 114 | + repository { |
| 115 | + name |
| 116 | + owner { login } |
| 117 | + } |
| 118 | + labels(first: 10) { |
| 119 | + nodes { name } |
| 120 | + } |
| 121 | + } |
| 122 | + } |
| 123 | + fieldValues(first: 20) { |
| 124 | + nodes { |
| 125 | + ... on ProjectV2ItemFieldSingleSelectValue { |
| 126 | + name |
| 127 | + field { |
| 128 | + ... on ProjectV2SingleSelectField { |
| 129 | + name |
| 130 | + } |
| 131 | + } |
| 132 | + } |
| 133 | + } |
| 134 | + } |
| 135 | + } |
| 136 | + } |
| 137 | + } |
| 138 | + } |
| 139 | + }' |
| 140 | + |
| 141 | + labeled_count=0 |
| 142 | + item_count=0 |
| 143 | + cursor="null" |
| 144 | + |
| 145 | + while true; do |
| 146 | + if [ "$cursor" = "null" ] || [ -z "$cursor" ]; then |
| 147 | + items_data=$(gh api graphql -f query="$ITEMS_QUERY" -F id="$project_id" --jq '.data.node.items') |
| 148 | + else |
| 149 | + items_data=$(gh api graphql -f query="$ITEMS_QUERY" -F id="$project_id" -F cursor="$cursor" --jq '.data.node.items') |
| 150 | + fi |
| 151 | + |
| 152 | + page_info=$(echo "$items_data" | jq -r '.pageInfo') |
| 153 | + has_next=$(echo "$page_info" | jq -r '.hasNextPage') |
| 154 | + end_cursor=$(echo "$page_info" | jq -r '.endCursor') |
| 155 | + |
| 156 | + nodes=$(echo "$items_data" | jq '.nodes') |
| 157 | + current_count=$(echo "$nodes" | jq 'length') |
| 158 | + item_count=$((item_count + current_count)) |
| 159 | + |
| 160 | + echo "Found $item_count items so far..." |
| 161 | + |
| 162 | + for item_row in $(echo "$nodes" | jq -r '.[] | @base64'); do |
| 163 | + item=$(echo "$item_row" | base64 -d) |
| 164 | + |
| 165 | + content=$(echo "$item" | jq -r '.content') |
| 166 | + if [ "$content" = "null" ] || [ -z "$content" ]; then |
| 167 | + continue |
| 168 | + fi |
| 169 | + |
| 170 | + issue_state=$(echo "$content" | jq -r '.state') |
| 171 | + if [ "$issue_state" != "OPEN" ]; then |
| 172 | + continue |
| 173 | + fi |
| 174 | + |
| 175 | + # Check Status field value |
| 176 | + status_field_value=$(echo "$item" | jq -r '.fieldValues.nodes[] | select(.field.name == "Status") | .name') |
| 177 | + |
| 178 | + if [ "$status_field_value" = "$STATUS_VALUE" ]; then |
| 179 | + issue_number=$(echo "$content" | jq -r '.number') |
| 180 | + issue_title=$(echo "$content" | jq -r '.title') |
| 181 | + repo_name=$(echo "$content" | jq -r '.repository.name') |
| 182 | + repo_owner=$(echo "$content" | jq -r '.repository.owner.login') |
| 183 | + |
| 184 | + # Check if label already exists |
| 185 | + existing_labels=$(echo "$content" | jq -r '.labels.nodes[].name') |
| 186 | + |
| 187 | + if echo "$existing_labels" | grep -q "^${LABEL_TO_ADD}$"; then |
| 188 | + echo "Issue #$issue_number already has label, skip" |
| 189 | + else |
| 190 | + echo "Adding label to issue #$issue_number: $issue_title" |
| 191 | + |
| 192 | + if echo '{"labels": ["$LABEL_TO_ADD"]}' | gh api repos/"$repo_owner"/"$repo_name"/issues/"$issue_number"/labels \ |
| 193 | + -X POST \ |
| 194 | + --input - \ |
| 195 | + > /dev/null 2>&1; then |
| 196 | + labeled_count=$((labeled_count + 1)) |
| 197 | + else |
| 198 | + echo " Warning: Failed to add label to #$issue_number" |
| 199 | + fi |
| 200 | + fi |
| 201 | + fi |
| 202 | + done |
| 203 | + |
| 204 | + if [ "$has_next" = "false" ]; then |
| 205 | + break |
| 206 | + fi |
| 207 | + |
| 208 | + cursor="$end_cursor" |
| 209 | + done |
| 210 | + |
| 211 | + echo "Project $project_title: Labeled $labeled_count issues (total: $item_count)" |
| 212 | + total_labeled=$((total_labeled + labeled_count)) |
| 213 | +done |
| 214 | + |
| 215 | +echo "" |
| 216 | +echo "=== Done! Total labeled: $total_labeled ===" |
0 commit comments