Added PR/Issue management actions

This commit is contained in:
2026-02-13 15:52:40 +00:00
parent 97354a2874
commit 07502a6b26
4 changed files with 182 additions and 12 deletions

View File

@@ -0,0 +1,73 @@
name: Issue Management Bot
on:
issues:
types: [opened, edited, labeled, unlabeled]
jobs:
issue-triage:
runs-on: ubuntu-latest
env:
API_TOKEN: ${{ secrets.BOT_ACCESS_TOKEN }}
API_BASE_URL: https://git.psmattas.com/api/v1
REPO: ${{ gitea.repository }}
ISSUE_NUMBER: ${{ gitea.event.issue.number }}
ISSUE_AUTHOR: ${{ gitea.event.issue.user.login }}
ISSUE_TITLE: ${{ gitea.event.issue.title }}
steps:
- name: Welcome new contributors
if: gitea.event.action == 'opened'
run: |
# Check if this is the user's first issue
AUTHOR_ISSUES=$(curl -s -H "Authorization: token $API_TOKEN" "$API_BASE_URL/repos/$REPO/issues?state=all&created_by=$ISSUE_AUTHOR" | jq '. | length')
if [ "$AUTHOR_ISSUES" -eq 1 ]; then
WELCOME_MSG="👋 Welcome @$ISSUE_AUTHOR! Thanks for opening your first issue in Evercatch!\n\nOur team will review this shortly. In the meantime:\n- Check our [documentation](https://docs.evercatch.dev) for common solutions\n- Join our [community](https://community.evercatch.dev) for discussions\n- Review our [Code of Conduct](CODE_OF_CONDUCT.md)\n\nWe appreciate your contribution! 🎉"
COMMENT_JSON=$(jq -n --arg body "$WELCOME_MSG" '{"body": $body}')
curl -s -f -X POST -H "Authorization: token $API_TOKEN" -H "Content-Type: application/json" -d "$COMMENT_JSON" "$API_BASE_URL/repos/$REPO/issues/$ISSUE_NUMBER/comments"
fi
- name: Auto-label bug reports
if: contains(gitea.event.issue.title, '[BUG]') || contains(gitea.event.issue.labels.*.name, 'bug')
run: |
LABELS='{"labels": ["bug", "status: investigating"]}'
curl -s -f -X POST -H "Authorization: token $API_TOKEN" -H "Content-Type: application/json" -d "$LABELS" "$API_BASE_URL/repos/$REPO/issues/$ISSUE_NUMBER/labels"
- name: Auto-label feature requests
if: contains(gitea.event.issue.title, '[FEATURE]') || contains(gitea.event.issue.labels.*.name, 'feature')
run: |
LABELS='{"labels": ["feature", "status: under-review"]}'
curl -s -f -X POST -H "Authorization: token $API_TOKEN" -H "Content-Type: application/json" -d "$LABELS" "$API_BASE_URL/repos/$REPO/issues/$ISSUE_NUMBER/labels"
- name: Auto-label support requests
if: contains(gitea.event.issue.title, '[SUPPORT]') || contains(gitea.event.issue.labels.*.name, 'question')
run: |
LABELS='{"labels": ["question", "status: needs-response"]}'
curl -s -f -X POST -H "Authorization: token $API_TOKEN" -H "Content-Type: application/json" -d "$LABELS" "$API_BASE_URL/repos/$REPO/issues/$ISSUE_NUMBER/labels"
- name: Auto-label documentation issues
if: contains(gitea.event.issue.title, '[DOCS]') || contains(gitea.event.issue.labels.*.name, 'documentation')
run: |
LABELS='{"labels": ["documentation", "status: under-review"]}'
curl -s -f -X POST -H "Authorization: token $API_TOKEN" -H "Content-Type: application/json" -d "$LABELS" "$API_BASE_URL/repos/$REPO/issues/$ISSUE_NUMBER/labels"
- name: Assign to team for critical bugs
if: contains(gitea.event.issue.labels.*.name, 'priority: critical')
run: |
ASSIGNEES='{"assignees": ["psmattas"]}'
curl -s -f -X PATCH -H "Authorization: token $API_TOKEN" -H "Content-Type: application/json" -d "$ASSIGNEES" "$API_BASE_URL/repos/$REPO/issues/$ISSUE_NUMBER"
URGENT_MSG="🚨 **Critical Issue Detected**\n\nThis issue has been marked as critical and assigned to @psmattas for immediate attention.\n\nExpected response time: **4 hours**"
COMMENT_JSON=$(jq -n --arg body "$URGENT_MSG" '{"body": $body}')
curl -s -f -X POST -H "Authorization: token $API_TOKEN" -H "Content-Type: application/json" -d "$COMMENT_JSON" "$API_BASE_URL/repos/$REPO/issues/$ISSUE_NUMBER/comments"
- name: Request more info for incomplete issues
if: gitea.event.action == 'opened' && !contains(gitea.event.issue.body, 'Steps to Reproduce') && contains(gitea.event.issue.labels.*.name, 'bug')
run: |
INFO_MSG="Hi @$ISSUE_AUTHOR! 👋\n\nTo help us investigate this bug, could you please provide:\n\n1. **Steps to reproduce** the issue\n2. **Expected behavior** vs **actual behavior**\n3. Your **subscription tier**\n4. **Event IDs** or **timestamps** (if applicable)\n\nThis will help us resolve the issue faster. Thanks!"
COMMENT_JSON=$(jq -n --arg body "$INFO_MSG" '{"body": $body}')
curl -s -f -X POST -H "Authorization: token $API_TOKEN" -H "Content-Type: application/json" -d "$COMMENT_JSON" "$API_BASE_URL/repos/$REPO/issues/$ISSUE_NUMBER/comments"
LABELS='{"labels": ["status: needs-info"]}'
curl -s -f -X POST -H "Authorization: token $API_TOKEN" -H "Content-Type: application/json" -d "$LABELS" "$API_BASE_URL/repos/$REPO/issues/$ISSUE_NUMBER/labels"

View File

@@ -49,17 +49,16 @@ jobs:
LABEL_JSON='{ "labels": ["status: needs-rebase"] }' LABEL_JSON='{ "labels": ["status: needs-rebase"] }'
LABELS_API_URL="$API_BASE_URL/repos/$REPO/issues/$PR_NUMBER/labels" LABELS_API_URL="$API_BASE_URL/repos/$REPO/issues/$PR_NUMBER/labels"
curl -s -f -X POST -H "Authorization: token $API_TOKEN" -H "Content-Type: application/json" -d "$LABEL_JSON" "$LABELS_API_URL" curl -s -f -X POST -H "Authorization: token $API_TOKEN" -H "Content-Type: application/json" -d "$LABEL_JSON" "$LABELS_API_URL"
- name: Remove 'needs-rebase' label if clean - name: Remove 'needs-rebase' label if clean
if: gitea.event.pull_request.merge_status != 'CONFLICT' if: gitea.event.pull_request.merge_status != 'CONFLICT'
run: | run: |
LABEL_TO_REMOVE="status: needs-rebase" LABEL_TO_REMOVE="status: needs-rebase"
ISSUES_API_URL="$API_BASE_URL/repos/$REPO/issues/$PR_NUMBER" ISSUES_API_URL="$API_BASE_URL/repos/$REPO/issues/$PR_NUMBER"
CURRENT_LABELS_JSON=$(curl -s -H "Authorization: token $API_TOKEN" "$ISSUES_API_URL/labels") CURRENT_LABELS_JSON=$(curl -s -H "Authorization: token $API_TOKEN" "$ISSUES_API_URL/labels")
LABEL_ID=$(echo "$CURRENT_LABELS_JSON" | jq --arg name "$LABEL_TO_REMOVE" '.[] | select(.name == $name) | .id') LABEL_ID=$(echo "$CURRENT_LABELS_JSON" | jq --arg name "$LABEL_TO_REMOVE" '.[] | select(.name == $name) | .id')
# Only try to delete the label if we found its ID
if [ -n "$LABEL_ID" ]; then if [ -n "$LABEL_ID" ]; then
echo "Found and removing '$LABEL_TO_REMOVE' (ID: $LABEL_ID)..." echo "Found and removing '$LABEL_TO_REMOVE' (ID: $LABEL_ID)..."
curl -s -f -X DELETE -H "Authorization: token $API_TOKEN" "$ISSUES_API_URL/labels/$LABEL_ID" curl -s -f -X DELETE -H "Authorization: token $API_TOKEN" "$ISSUES_API_URL/labels/$LABEL_ID"
@@ -68,9 +67,9 @@ jobs:
- name: Auto-assign reviewers - name: Auto-assign reviewers
run: | run: |
REVIEWERS_JSON='{ "reviewers": ["psmattas"] }' REVIEWERS_JSON='{ "reviewers": ["psmattas"] }'
if [[ -n "$REVIEWERS_JSON" ]]; then if [[ -n "$REVIEWERS_JSON" ]]; then
echo "Requesting review from: $REVIEWERS_JSON" echo "Requesting review from: $REVIEWERS_JSON"
API_URL="$API_BASE_URL/repos/$REPO/pulls/$PR_NUMBER/requested_reviewers" API_URL="$API_BASE_URL/repos/$REPO/pulls/$PR_NUMBER/requested_reviewers"
curl -s -f -X POST -H "Authorization: token $API_TOKEN" -H "Content-Type: application/json" -d "$REVIEWERS_JSON" "$API_URL" curl -s -f -X POST -H "Authorization: token $API_TOKEN" -H "Content-Type: application/json" -d "$REVIEWERS_JSON" "$API_URL"
fi fi

