This is an automated email from the ASF dual-hosted git repository.

mfordjody pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/dubbo-kubernetes.git


The following commit(s) were added to refs/heads/master by this push:
     new d255365a Add maintainer approval status gate (#969)
d255365a is described below

commit d255365a826f692998dd4be1065a7f18dd0d74c3
Author: mfordjody <[email protected]>
AuthorDate: Mon Jul 20 02:43:37 2026 +0800

    Add maintainer approval status gate (#969)
---
 .asf.yaml                         |  29 ----------
 .github/workflows/commands.yml    |  37 +++++++++++-
 .github/workflows/review-gate.yml | 117 ++++++++++++++++++++++++++++++++++++++
 OWNERS                            |   4 +-
 4 files changed, 153 insertions(+), 34 deletions(-)

diff --git a/.asf.yaml b/.asf.yaml
index 4833ba24..4b54410a 100644
--- a/.asf.yaml
+++ b/.asf.yaml
@@ -13,11 +13,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-meta:
-  # ASF currently gates repository ruleset reconciliation behind this rollout.
-  environment: github_rulesets
-  nextgen: true
-
 notifications:
     commits:      [email protected]
     issues:       [email protected]
@@ -47,27 +42,3 @@ github:
         contexts:
           # This stable aggregate check fails unless every required CI job 
passes.
           - CI Required
-  rulesets:
-    # Stage the review policy on an isolated branch before targeting master.
-    - name: "Solo Maintainer Review Gate"
-      target: branch
-      enforcement: active
-      bypass_actors:
-        - actor_id: 91593982
-          actor_type: User
-          bypass_mode: pull_request
-      conditions:
-        ref_name:
-          include:
-            - "refs/heads/policy/ruleset-sandbox"
-          exclude: []
-      rules:
-        - type: deletion
-        - type: non_fast_forward
-        - type: pull_request
-          parameters:
-            dismiss_stale_reviews_on_push: true
-            require_code_owner_review: true
-            require_last_push_approval: false
-            required_approving_review_count: 1
-            required_review_thread_resolution: true
diff --git a/.github/workflows/commands.yml b/.github/workflows/commands.yml
index 6e913d0f..a0cdfbe6 100644
--- a/.github/workflows/commands.yml
+++ b/.github/workflows/commands.yml
@@ -20,9 +20,9 @@
 #   /unassign [@user ...]  remove assignees
 #   /lgtm                  (PR only, write access, not the PR author) label the
 #                          PR with `lgtm`; if the commenter is an approver in
-#                          the root OWNERS file, request a squash merge (or
-#                          auto-merge while checks run). Repository rulesets
-#                          still enforce CI and required Code Owner review;
+#                          the root OWNERS file, record Maintainer Approval and
+#                          request a squash merge (or auto-merge while checks
+#                          run). Required status checks still enforce CI;
 #                          non-approvers only apply the label
 #   /lgtm cancel           remove the `lgtm` label and disable auto-merge
 #   /close                 close the issue/PR (author or write access)
@@ -49,6 +49,7 @@ permissions:
   issues: write
   pull-requests: write
   actions: write
+  statuses: write
 
 jobs:
   command:
@@ -70,6 +71,36 @@ jobs:
           app-id: ${{ secrets.BOT_APP_ID }}
           private-key: ${{ secrets.BOT_APP_PRIVATE_KEY }}
 
+      # Keep the required status tied to the built-in GitHub Actions App even
+      # when slash-command comments and merges use a custom bot identity.
+      - name: Record maintainer approval
+        if: github.event.issue.pull_request && github.actor == 'mfordjody'
+        uses: actions/github-script@v7
+        with:
+          github-token: ${{ github.token }}
+          script: |
+            const firstLine = (context.payload.comment.body || 
'').trim().split(/\r?\n/)[0].trim();
+            const match = firstLine.match(/^\/lgtm(?:\s+(cancel))?\s*$/i);
+            if (!match) return;
+
+            const {owner, repo} = context.repo;
+            const pullNumber = context.payload.issue.number;
+            const {data: pr} = await github.rest.pulls.get({owner, repo, 
pull_number: pullNumber});
+            const authorIsMaintainer = pr.user.login.toLowerCase() === 
'mfordjody';
+            if (!match[1] && authorIsMaintainer) return;
+
+            await github.rest.repos.createCommitStatus({
+              owner,
+              repo,
+              sha: pr.head.sha,
+              state: match[1] && !authorIsMaintainer ? 'pending' : 'success',
+              context: 'Maintainer Approval',
+              description: match[1] && !authorIsMaintainer
+                ? 'Maintainer approval cancelled'
+                : 'Approved by @mfordjody via /lgtm',
+              target_url: pr.html_url,
+            });
+
       - name: Dispatch command
         uses: actions/github-script@v7
         with:
diff --git a/.github/workflows/review-gate.yml 
b/.github/workflows/review-gate.yml
new file mode 100644
index 00000000..196aa218
--- /dev/null
+++ b/.github/workflows/review-gate.yml
@@ -0,0 +1,117 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+name: Maintainer Review Gate
+
+on:
+  pull_request_target:
+    types:
+      - opened
+      - reopened
+      - synchronize
+      - ready_for_review
+  pull_request_review:
+    types:
+      - submitted
+      - dismissed
+  push:
+    branches:
+      - master
+  workflow_dispatch:
+    inputs:
+      pr_number:
+        description: Optional pull request number to recompute
+        required: false
+        type: number
+
+permissions:
+  contents: read
+  pull-requests: read
+  statuses: write
+
+jobs:
+  approval:
+    name: Maintainer Approval
+    runs-on: ubuntu-latest
+    timeout-minutes: 5
+    if: |
+      github.repository == 'apache/dubbo-kubernetes' &&
+      (github.event_name != 'pull_request_review' ||
+       github.event.review.user.login == 'mfordjody')
+    steps:
+      - name: Publish approval status
+        uses: actions/github-script@v7
+        with:
+          script: |
+            const {owner, repo} = context.repo;
+            const maintainer = 'mfordjody';
+            const publish = async (sha, state, description, targetUrl) => {
+              await github.rest.repos.createCommitStatus({
+                owner,
+                repo,
+                sha,
+                state,
+                context: 'Maintainer Approval',
+                description,
+                target_url: targetUrl,
+              });
+            };
+
+            if (context.eventName === 'push') {
+              await publish(context.sha, 'success', 'Merged through the 
protected branch', context.payload.compare);
+              return;
+            }
+
+            let pr = context.payload.pull_request;
+            if (context.eventName === 'workflow_dispatch') {
+              const prNumber = Number(context.payload.inputs?.pr_number || 0);
+              if (!prNumber) {
+                await publish(context.sha, 'success', 'Maintainer-authorized 
workflow dispatch', 
`${context.serverUrl}/${owner}/${repo}/actions/runs/${context.runId}`);
+                return;
+              }
+              ({data: pr} = await github.rest.pulls.get({owner, repo, 
pull_number: prNumber}));
+            }
+
+            if (!pr) {
+              core.setFailed('Pull request payload is required');
+              return;
+            }
+
+            const authorIsMaintainer = pr.user.login.toLowerCase() === 
maintainer;
+            let approved = authorIsMaintainer;
+            if (!approved) {
+              const reviews = await 
github.paginate(github.rest.pulls.listReviews, {
+                owner,
+                repo,
+                pull_number: pr.number,
+                per_page: 100,
+              });
+              approved = reviews.some((review) =>
+                review.user?.login.toLowerCase() === maintainer &&
+                review.state === 'APPROVED' &&
+                review.commit_id === pr.head.sha,
+              );
+            }
+
+            await publish(
+              pr.head.sha,
+              approved ? 'success' : 'pending',
+              authorIsMaintainer
+                ? 'Maintainer-authored pull request'
+                : approved
+                  ? `Approved by @${maintainer}`
+                  : `Waiting for @${maintainer} approval`,
+              pr.html_url,
+            );
diff --git a/OWNERS b/OWNERS
index 5d03d00d..9e7261b5 100644
--- a/OWNERS
+++ b/OWNERS
@@ -14,8 +14,8 @@
 # limitations under the License.
 
 # OWNERS drives the /lgtm merge command in .github/workflows/commands.yml.
-# GitHub CODEOWNERS and the repository rulesets independently enforce the
-# required maintainer review before another user's pull request can merge.
+# The required Maintainer Approval status independently enforces approval by
+# an approver before another user's pull request can merge.
 
 approvers:
   - mfordjody

Reply via email to