diff --git a/.github/mergeable.yml b/.github/mergeable.yml index c4bda8c..80971cd 100644 --- a/.github/mergeable.yml +++ b/.github/mergeable.yml @@ -25,6 +25,17 @@ mergeable: must_include: regex: "^(bug|enhancement|documentation|feature|refactor|performance|chore|wip|test|ci|security|dependencies)$" message: "PR must include at least one valid label." + # Ensure PR has at least one assignee + + - do: assignee + min: + count: 1 + message: "PR must have at least one assignee." + # Ensure PR has at least one reviewer requested + - do: approvals + min: + count: 1 + message: "PR must have at least one reviewer requested or approved." pass: - do: labels diff --git a/.github/workflows/github-event-notification.yaml b/.github/workflows/github-event-notification.yaml new file mode 100644 index 0000000..d452f56 --- /dev/null +++ b/.github/workflows/github-event-notification.yaml @@ -0,0 +1,178 @@ +name: Notify (GitHub Events) + +permissions: + issues: write + pull-requests: write + contents: read + +on: + issues: + types: [opened, edited, reopened, closed, assigned, labeled] + pull_request: + types: [opened, closed, reopened, ready_for_review, review_requested] + pull_request_review: + types: [submitted, edited, dismissed] + issue_comment: + types: [created, edited] + pull_request_review_comment: + types: [created, edited] + +jobs: + notify: + runs-on: ubuntu-latest + name: Send Notifications + + steps: + - name: Determine Event Type + id: event_info + run: | + EVENT="${{ github.event_name }}" + ACTION="${{ github.event.action }}" + REPO="${{ github.repository }}" + ACTOR="${{ github.actor }}" + REPO_URL="${{ github.server_url }}/${{ github.repository }}" + + if [[ "$EVENT" == "issues" ]]; then + TITLE="Issue $ACTION: ${{ github.event.issue.title }}" + URL="${{ github.event.issue.html_url }}" + NUMBER="${{ github.event.issue.number }}" + USER="${{ github.event.issue.user.login }}" + DESCRIPTION="Issue #$NUMBER by $USER" + STATE="${{ github.event.issue.state }}" + EMOJI="🐛" + + elif [[ "$EVENT" == "pull_request" ]]; then + NUMBER="${{ github.event.pull_request.number }}" + USER="${{ github.event.pull_request.user.login }}" + URL="${{ github.event.pull_request.html_url }}" + STATE="${{ github.event.pull_request.state }}" + SOURCE="${{ github.event.pull_request.head.ref }}" + TARGET="${{ github.event.pull_request.base.ref }}" + + if [[ "$ACTION" == "closed" && "${{ github.event.pull_request.merged }}" == "true" ]]; then + TITLE="PR merged: ${{ github.event.pull_request.title }}" + EMOJI="✅" + STATE="merged" + elif [[ "$ACTION" == "closed" ]]; then + TITLE="PR closed: ${{ github.event.pull_request.title }}" + EMOJI="❌" + else + TITLE="PR $ACTION: ${{ github.event.pull_request.title }}" + EMOJI="🔀" + fi + DESCRIPTION="PR #$NUMBER by $USER: $SOURCE → $TARGET" + + elif [[ "$EVENT" == "pull_request_review" ]]; then + TITLE="PR Review: ${{ github.event.pull_request.title }}" + URL="${{ github.event.review.html_url }}" + NUMBER="${{ github.event.pull_request.number }}" + USER="${{ github.event.review.user.login }}" + REVIEW_STATE="${{ github.event.review.state }}" + STATE="${{ github.event.review.state }}" + SOURCE="${{ github.event.pull_request.head.ref }}" + TARGET="${{ github.event.pull_request.base.ref }}" + DESCRIPTION="Review by $USER on PR #$NUMBER" + EMOJI="👀" + + elif [[ "$EVENT" == "issue_comment" ]]; then + NUMBER="${{ github.event.issue.number }}" + USER="${{ github.event.comment.user.login }}" + URL="${{ github.event.comment.html_url }}" + STATE="commented" + + if [[ "${{ github.event.issue.pull_request }}" != "" ]]; then + TITLE="Comment on PR: ${{ github.event.issue.title }}" + EMOJI="💬" + else + TITLE="Comment on Issue: ${{ github.event.issue.title }}" + EMOJI="💬" + fi + DESCRIPTION="Comment by $USER on #$NUMBER" + + elif [[ "$EVENT" == "pull_request_review_comment" ]]; then + TITLE="Comment on PR: ${{ github.event.pull_request.title }}" + URL="${{ github.event.comment.html_url }}" + NUMBER="${{ github.event.pull_request.number }}" + USER="${{ github.event.comment.user.login }}" + STATE="commented" + SOURCE="${{ github.event.pull_request.head.ref }}" + TARGET="${{ github.event.pull_request.base.ref }}" + DESCRIPTION="Review comment by $USER on PR #$NUMBER" + EMOJI="💬" + else + TITLE="GitHub Event: $EVENT" + URL="${{ github.event.repository.html_url }}" + DESCRIPTION="Event triggered in $REPO" + STATE="N/A" + NUMBER="N/A" + USER="$ACTOR" + EMOJI="📢" + fi + + echo "title=$TITLE" >> $GITHUB_OUTPUT + echo "url=$URL" >> $GITHUB_OUTPUT + echo "description=$DESCRIPTION" >> $GITHUB_OUTPUT + echo "emoji=$EMOJI" >> $GITHUB_OUTPUT + echo "number=${NUMBER:-N/A}" >> $GITHUB_OUTPUT + echo "user=${USER:-$ACTOR}" >> $GITHUB_OUTPUT + echo "state=${STATE:-N/A}" >> $GITHUB_OUTPUT + echo "source=${SOURCE:-N/A}" >> $GITHUB_OUTPUT + echo "target=${TARGET:-N/A}" >> $GITHUB_OUTPUT + echo "repo_url=$REPO_URL" >> $GITHUB_OUTPUT + + - name: Google Chat Notification + if: always() + uses: Co-qn/google-chat-notification@releases/v1 + with: + name: ${{ steps.event_info.outputs.title }} + url: ${{ secrets.GOOGLE_CHAT_WEBHOOK }} + status: ${{ job.status }} + + - name: Slack Notification + if: always() + uses: slackapi/slack-github-action@v1.24.0 + with: + channel-id: ${{ secrets.SLACK_CHANNEL_ID }} + payload: | + { + "text": "${{ steps.event_info.outputs.title }}", + "blocks": [ + { + "type": "header", + "text": { + "type": "plain_text", + "text": "${{ steps.event_info.outputs.emoji }} ${{ steps.event_info.outputs.title }}" + } + }, + { + "type": "section", + "fields": [ + { "type": "mrkdwn", "text": "*Repository:*\n<${{ steps.event_info.outputs.repo_url }}|${{ github.repository }}>" }, + { "type": "mrkdwn", "text": "*Event:*\n${{ github.event_name }}" }, + { "type": "mrkdwn", "text": "*Author:*\n${{ steps.event_info.outputs.user }}" }, + { "type": "mrkdwn", "text": "*Action:*\n${{ github.event.action }}" }, + { "type": "mrkdwn", "text": "*Number:*\n<${{ steps.event_info.outputs.url }}|#${{ steps.event_info.outputs.number }}>" }, + { "type": "mrkdwn", "text": "*State:*\n${{ steps.event_info.outputs.state }}" } + ] + }, + { + "type": "section", + "text": { "type": "mrkdwn", "text": "${{ steps.event_info.outputs.description }}" } + }, + { + "type": "actions", + "elements": [ + { "type": "button", "text": { "type": "plain_text", "text": "View on GitHub" }, "url": "${{ steps.event_info.outputs.url }}", "style": "primary" }, + { "type": "button", "text": { "type": "plain_text", "text": "View Repository" }, "url": "${{ steps.event_info.outputs.repo_url }}" } + ] + }, + { + "type": "context", + "elements": [ + { "type": "mrkdwn", "text": "Triggered by ${{ github.actor }} • ${{ github.event_name }} event" } + ] + } + ] + } + env: + SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 0000000..e0e34a5 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,47 @@ +name: Schedule Stale PRs and Issues Management + +permissions: + actions: write + contents: write + issues: write + pull-requests: write + +on: + schedule: + - cron: '0 9 * * 1,4' # Monday & Thursday at 9 AM UTC + workflow_dispatch: + +concurrency: + group: stale-management + cancel-in-progress: false + +jobs: + stale-management: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Manage Stale Issues and PRs + uses: actions/stale@v10.1.0 + with: + repo-token: ${{ github.token }} + + # General settings + days-before-stale: '30' + days-before-close: '7' + operations-per-run: '60' + remove-stale-when-updated: 'true' + + # Issue-specific settings + stale-issue-label: 'stale' + stale-issue-message: 'This issue has been marked as stale due to inactivity. It will be closed in 7 days unless there is further activity.' + close-issue-message: 'This issue has been closed due to inactivity. Please feel free to reopen if you think it is still relevant.' + exempt-issue-labels: 'bug,critical,security,high-priority,enhancement,discussion,help wanted,good first issue' + + # PR-specific settings + stale-pr-label: 'stale' + stale-pr-message: 'This pull request has been marked as stale due to inactivity. It will be closed in 7 days unless further changes are made or a review is requested.' + close-pr-message: 'This pull request has been closed due to inactivity. Please feel free to reopen if you think it is still relevant.' + exempt-pr-labels: 'WIP,In Progress' diff --git a/cluster-api/schemas/cluster_schema.py b/cluster-api/schemas/cluster_schema.py index 75cee10..e27bf79 100644 --- a/cluster-api/schemas/cluster_schema.py +++ b/cluster-api/schemas/cluster_schema.py @@ -1,3 +1,4 @@ +# pylint: disable=import-error """ Utility functions for cluster operations.