Skip to content

Instantly share code, notes, and snippets.

@indigoviolet
Last active June 6, 2025 07:29
Show Gist options
  • Save indigoviolet/deed12de2e90fea05ecc39252ab9bb9e to your computer and use it in GitHub Desktop.
Save indigoviolet/deed12de2e90fea05ecc39252ab9bb9e to your computer and use it in GitHub Desktop.
Label all issues in a repo that I'm subscribed to
#!/bin/bash
# Script to find all issues in a repository that a user is subscribed to and add a label
# Check if gum is installed
if ! command -v gum &> /dev/null; then
echo "Error: gum is not installed. Install it with:"
echo " brew install gum"
echo " or visit: https://github.com/charmbracelet/gum"
exit 1
fi
# Parse command line arguments
DRY_RUN=false
if [[ "$1" == "--dry-run" ]]; then
DRY_RUN=true
shift
fi
# Check if required arguments are provided
if [ $# -lt 3 ]; then
echo "Usage: $0 [--dry-run] <owner/repo> <username> <label>"
echo "Example: $0 octocat/hello-world octocat subscribed"
echo " $0 --dry-run octocat/hello-world octocat subscribed"
exit 1
fi
REPO=$1
USERNAME=$2
LABEL=$3
OWNER=$(echo $REPO | cut -d'/' -f1)
REPO_NAME=$(echo $REPO | cut -d'/' -f2)
# Display configuration
echo
gum style --foreground 212 --bold "GitHub Issue Tagger"
echo
gum style --foreground 240 "Repository: $REPO"
gum style --foreground 240 "Username: $USERNAME"
gum style --foreground 240 "Label: $LABEL"
if [ "$DRY_RUN" = "true" ]; then
gum style --foreground 220 "Mode: DRY RUN"
else
gum style --foreground 120 "Mode: LIVE"
fi
echo
# Check if the label exists (skip in dry-run mode)
if [ "$DRY_RUN" = "false" ]; then
echo "Checking if label exists..."
LABEL_CHECK=$(gh label list -R "$REPO" --limit 1000 --json name -q ".[].name" | grep -x "$LABEL" || echo "")
if [ -z "$LABEL_CHECK" ]; then
gum style --foreground 220 "Label '$LABEL' not found."
if gum confirm "Create it?"; then
echo "Creating label..."
gh label create "$LABEL" -R "$REPO" --color "0366d6" --description "User is subscribed to this issue"
gum style --foreground 120 "✓ Label created"
else
exit 1
fi
else
gum style --foreground 120 "✓ Label exists"
fi
fi
# Get label ID
if [ "$DRY_RUN" = "false" ]; then
echo "Getting label ID..."
LABEL_ID=$(gh api "/repos/$REPO/labels/$LABEL" --jq '.node_id' 2>/dev/null)
if [ -z "$LABEL_ID" ]; then
# Try URL-encoding the label name
ENCODED_LABEL=$(echo -n "$LABEL" | jq -sRr @uri)
LABEL_ID=$(gh api "/repos/$REPO/labels/$ENCODED_LABEL" --jq '.node_id' 2>/dev/null)
fi
if [ -z "$LABEL_ID" ]; then
gum style --foreground 196 "Failed to get label ID"
gum style --foreground 240 "Trying alternate method..."
# Try using GraphQL as fallback
LABEL_ID=$(gh api graphql -f query='query($owner: String!, $repo: String!, $label: String!) {
repository(owner: $owner, name: $repo) {
label(name: $label) {
id
}
}
}' -f owner="$OWNER" -f repo="$REPO_NAME" -f label="$LABEL" --jq '.data.repository.label.id' 2>/dev/null)
if [ -z "$LABEL_ID" ]; then
gum style --foreground 196 "Still failed to get label ID"
exit 1
fi
fi
gum style --foreground 120 "✓ Got label ID: ${LABEL_ID:0:20}..."
fi
# GraphQL query
QUERY='query($owner: String!, $repo: String!, $cursor: String) {
repository(owner: $owner, name: $repo) {
issues(first: 100, states: OPEN, after: $cursor) {
pageInfo {
hasNextPage
endCursor
}
nodes {
number
title
id
viewerSubscription
author { login }
assignees(first: 10) { nodes { login } }
participants(first: 50) { nodes { login } }
}
}
}
}'
# Arrays to store results
declare -a SUBSCRIBED_IDS
declare -a SUBSCRIBED_INFO
TOTAL=0
CURSOR=""
HAS_MORE=true
# Fetch issues
echo "Scanning issues..."
while [ "$HAS_MORE" = "true" ]; do
echo -ne "\rProcessing issues... Total: $TOTAL"
if [ -z "$CURSOR" ]; then
RESPONSE=$(gh api graphql -f query="$QUERY" -f owner="$OWNER" -f repo="$REPO_NAME")
else
RESPONSE=$(gh api graphql -f query="$QUERY" -f owner="$OWNER" -f repo="$REPO_NAME" -f cursor="$CURSOR")
fi
# Parse response
HAS_MORE=$(echo "$RESPONSE" | jq -r '.data.repository.issues.pageInfo.hasNextPage')
CURSOR=$(echo "$RESPONSE" | jq -r '.data.repository.issues.pageInfo.endCursor')
# Process each issue
while IFS= read -r issue; do
NUMBER=$(echo "$issue" | jq -r '.number')
TITLE=$(echo "$issue" | jq -r '.title')
ID=$(echo "$issue" | jq -r '.id')
VIEWER_SUB=$(echo "$issue" | jq -r '.viewerSubscription')
AUTHOR=$(echo "$issue" | jq -r '.author.login // ""')
((TOTAL++))
# Check if subscribed
IS_SUBSCRIBED=false
if [ "$VIEWER_SUB" = "SUBSCRIBED" ]; then
IS_SUBSCRIBED=true
elif [ "$AUTHOR" = "$USERNAME" ]; then
IS_SUBSCRIBED=true
else
# Check assignees
if echo "$issue" | jq -e ".assignees.nodes[] | select(.login == \"$USERNAME\")" >/dev/null 2>&1; then
IS_SUBSCRIBED=true
fi
# Check participants
if [ "$IS_SUBSCRIBED" = "false" ]; then
if echo "$issue" | jq -e ".participants.nodes[] | select(.login == \"$USERNAME\")" >/dev/null 2>&1; then
IS_SUBSCRIBED=true
fi
fi
fi
if [ "$IS_SUBSCRIBED" = "true" ]; then
SUBSCRIBED_IDS+=("$ID")
SUBSCRIBED_INFO+=("#$NUMBER: $TITLE")
fi
done < <(echo "$RESPONSE" | jq -c '.data.repository.issues.nodes[]')
done
echo # New line after progress
# Display results
echo
gum style --foreground 120 "Found ${#SUBSCRIBED_IDS[@]} subscribed issues out of $TOTAL total"
if [ ${#SUBSCRIBED_INFO[@]} -gt 0 ]; then
echo
for info in "${SUBSCRIBED_INFO[@]}"; do
gum style --foreground 240 " • $info"
done
fi
# Add labels
if [ ${#SUBSCRIBED_IDS[@]} -gt 0 ] && [ "$DRY_RUN" = "false" ]; then
echo
if gum confirm "Add label to ${#SUBSCRIBED_IDS[@]} issues?"; then
echo
SUCCESS=0
for i in "${!SUBSCRIBED_IDS[@]}"; do
ID="${SUBSCRIBED_IDS[$i]}"
INFO="${SUBSCRIBED_INFO[$i]}"
MUTATION='mutation($id: ID!, $labelIds: [ID!]!) {
addLabelsToLabelable(input: {labelableId: $id, labelIds: $labelIds}) {
clientMutationId
}
}'
# Create the labelIds JSON array properly
if gh api graphql -f query="$MUTATION" -f id="$ID" -f "labelIds[]=$LABEL_ID" >/dev/null 2>&1; then
gum style --foreground 120 " ✓ Tagged: $INFO"
((SUCCESS++))
else
# Try to debug the error
ERROR=$(gh api graphql -f query="$MUTATION" -f id="$ID" -f "labelIds[]=$LABEL_ID" 2>&1)
gum style --foreground 196 " ✗ Failed: $INFO"
if [[ "$ERROR" == *"was already present"* ]]; then
gum style --foreground 240 " (Label already exists on issue)"
fi
fi
done
echo
gum style --foreground 120 "Successfully tagged $SUCCESS issues"
fi
elif [ "$DRY_RUN" = "true" ] && [ ${#SUBSCRIBED_IDS[@]} -gt 0 ]; then
echo
gum style --foreground 220 "DRY RUN: Would tag ${#SUBSCRIBED_IDS[@]} issues"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment