shahar1 commented on code in PR #74822:
URL: https://github.com/apache/airflow/pull/74822#discussion_r4301817949


##########
.github/workflows/fork-ci-monitor.yml:
##########
@@ -0,0 +1,71 @@
+# 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.
+#
+---
+# Drives pull requests whose CI runs in contributor forks (AIP-120): labels, 
draft state, the
+# state comment, the `Fork CI / Tests (AMD)` commit status and closing. It is 
the only writer for
+# those pull requests and never checks out or runs pull request code. See
+# dev/breeze/doc/ci/08_fork_ci_for_external_contributors.md.
+name: "Fork CI monitor"
+on:  # yamllint disable-line rule:truthy
+  schedule:
+    - cron: '*/15 * * * *'
+  workflow_dispatch:
+    inputs:
+      dry-run:
+        description: "Compute the changes without applying them to pull 
requests"
+        type: boolean
+        default: false
+permissions:
+  contents: read
+  # Labels, draft state, comments and closing of gated pull requests.
+  pull-requests: write
+  # The `Fork CI / Tests (AMD)` commit status on pull request head commits.
+  statuses: write
+concurrency:
+  group: fork-ci-monitor
+  cancel-in-progress: false
+
+jobs:
+  reconcile:
+    name: "Reconcile pull requests gated to fork CI"
+    if: github.repository == 'apache/airflow'
+    # The breeze action frees disk space with sudo and docker, which 
ubuntu-slim does not provide.
+    runs-on: ubuntu-latest
+    timeout-minutes: 20
+    steps:
+      - name: "Checkout main"
+        uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1  # 
v7.0.1
+        with:
+          ref: main
+          persist-credentials: false
+      - name: "Install Breeze"
+        uses: ./.github/actions/breeze
+      - name: "Reconcile"
+        id: reconcile
+        env:
+          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          DRY_RUN: ${{ inputs.dry-run && 'true' || 'false' }}

Review Comment:
   `DRY_RUN` stops the mutations, but the Slack step below still posts when 
`has-alerts` is true, so a `workflow_dispatch` dry run reports closures it did 
not make. Adding `&& !inputs.dry-run` to that step's `if` would avoid it.



##########
dev/breeze/src/airflow_breeze/utils/fork_ci_github.py:
##########
@@ -0,0 +1,170 @@
+# 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.
+"""GitHub access for the fork CI reconciler (AIP-120); the only module of it 
that calls ``gh``."""
+
+from __future__ import annotations
+
+import json
+import subprocess
+import time
+from collections import Counter
+from collections.abc import Sequence
+from typing import Any
+from urllib.parse import quote
+
+from airflow_breeze.utils.console import console_print
+from airflow_breeze.utils.fork_ci_messages import STATUS_CONTEXT
+
+RATE_LIMIT_MARKERS = ("rate limit", "secondary rate", "abuse detection")
+MAX_ATTEMPTS = 4
+
+
+class GitHubError(RuntimeError):
+    """A GitHub API call failed for a reason other than a rate limit."""
+
+
+class GitHubClient:
+    def __init__(self, repo: str = "apache/airflow", *, dry_run: bool = False):
+        self.repo = repo
+        self.dry_run = dry_run
+        self.calls: Counter[str] = Counter()
+
+    def _gh(self, args: Sequence[str], body: dict[str, Any] | None, kind: str) 
-> Any:
+        payload = json.dumps(body) if body is not None else None
+        for attempt in range(MAX_ATTEMPTS):
+            self.calls[kind] += 1
+            result = subprocess.run(
+                ["gh", *args, *(["--input", "-"] if payload is not None else 
[])],
+                input=payload,
+                capture_output=True,
+                text=True,
+                check=False,
+            )
+            if result.returncode == 0:
+                return json.loads(result.stdout) if result.stdout.strip() else 
None
+            if any(marker in result.stderr.lower() for marker in 
RATE_LIMIT_MARKERS):
+                wait = 30 * 2**attempt

Review Comment:
   On the last attempt this still sleeps (240 s) before falling through to the 
raise. Also, `subprocess.run` above has no `timeout=`, so a hung `gh` holds the 
job until `timeout-minutes: 20`.



##########
.github/workflows/fork-ci-monitor.yml:
##########
@@ -0,0 +1,71 @@
+# 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.
+#
+---
+# Drives pull requests whose CI runs in contributor forks (AIP-120): labels, 
draft state, the
+# state comment, the `Fork CI / Tests (AMD)` commit status and closing. It is 
the only writer for
+# those pull requests and never checks out or runs pull request code. See
+# dev/breeze/doc/ci/08_fork_ci_for_external_contributors.md.
+name: "Fork CI monitor"
+on:  # yamllint disable-line rule:truthy
+  schedule:
+    - cron: '*/15 * * * *'
+  workflow_dispatch:
+    inputs:
+      dry-run:
+        description: "Compute the changes without applying them to pull 
requests"
+        type: boolean
+        default: false
+permissions:

Review Comment:
   `permissions:` sets every unlisted scope to `none`, including `actions`, but 
the reconciler lists `actions/runs` and `actions/runs/{id}/jobs` for the tamper 
check (`fork_ci_reconcile.py:890`, `:918`). Does that work without `actions: 
read`? Worth confirming with a dry run.



##########
.github/workflows/fork-ci-monitor.yml:
##########
@@ -0,0 +1,71 @@
+# 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.
+#
+---
+# Drives pull requests whose CI runs in contributor forks (AIP-120): labels, 
draft state, the
+# state comment, the `Fork CI / Tests (AMD)` commit status and closing. It is 
the only writer for
+# those pull requests and never checks out or runs pull request code. See
+# dev/breeze/doc/ci/08_fork_ci_for_external_contributors.md.
+name: "Fork CI monitor"
+on:  # yamllint disable-line rule:truthy
+  schedule:
+    - cron: '*/15 * * * *'
+  workflow_dispatch:
+    inputs:
+      dry-run:
+        description: "Compute the changes without applying them to pull 
requests"
+        type: boolean
+        default: false
+permissions:
+  contents: read
+  # Labels, draft state, comments and closing of gated pull requests.
+  pull-requests: write
+  # The `Fork CI / Tests (AMD)` commit status on pull request head commits.
+  statuses: write
+concurrency:
+  group: fork-ci-monitor
+  cancel-in-progress: false
+
+jobs:
+  reconcile:
+    name: "Reconcile pull requests gated to fork CI"
+    if: github.repository == 'apache/airflow'
+    # The breeze action frees disk space with sudo and docker, which 
ubuntu-slim does not provide.
+    runs-on: ubuntu-latest
+    timeout-minutes: 20
+    steps:
+      - name: "Checkout main"
+        uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1  # 
v7.0.1
+        with:
+          ref: main
+          persist-credentials: false
+      - name: "Install Breeze"
+        uses: ./.github/actions/breeze
+      - name: "Reconcile"
+        id: reconcile
+        env:
+          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          DRY_RUN: ${{ inputs.dry-run && 'true' || 'false' }}
+          RUN_URL: ${{ github.server_url }}/${{ github.repository 
}}/actions/runs/${{ github.run_id }}
+        run: breeze ci fork-ci-reconcile --output-file slack-message.json
+      - name: "Post to Slack"
+        if: steps.reconcile.outputs.has-alerts == 'true'

Review Comment:
   If the reconcile step fails before `has-alerts` is written (discovery query, 
rate limit), nothing reaches Slack and the failures stay silent. An extra `if: 
failure()` post would surface them.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to