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


##########
dev/breeze/src/airflow_breeze/utils/fork_ci_messages.py:
##########
@@ -0,0 +1,193 @@
+# 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.
+"""
+Texts the fork CI reconciler posts on pull requests (AIP-120).
+
+All wording lives here so it can be reviewed in one place. Texts must say that 
CI is free for the
+contributor and finite for the project, must not characterise the recipient, 
and must give the
+exact command to run.
+"""
+
+from __future__ import annotations
+
+import json
+import re
+from dataclasses import dataclass
+from datetime import datetime
+
+STATUS_CONTEXT = "Fork CI / Tests (AMD)"
+MARKER_PREFIX = "<!-- fork-ci-gate: "
+MARKER_PATTERN = re.compile(r"<!-- fork-ci-gate: (?P<payload>.*?) -->")
+DESIGN_URL = (
+    
"https://github.com/apache/airflow/blob/main/dev/breeze/doc/ci/08_fork_ci_for_external_contributors.md";
+)
+CONTRIBUTING_URL = (
+    
"https://github.com/apache/airflow/blob/main/contributing-docs/18_contribution_workflow.rst";
+)
+WHY_AUTOMATED_URL = (
+    
"https://github.com/apache/airflow/blob/main/contributing-docs/25_maintainer_pr_triage.md";
+    "#why-the-first-pass-is-automated"
+)
+
+FOOTER = (
+    "\n\n---\n"
+    "_This comment was posted automatically by the fork CI monitor from 
templates drafted with an "
+    "AI-assisted tool, so it may contain mistakes. An Apache Airflow 
maintainer, a real person, "
+    "will review the pull request once its fork CI is green. "
+    f"[Why the first pass is automated]({WHY_AUTOMATED_URL})._"
+)
+
+WHY_FORK_CI = (
+    "Pull requests from contributors run their CI in the contributor's own 
fork. GitHub Actions is free "
+    "and has no minute limit on public forks, while the CI capacity of the 
Apache Software Foundation "
+    "is shared by every ASF project and is finite."
+)
+
+
+@dataclass(frozen=True)
+class Marker:
+    state: str
+    sha: str
+    since: datetime
+
+
+def render_marker(marker: Marker) -> str:
+    payload = json.dumps(
+        {"state": marker.state, "sha": marker.sha, "since": 
marker.since.isoformat()}, sort_keys=True
+    )
+    return f"{MARKER_PREFIX}{payload} -->"
+
+
+def parse_marker(body: str) -> Marker | None:
+    """Return the marker of a comment, None when it has none; raise ValueError 
when it is malformed."""
+    match = MARKER_PATTERN.search(body)
+    if match is None:
+        return None
+    try:
+        data = json.loads(match.group("payload"))
+        return Marker(state=data["state"], sha=data["sha"], 
since=datetime.fromisoformat(data["since"]))
+    except (json.JSONDecodeError, KeyError, TypeError, ValueError) as e:
+        raise ValueError(f"Malformed fork CI marker: {match.group(0)!r}") from 
e
+
+
+def fork_actions_url(fork: str) -> str:
+    return f"https://github.com/{fork}/actions/workflows/ci-amd.yml";
+
+
+def _setup_instructions(fork: str) -> str:
+    return (
+        "To get CI for this pull request:\n\n"
+        "1. Enable the workflow in your fork (once per fork):\n\n"
+        f"   ```bash\n   gh workflow enable ci-amd.yml -R {fork}\n   ```\n\n"
+        "   If your fork's `main` is older than the workflow, sync it first 
with "
+        f"`gh repo sync {fork} --branch main`.\n"
+        "2. Push your branch again. Every push then runs CI in your fork.\n\n"
+        "`breeze ci audit` checks your whole local and fork setup and prints 
the command that fixes "
+        f"anything missing. See [the contribution 
workflow]({CONTRIBUTING_URL})."
+    )
+
+
+_STATE_TEXTS = {
+    "setup_required": "**Fork CI: setup required.** CI for this pull request 
has not run in your fork yet, "
+    "because the `Tests (AMD)` workflow is not enabled there.",
+    "awaiting_push": "**Fork CI: waiting for a push.** The workflow is enabled 
in your fork, but it has not "
+    "run for the latest commit of this pull request. Push the branch again to 
start it.",
+    "running": "**Fork CI: running.** CI for the latest commit is running in 
your fork.",
+    "failed": "**Fork CI: failed.** CI for the latest commit failed in your 
fork. Fix the failures and "
+    "push again; this pull request stays a draft until the run is green.",
+    "green": "**Fork CI: green.** CI for the latest commit passed in your 
fork. The pull request is "
+    "ready for maintainer review.",
+    "green_unverified": "**Fork CI: green, needs a maintainer.** CI for the 
latest commit passed in your "
+    "fork, but this pull request changes files that define what CI runs (such 
as `.github/`, "
+    "`dev/breeze/` or `scripts/ci/`), and the fork run used those changed 
files. A maintainer will "
+    "review the change and run project CI for it; the pull request stays a 
draft until then.",
+    "closed": "**Fork CI: closed after inactivity.** CI did not run in your 
fork in time; see the "
+    "comment below for how to continue.",
+    "gate_bypassed": "**Fork CI: closed.** Project CI ran for this pull 
request although its CI runs in "
+    "the contributor's fork; see the comment below.",
+}
+
+
+def state_comment(marker: Marker, *, fork: str, run_url: str | None) -> str:
+    parts = [render_marker(marker), _STATE_TEXTS[marker.state], WHY_FORK_CI]
+    if run_url:
+        parts.append(f"Latest run: {run_url}")
+    if marker.state == "setup_required":
+        parts.append(_setup_instructions(fork))
+    parts.append(f"How this works: [fork CI for external 
contributors]({DESIGN_URL}).")
+    return "\n\n".join(parts) + FOOTER
+
+
+def released_comment(marker: Marker) -> str:
+    return (
+        f"{render_marker(marker)}\n\n"
+        "**Project CI in use.** A maintainer added the `use project ci` label, 
so CI for this pull "
+        "request runs in apache/airflow. It starts on the next push or 
re-run." + FOOTER
+    )
+
+
+def nudge_comment(*, fork: str) -> str:
+    return (
+        f"The `Tests (AMD)` workflow is now enabled in {fork}. Push this 
branch again to start CI "
+        "for this pull request; the workflow only runs for pushes made after 
it was enabled." + FOOTER
+    )
+
+
+def inactivity_close_comment(*, days: int, fork: str) -> str:
+    return (
+        f"Closing this pull request because CI has not run in your fork for 
{days} days. {WHY_FORK_CI}\n\n"
+        "You are welcome to reopen it at any time after setting up fork 
CI:\n\n"
+        f"{_setup_instructions(fork)}\n\nReopening restarts the {days}-day 
period." + FOOTER
+    )
+
+
+def tamper_close_comment(*, run_url: str) -> str:
+    return (
+        "Closing this pull request because project CI ran for it in 
apache/airflow although it is "
+        f"set to run its CI in the contributor's fork: {run_url}\n\n"
+        "The pull request changes files under `.github/workflows/`. Such 
changes cannot be verified "
+        "in apache/airflow without a maintainer, because they can change which 
CI runs. If the "
+        "workflow change is wanted, a maintainer can reopen the pull request 
and add the "
+        "`use project ci` label." + FOOTER
+    )
+
+
+_STATUS_DESCRIPTIONS = {
+    "setup_required": "Fork CI not set up - see the pull request comment",
+    "awaiting_push": "Fork CI enabled, waiting for a push - see the pull 
request comment",
+    "running": "Fork CI running - see Details",
+    "failed": "Fork CI failed - see Details",
+    "green": "Fork CI passed - see Details",
+    "green_unverified": "Fork CI passed, but CI files changed - a maintainer 
must run project CI",

Review Comment:
   We might need a way to find `green_unverified` PRs from the PR list here? (a 
label, for example)
   
   If non-committers contribute to developer tools, IIUC, the new rule will 
categorize them into "green_unverified" and wait for committers' approval to 
run project CI. But this makes them look the same as a running PR in the list.
   
   If we could add a label for this, I think committers would also have a 
better "filter" to see which still need approval.
   



-- 
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