View File

@@ -22,7 +22,7 @@ jobs:
The title of this pull request does not follow the required format. This PR is currently **blocked from merging**. The title of this pull request does not follow the required format. This PR is currently **blocked from merging**.
To fix this, please update the title to match the following structure: To fix this, please update the title to match the following structure:
**`EC-[JIRA_NUMBER]: [TYPE]: [Your Description]`** **`EC-[ISSUE_NUMBER]: [TYPE]: [Your Description]`**
- **Valid [TYPE] values are:** `FEAT`, `FIX`, `DOCS`, `REFACTOR`, `STYLE`, `PERF`, `TEST`, `CHORE` - **Valid [TYPE] values are:** `FEAT`, `FIX`, `DOCS`, `REFACTOR`, `STYLE`, `PERF`, `TEST`, `CHORE`
- **Example:** `EC-42: FEAT: Add new user login endpoint` - **Example:** `EC-42: FEAT: Add new user login endpoint`
@@ -40,7 +40,7 @@ jobs:
<!-- WIP_VALIDATOR_COMMENT --> <!-- WIP_VALIDATOR_COMMENT -->
run: | run: |
WIP_LABEL="status: work-in-progress" WIP_LABEL="status: work-in-progress"
INVALID_LABEL="INVALID-TITLE-DO-NOT-MERGE" INVALID_LABEL="status: invalid-title"
WIP_REGEX="^WIP:" WIP_REGEX="^WIP:"
FORMAT_REGEX="^EC-[0-9]+: (FEAT|FIX|DOCS|REFACTOR|STYLE|PERF|TEST|CHORE): .+$" FORMAT_REGEX="^EC-[0-9]+: (FEAT|FIX|DOCS|REFACTOR|STYLE|PERF|TEST|CHORE): .+$"
ISSUES_API_URL="$API_BASE_URL/repos/$REPO/issues/$PR_NUMBER" ISSUES_API_URL="$API_BASE_URL/repos/$REPO/issues/$PR_NUMBER"
@@ -67,7 +67,7 @@ jobs:
elif [[ "$PR_TITLE" =~ $FORMAT_REGEX ]]; then elif [[ "$PR_TITLE" =~ $FORMAT_REGEX ]]; then
echo "✅ PR title format is correct and not a WIP." echo "✅ PR title format is correct and not a WIP."
echo "Checking for labels to remove..." echo "Checking for labels to remove..."
CURRENT_LABELS_JSON=$(curl -s -H "Authorization: token $API_TOKEN" "$ISSUES_API_URL/labels") CURRENT_LABELS_JSON=$(curl -s -H "Authorization: token $API_TOKEN" "$ISSUES_API_URL/labels")
INVALID_LABEL_ID=$(echo "$CURRENT_LABELS_JSON" | jq --arg name "$INVALID_LABEL" '.[] | select(.name == $name) | .id') INVALID_LABEL_ID=$(echo "$CURRENT_LABELS_JSON" | jq --arg name "$INVALID_LABEL" '.[] | select(.name == $name) | .id')
@@ -81,15 +81,15 @@ jobs:
echo "Found and removing '$WIP_LABEL' (ID: $WIP_LABEL_ID)..." echo "Found and removing '$WIP_LABEL' (ID: $WIP_LABEL_ID)..."
curl -s -f -X DELETE -H "Authorization: token $API_TOKEN" "$ISSUES_API_URL/labels/$WIP_LABEL_ID" curl -s -f -X DELETE -H "Authorization: token $API_TOKEN" "$ISSUES_API_URL/labels/$WIP_LABEL_ID"
fi fi
exit 0 exit 0
else else
echo "❌ ERROR: PR title does not match the required format." echo "❌ ERROR: PR title does not match the required format."
echo "Adding '$INVALID_LABEL' label..." echo "Adding '$INVALID_LABEL' label..."
LABEL_JSON=$(echo "$INVALID_LABEL" | jq -R '{"labels": [.]}') LABEL_JSON=$(echo "$INVALID_LABEL" | jq -R '{"labels": [.]}')
curl -s -f -X POST -H "Authorization: token $API_TOKEN" -H "Content-Type: application/json" -d "$LABEL_JSON" "$ISSUES_API_URL/labels" curl -s -f -X POST -H "Authorization: token $API_TOKEN" -H "Content-Type: application/json" -d "$LABEL_JSON" "$ISSUES_API_URL/labels"
# Remove WIP label just in case # Remove WIP label just in case
ENCODED_WIP_LABEL=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$WIP_LABEL'))") ENCODED_WIP_LABEL=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$WIP_LABEL'))")
curl -s -X DELETE -H "Authorization: token $API_TOKEN" "$ISSUES_API_URL/labels/$ENCODED_WIP_LABEL" || true curl -s -X DELETE -H "Authorization: token $API_TOKEN" "$ISSUES_API_URL/labels/$ENCODED_WIP_LABEL" || true
@@ -102,4 +102,4 @@ jobs:
curl -s -f -X POST -H "Authorization: token $API_TOKEN" -H "Content-Type: application/json" -d "$COMMENT_JSON" "$ISSUES_API_URL/comments" curl -s -f -X POST -H "Authorization: token $API_TOKEN" -H "Content-Type: application/json" -d "$COMMENT_JSON" "$ISSUES_API_URL/comments"
fi fi
exit 1 exit 1
fi fi

