-
Notifications
You must be signed in to change notification settings - Fork 133
Implement '/lgtm' ChatOps Workflow. #612
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
6692afe
71a218e
4203f6c
077f8de
85a7d53
bf39544
bb36230
60cb0ff
2f3dbb7
a7fb2b5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| name: ChatOps Dispatcher | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. due to my lack of knowledge, what does this action do? Is only used to dispatch other jobs when a comment is made?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes It is a "traffic controller" action that robustly parses slash commands (like /lgtm) from comments, validates user permissions, and dispatches events to trigger specific workflows. |
||
| on: | ||
| issue_comment: | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Q: shuoldn't this be done on PR comments? (assuming there is such a trigger)
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, apparently in GitHub's internal architecture, every Pull Request is an Issue. It has |
||
| types: [created] | ||
|
|
||
| jobs: | ||
| dispatch: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Slash Command Dispatch | ||
| uses: peter-evans/slash-command-dispatch@v3 | ||
| with: | ||
| token: ${{ secrets.GITHUB_TOKEN }} | ||
| commands: lgtm | ||
| # 1. Basic Security: Only allow users with 'write' access to even trigger this | ||
| permission: write | ||
| issue-type: pull-request | ||
| reactions: false | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. typo in file name, missing
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks will fix it. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,134 @@ | ||
| # ============================================================================ | ||
| # LGTM Command Worker | ||
| # ============================================================================ | ||
| # Handles /lgtm commands from chatops-dispatcher | ||
| # | ||
| # Flow: | ||
| # 1. User comments /lgtm on PR | ||
| # 2. chatops-dispatcher catches it and dispatches here | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. how does the dispatcher know which job/action to invoke?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is an "invisible handshake" done through Events, not file references. When /lgtm is typed, the slash-command-dispatch action fires a Repository Dispatch event. By default, it constructs the event type by adding -command to the slash command. The Signal: When the dispatcher validates the /lgtm comment, it broadcasts a repository_dispatch event with the custom type lgtm-command. The Listener: The lgtm-command.yml workflow is explicitly configured to listen for this signal via types: [lgtm-command]. |
||
| # 3. This workflow: | ||
| # - Verifies user is listed under 'reviewers' in OWNERS file | ||
| # - Checks if PR is draft | ||
| # - Checks for blocking labels (hold) | ||
| # - Adds lgtm label | ||
| # - Enables auto-merge | ||
| # ============================================================================ | ||
|
|
||
| name: LGTM Command Worker | ||
| on: | ||
| repository_dispatch: | ||
| types: [lgtm-command] | ||
|
|
||
| env: | ||
| BLOCKING_LABELS: "hold" | ||
|
|
||
| jobs: | ||
| apply-lgtm: | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: write | ||
| pull-requests: write | ||
| issues: write | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - uses: tibdex/github-app-token@v1 | ||
| id: generate-token | ||
| with: | ||
| app_id: ${{ secrets.VLLMD_BOT_APP_ID }} | ||
| private_key: ${{ secrets.VLLMD_BOT_APP_PRIVATE_KEY }} | ||
| repository: ${{ github.repository }} | ||
|
|
||
| # ----------------------------------------------------------------------- | ||
| # STEP 1: AUTHORIZATION - Verify user is in OWNERS file | ||
| # ----------------------------------------------------------------------- | ||
| # Only users listed as reviewers in OWNERS can use /lgtm | ||
| # This prevents unauthorized users from applying the lgtm label | ||
| - name: Check Permissions | ||
| env: | ||
| ACTOR: ${{ github.event.client_payload.github.actor }} | ||
| run: | | ||
| # Extract only the reviewers section and check if ACTOR is listed | ||
| REVIEWERS=$(sed -n '/^reviewers:/,/^[^ -]/p' OWNERS | grep -v '^reviewers:') | ||
| if echo "$REVIEWERS" | grep -q "^\s*-\s*$ACTOR\s*$"; then | ||
| echo "User $ACTOR is authorized" | ||
| else | ||
| echo "::error:: User $ACTOR is not a reviewer." | ||
| exit 1 | ||
| fi | ||
|
|
||
| # ----------------------------------------------------------------------- | ||
| # STEP 2: VALIDATION - Check if PR is in draft mode | ||
| # ----------------------------------------------------------------------- | ||
| # Draft PRs cannot be approved - they must be marked ready for review | ||
| # This prevents accidental approval of incomplete work | ||
| - name: Check Draft Status | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| PR_NUMBER: ${{ github.event.client_payload.github.payload.issue.number }} | ||
| REPO: ${{ github.repository }} | ||
| run: | | ||
| IS_DRAFT=$(gh pr view $PR_NUMBER --repo "$REPO" --json isDraft --jq '.isDraft') | ||
| if [ "$IS_DRAFT" = "true" ]; then | ||
| echo "::error:: Cannot LGTM a Draft PR." | ||
| gh issue comment $PR_NUMBER --repo "$REPO" --body "⚠️ **LGTM Failed**: PR is a Draft." | ||
| exit 1 | ||
| fi | ||
|
|
||
| # ----------------------------------------------------------------------- | ||
| # STEP 3: BLOCKING LABELS - Check for hold | ||
| # ----------------------------------------------------------------------- | ||
| # If any blocking label exists, fail immediately | ||
| # This prevents approving PRs that are explicitly marked as not ready | ||
| - name: Check for Blocking Labels | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| PR_URL: ${{ github.event.client_payload.github.payload.issue.html_url }} | ||
| run: | | ||
| LABELS=$(gh pr view "$PR_URL" --json labels --jq '.labels[].name') | ||
| if echo "$LABELS" | grep -Eiq "^($BLOCKING_LABELS)$"; then | ||
| echo "::error:: PR is blocked by label." | ||
| gh issue comment "$PR_URL" --body " **Merge Blocked**: Please remove the \`hold\` label before merging." | ||
| exit 1 | ||
| fi | ||
|
|
||
| # ----------------------------------------------------------------------- | ||
| # STEP 4: APPLY LGTM - Add label, wait, then enable auto-merge | ||
| # ----------------------------------------------------------------------- | ||
| # 1. Add lgtm label (triggers gatekeeper to validate) | ||
| # 2. Enable auto-merge (PR will merge when all checks pass) | ||
| - name: Apply or Cancel LGTM | ||
| env: | ||
| GH_TOKEN: ${{ steps.generate-token.outputs.token }} | ||
| PR_NUMBER: ${{ github.event.client_payload.github.payload.issue.number }} | ||
| PR_URL: ${{ github.event.client_payload.github.payload.issue.html_url }} | ||
| # Extract the full comment body from the dispatcher payload | ||
| COMMENT_BODY: ${{ github.event.client_payload.github.payload.comment.body }} | ||
| run: | | ||
| # Check if the command is a cancellation | ||
| if echo "$COMMENT_BODY" | grep -q "/lgtm cancel"; then | ||
| echo "🚨 Retracting LGTM status..." | ||
|
|
||
| # 1. Remove lgtm label | ||
| gh issue edit "$PR_NUMBER" --remove-label "lgtm" || echo "Label already gone" | ||
|
|
||
| # 2. Disable Auto-Merge | ||
| gh pr merge --disable-auto "$PR_URL" || echo "Auto-merge was not enabled" | ||
|
|
||
| # 3. Notify user | ||
| gh issue comment "$PR_URL" --body "Retracted: **LGTM** label removed and auto-merge disabled by @$ACTOR." | ||
|
|
||
| else | ||
| echo "✅ Applying LGTM status..." | ||
|
|
||
| # 1. Add lgtm label | ||
| gh issue edit "$PR_NUMBER" --add-label "lgtm" | ||
|
|
||
| # 2. Enable auto-merge (Squash) | ||
| if ! gh pr merge --auto --squash "$PR_URL" 2>&1 | tee merge_output.txt; then | ||
| ERROR_MSG=$(cat merge_output.txt) | ||
| gh issue comment "$PR_URL" --body "⚠️ **Auto-merge failed**: $ERROR_MSG" | ||
| exit 1 | ||
| fi | ||
|
|
||
| gh issue comment "$PR_URL" --body "✅ **LGTM**: auto-merge enabled." | ||
| fi | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| # ============================================================================ | ||
| # LGTM Gatekeeper - Required Status Check | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Q: there seems to be quite a bit of overlap between this file and the previous. Can you explain how they differ and what purpuse they serve.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| # ============================================================================ | ||
| # Rules Enforced: | ||
| # 1. PR MUST have the "lgtm" label | ||
| # 2. PR MUST have a native GitHub Approved status (via /approve) | ||
| # 3. PR MUST NOT have blocking labels (hold) | ||
| # ============================================================================ | ||
|
|
||
| name: LGTM Gatekeeper | ||
| on: | ||
| pull_request: | ||
| types: [opened, labeled, unlabeled, reopened, synchronize] | ||
| pull_request_review: | ||
| types: [submitted, dismissed] | ||
|
|
||
| env: | ||
| BLOCKING_LABELS: "hold" | ||
|
|
||
| jobs: | ||
| validate-pr: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Enforce LGTM, Approvals & Blockers | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| # Dynamically grab the PR number depending on which event triggered the workflow | ||
| PR_NUMBER: ${{ github.event.pull_request.number || github.event.pull_request_review.pull_request.number }} | ||
| REPO: ${{ github.repository }} | ||
| run: | | ||
| # Fetch current labels and review status in a single API call | ||
| PR_DATA=$(gh pr view $PR_NUMBER --repo "$REPO" --json labels,reviewDecision) | ||
|
|
||
| # 1. Extract labels | ||
| LABELS=$(echo "$PR_DATA" | jq -r '.labels[].name') | ||
|
|
||
| # Check 1: IS IT BLOCKED? | ||
| if echo "$LABELS" | grep -Eiq "^($BLOCKING_LABELS)$"; then | ||
| echo "::error:: ⛔ FAILED: PR is blocked by a 'hold' label." | ||
| exit 1 | ||
| fi | ||
|
|
||
| # Check 2: DOES IT HAVE LGTM? | ||
| HAS_LGTM=false | ||
| if echo "$LABELS" | grep -Fqx "lgtm"; then | ||
| HAS_LGTM=true | ||
| fi | ||
|
|
||
| # Check 3: IS IT NATIVELY APPROVED? | ||
| REVIEW_STATUS=$(echo "$PR_DATA" | jq -r '.reviewDecision') | ||
| HAS_APPROVE=false | ||
| if [ "$REVIEW_STATUS" = "APPROVED" ]; then | ||
| HAS_APPROVE=true | ||
| fi | ||
|
|
||
| if [ "$HAS_LGTM" = false ] || [ "$HAS_APPROVE" = false ]; then | ||
| echo "::error:: ⛔ FAILED: PR requires both the 'lgtm' label and a native GitHub Approval." | ||
| echo " - lgtm label present: $HAS_LGTM" | ||
| echo " - Native Approval (reviewDecision): $REVIEW_STATUS" | ||
| exit 1 | ||
| fi | ||
|
|
||
| echo "✅ PASSED: PR has the lgtm label, is natively approved, and has no blockers." | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| # ============================================================================ | ||
| # LGTM Reset - Auto-Remove LGTM on New Commits | ||
| # ============================================================================ | ||
| # Kubernetes Prow behavior: When new commits are pushed, approval is invalidated | ||
| # | ||
| # What It Does: | ||
| # 1. Detects when new commits are pushed to a PR | ||
| # 2. Removes the "lgtm" label (if present) | ||
| # 3. Disables auto-merge (safety net) | ||
| # 4. Posts a comment explaining why | ||
| # ============================================================================ | ||
|
|
||
| name: LGTM Reset | ||
| on: | ||
| pull_request: | ||
| types: [synchronize] # Triggers instantly on new commits | ||
|
|
||
| jobs: | ||
| reset-lgtm: | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| pull-requests: write | ||
| steps: | ||
| - uses: tibdex/github-app-token@v1 | ||
| id: generate-token | ||
| with: | ||
| app_id: ${{ secrets.VLLMD_BOT_APP_ID }} | ||
| private_key: ${{ secrets.VLLMD_BOT_APP_PRIVATE_KEY }} | ||
| repository: ${{ github.repository }} | ||
| - name: Invalidate LGTM | ||
| env: | ||
| GH_TOKEN: ${{ steps.generate-token.outputs.token }} | ||
| PR_NUMBER: ${{ github.event.pull_request.number }} | ||
| REPO: ${{ github.repository }} | ||
| run: | | ||
| echo "🚨 New code pushed. Resetting LGTM status..." | ||
|
|
||
| # 1. Remove the label (This triggers the Gatekeeper to run again) | ||
| gh pr edit $PR_NUMBER --repo "$REPO" --remove-label "lgtm" || true | ||
|
|
||
| # 2. Disable Auto-Merge (Safety net) | ||
| gh pr merge --disable-auto $PR_NUMBER --repo "$REPO" || true | ||
|
|
||
| # 3. Notify user | ||
| gh issue comment $PR_NUMBER --repo "$REPO" --body "🔄 **Reset**: New commits pushed. LGTM removed." | ||
|
|
This file was deleted.
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -356,3 +356,74 @@ helm uninstall kgateway-crds -n kgateway-system | |
| ``` | ||
|
|
||
| For more details, see the Gateway API inference Extension [getting started guide](https://gateway-api-inference-extension.sigs.k8s.io/guides/) | ||
|
|
||
| ## PR Approval Process | ||
|
|
||
| The project uses a Prow-inspired ChatOps system to manage PR review process via comment commands. | ||
|
|
||
| ### Available Commands | ||
|
|
||
| | Command | Policy | Description | | ||
| |---------|--------|-------------| | ||
| | `/approve` | [OWNERS](./OWNERS) | Approve all the files for the current PR | | ||
| | `/approve cancel` | [OWNERS](./OWNERS) | Removes your approval on this pull-request | | ||
| | `/lgtm` | [OWNERS](./OWNERS) | Adds the `lgtm` label and enables auto-merge (squash). The PR merges automatically once requirements below are met | | ||
| | `/lgtm cancel` | [OWNERS](./OWNERS) | Removes the `lgtm` label and disables auto-merge | | ||
| | `/assign [@userA @userB @etc]` | Anyone | Assign other users (or yourself if no one is specified). Target user must be Org Member, Collaborator, or have previously commented | | ||
| | `/unassign [@userA @userB @etc]` | Anyone | Unassigns specified people (or yourself if no one is specified). Target must have been already assigned | | ||
| | `/cc [@userA @userB @etc]` | Anyone | Request review from specified people (or yourself if no one is specified). Target be an Org Member, Collaborator, or have previously commented | | ||
| | `/uncc [@userA @userB @etc]` | Anyone | Dismiss review request for specified people (or yourself if no one is specified). Target must already have had a review requested | | ||
| | `/close` | Collaborators | Closes the issue / PR | | ||
| | `/reopen` | Collaborators | Reopens a closed issue / PR | | ||
| | `/lock [resolved / off-topic / too-heated / spam]` | Collaborators | Locks the issue / PR with the specified reason | | ||
| | `/milestone milestone-name` | Collaborators | Adds issue / PR to an existing milestone | | ||
| | `/retitle some new title` | Collaborators | Renames the issue / PR | | ||
| | `/hold` | Anyone | Adds the `hold` label to prevent the PR from merging | | ||
| | `/hold cancel` | Anyone | Removes the `hold` label | | ||
| | `/area [label1 label2 ...]` | Anyone | Adds an area/<> label(s) if it's defined in the `.prowlabels.yaml` file | | ||
| | `/kind [label1 label2 ...]` | Anyone | Adds a kind/<> label(s) if it's defined in the `.prowlabels.yaml` file | | ||
| | `/priority [label1 label2 ...]` | Anyone | Adds a priority/<> label(s) if it's defined in the `.prowlabels.yaml` file | | ||
| | `/remove [label1 label2 ...]` | Collaborators | Removes a specified label(s) on an issue / PR | | ||
|
|
||
| ### Merge Requirements | ||
|
|
||
| For a PR to be merged, it must have: | ||
| - ✅ **The `lgtm` label AND a native GitHub Approval** - Triggered by authorized users via the `/lgtm` and `/approve` commands in the comments. | ||
| - ✅ **No blocking labels** - The `hold` label must not be present. | ||
| - ✅ **Signed and verified commits** - All commits must include a DCO sign-off. | ||
| - ✅ **All required status checks passing** - CI/CD pipelines and gatekeeper checks must succeed. | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated Developer.md: Added documentation noting that signed commits can be enforced as a pre-merge check via branch rulesets |
||
| The gatekeeper workflow enforces these requirements as a required status check. | ||
|
|
||
| *Note: The label and approval requirements are enforced by the custom LGTM Gatekeeper workflow, while commit signatures are enforced natively by the repository's branch rulesets.* | ||
|
|
||
| ### Approval Reset on New Commits | ||
|
|
||
| When new commits are pushed to an approved PR, the `lgtm` label is automatically removed and auto-merge is disabled. This ensures approvals always reflect the latest code. The author must request a new `/lgtm` after pushing changes. | ||
|
|
||
| **Note:** The `approve` label is NOT automatically removed on new commits. If significant changes are made, reviewers should use `/approve cancel` to remove their approval. | ||
|
|
||
| ## OWNERS | ||
|
|
||
| A simplified version of [Prow's OWNERS](https://go.k8s.io/owners) file is supported. When an OWNERS file is present at the root of the repository, it is used to authorize the /lgtm and /approve commands. | ||
|
|
||
| The `reviewers` role grants access to the /lgtm command and the approvers role grants access to the /approve command. | ||
|
|
||
| The `approvers` role does not grant the reviewers role, a user must be in both roles to use /lgtm and /approve. | ||
|
|
||
| The OWNERS file must be in YAML format. All entries are expected to be GitHub usernames; teams are not supported. | ||
|
|
||
| ```yaml | ||
| # List of usernames who may use /lgtm | ||
| reviewers: | ||
| - user1 | ||
| - user2 | ||
| - user3 | ||
|
|
||
| # List of usernames who may use /approve | ||
| approvers: | ||
| - user1 | ||
| - user2 | ||
| - admin1 | ||
| ``` | ||
|
|
||

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not a fan of calling it "chatops" in file or action name. Please use a more specific name (e.g.
dispatch-on-lgtm)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure Thanks