View File

@@ -0,0 +1,98 @@
name: Close Stale Issues
on:
schedule:
- cron: '0 0 * * *' # Daily at midnight UTC
workflow_dispatch: # Manual trigger
jobs:
stale:
runs-on: ubuntu-latest
env:
API_TOKEN: ${{ secrets.BOT_ACCESS_TOKEN }}
API_BASE_URL: https://git.psmattas.com/api/v1
REPO: ${{ gitea.repository }}
steps:
- name: Mark stale issues
run: |
# Issues with no activity for 30 days
THIRTY_DAYS_AGO=$(date -u -d '30 days ago' '+%Y-%m-%dT%H:%M:%SZ')
# Get all open issues updated before 30 days ago
ISSUES=$(curl -s -H "Authorization: token $API_TOKEN" "$API_BASE_URL/repos/$REPO/issues?state=open&since=$THIRTY_DAYS_AGO&sort=updated&order=asc" | jq -c '.[]')
echo "$ISSUES" | while IFS= read -r issue; do
ISSUE_NUMBER=$(echo "$issue" | jq -r '.number')
UPDATED_AT=$(echo "$issue" | jq -r '.updated_at')
HAS_STALE_LABEL=$(echo "$issue" | jq -r '.labels[] | select(.name == "status: stale") | .name')
# Skip if already has stale label
if [ -n "$HAS_STALE_LABEL" ]; then
continue
fi
echo "Marking issue #$ISSUE_NUMBER as stale (last updated: $UPDATED_AT)"
# Add stale label
LABELS='{"labels": ["status: stale"]}'
curl -s -f -X POST -H "Authorization: token $API_TOKEN" -H "Content-Type: application/json" -d "$LABELS" "$API_BASE_URL/repos/$REPO/issues/$ISSUE_NUMBER/labels"
# Add stale comment
STALE_MSG="This issue has been automatically marked as stale because it has not had recent activity.\n\nIt will be closed in **14 days** if no further activity occurs.\n\nIf this issue is still relevant, please:\n- Add a comment with updates\n- Remove the \`status: stale\` label\n\nThank you for your contributions! 🙏"
COMMENT_JSON=$(jq -n --arg body "$STALE_MSG" '{"body": $body}')
curl -s -f -X POST -H "Authorization: token $API_TOKEN" -H "Content-Type: application/json" -d "$COMMENT_JSON" "$API_BASE_URL/repos/$REPO/issues/$ISSUE_NUMBER/comments"
done
- name: Close stale issues
run: |
# Issues marked stale for 14+ days
FOURTEEN_DAYS_AGO=$(date -u -d '14 days ago' '+%Y-%m-%dT%H:%M:%SZ')
# Get stale issues
ISSUES=$(curl -s -H "Authorization: token $API_TOKEN" "$API_BASE_URL/repos/$REPO/issues?state=open&labels=status:stale" | jq -c '.[]')
echo "$ISSUES" | while IFS= read -r issue; do
ISSUE_NUMBER=$(echo "$issue" | jq -r '.number')
UPDATED_AT=$(echo "$issue" | jq -r '.updated_at')
# Check if updated_at is older than 14 days
if [[ "$UPDATED_AT" < "$FOURTEEN_DAYS_AGO" ]]; then
echo "Closing stale issue #$ISSUE_NUMBER (last updated: $UPDATED_AT)"
# Close the issue
CLOSE_JSON='{"state": "closed"}'
curl -s -f -X PATCH -H "Authorization: token $API_TOKEN" -H "Content-Type: application/json" -d "$CLOSE_JSON" "$API_BASE_URL/repos/$REPO/issues/$ISSUE_NUMBER"
# Add closing comment
CLOSE_MSG="This issue was automatically closed due to inactivity.\n\nIf you believe this was closed in error, please:\n1. Reopen the issue\n2. Provide updated information\n3. Remove the \`status: stale\` label\n\nThank you! 🙏"
COMMENT_JSON=$(jq -n --arg body "$CLOSE_MSG" '{"body": $body}')
curl -s -f -X POST -H "Authorization: token $API_TOKEN" -H "Content-Type: application/json" -d "$COMMENT_JSON" "$API_BASE_URL/repos/$REPO/issues/$ISSUE_NUMBER/comments"
fi
done
- name: Remove stale label on activity
run: |
# Get stale issues that were recently updated
ISSUES=$(curl -s -H "Authorization: token $API_TOKEN" "$API_BASE_URL/repos/$REPO/issues?state=open&labels=status:stale" | jq -c '.[]')
echo "$ISSUES" | while IFS= read -r issue; do
ISSUE_NUMBER=$(echo "$issue" | jq -r '.number')
# Get comments
COMMENTS=$(curl -s -H "Authorization: token $API_TOKEN" "$API_BASE_URL/repos/$REPO/issues/$ISSUE_NUMBER/comments")
LATEST_COMMENT_AUTHOR=$(echo "$COMMENTS" | jq -r '.[-1].user.login')
# If latest comment is not from bot, remove stale label
if [ "$LATEST_COMMENT_AUTHOR" != "EC-bot" ] && [ "$LATEST_COMMENT_AUTHOR" != "gitea-actions" ]; then
echo "Removing stale label from issue #$ISSUE_NUMBER (recent activity detected)"
# Get label ID
LABELS=$(curl -s -H "Authorization: token $API_TOKEN" "$API_BASE_URL/repos/$REPO/issues/$ISSUE_NUMBER/labels")
STALE_LABEL_ID=$(echo "$LABELS" | jq -r '.[] | select(.name == "status: stale") | .id')
if [ -n "$STALE_LABEL_ID" ]; then
curl -s -f -X DELETE -H "Authorization: token $API_TOKEN" "$API_BASE_URL/repos/$REPO/issues/$ISSUE_NUMBER/labels/$STALE_LABEL_ID"
fi
fi
done