You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by "potiuk (via GitHub)" <gi...@apache.org> on 2026/10/15 22:44:08 UTC
[PR] Run fork pushes with pull-request selective checks [airflow]
potiuk opened a new pull request, #74820:
URL: https://github.com/apache/airflow/pull/74820
Stacked on #74819. Part 2 of 5 of the AIP-120 implementation (design: #73378); this PR's base is the previous branch, so the diff shows only this layer.
In a contributor fork, selective checks take the changed files from the cross-repository compare against apache/airflow (`main`, or the release branch the pushed branch name starts with) and select tests exactly as for a pull request, instead of running the full matrix for every push. Fork pushes are never committer builds. A change set at the compare API's 300-file cap runs everything. `04_selective_checks.md` is updated.
---
##### Was generative AI tooling used to co-author this PR?
- [X] Yes — Claude Code (Opus 5)
Generated-by: Claude Code (Opus 5) following [the guidelines](https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
--
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: commits-unsubscribe@airflow.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Run fork pushes with pull-request selective checks [airflow]
Posted by "shahar1 (via GitHub)" <gi...@apache.org>.
shahar1 commented on code in PR #74820:
URL: https://github.com/apache/airflow/pull/74820#discussion_r4301817592
##########
dev/breeze/src/airflow_breeze/utils/fork_compare.py:
##########
@@ -0,0 +1,79 @@
+# 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.
+"""Changed files of a push in a contributor fork, computed against apache/airflow (AIP-120)."""
+
+from __future__ import annotations
+
+import json
+import re
+from dataclasses import dataclass
+
+from airflow_breeze.global_constants import APACHE_AIRFLOW_GITHUB_REPOSITORY, GithubEvents
+from airflow_breeze.utils.github import run_gh_command
+
+# The compare API lists at most this many files; a longer change set is reported as truncated.
+COMPARE_FILE_CAP = 300
+
+RELEASE_BRANCH_PREFIX = re.compile(
+ r"^(v[0-9]+-[0-9]+-(?:test|stable)"
+ r"|providers-[a-z]+-?[a-z]*/v[0-9]+-[0-9]+"
+ r"|chart/v[0-9]+-[0-9]+x-(?:test|stable)"
+ r"|airflow-ctl/v[0-9]+-[0-9]+-(?:test|stable))"
+)
+
+
+@dataclass(frozen=True)
+class ForkChanges:
+ files: tuple[str, ...]
+ truncated: bool
+
+
+def is_fork_push(github_event: GithubEvents, github_repository: str) -> bool:
+ return github_event == GithubEvents.PUSH and github_repository != APACHE_AIRFLOW_GITHUB_REPOSITORY
+
+
+def get_compare_base(branch: str) -> str:
+ """Compare against main unless the branch is named after a release branch it targets."""
+ match = RELEASE_BRANCH_PREFIX.match(branch)
+ return match.group(1) if match else "main"
+
+
+def get_fork_changes(*, fork_owner: str, sha: str, base: str) -> ForkChanges:
+ """
+ Three-dot compare of the pushed commit against the upstream base branch.
+
+ Three-dot semantics give the same file list a pull request against upstream would show,
+ so a fork whose ``main`` is behind upstream does not inflate the change set.
+ """
+ result = run_gh_command(
+ [
+ "gh",
+ "api",
+ f"repos/{APACHE_AIRFLOW_GITHUB_REPOSITORY}/compare/{base}...{fork_owner}:{sha}",
+ "--jq",
+ "[.files[].filename]",
Review Comment:
`[.files[].filename]` drops `previous_filename`, so a rename out of an area does not select the area it left, unlike the PR path (`git diff-tree` lists both). `[.files[] | .filename, (.previous_filename // empty)]` would keep both.
##########
dev/breeze/src/airflow_breeze/utils/fork_compare.py:
##########
@@ -0,0 +1,79 @@
+# 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.
+"""Changed files of a push in a contributor fork, computed against apache/airflow (AIP-120)."""
+
+from __future__ import annotations
+
+import json
+import re
+from dataclasses import dataclass
+
+from airflow_breeze.global_constants import APACHE_AIRFLOW_GITHUB_REPOSITORY, GithubEvents
+from airflow_breeze.utils.github import run_gh_command
+
+# The compare API lists at most this many files; a longer change set is reported as truncated.
+COMPARE_FILE_CAP = 300
+
+RELEASE_BRANCH_PREFIX = re.compile(
+ r"^(v[0-9]+-[0-9]+-(?:test|stable)"
+ r"|providers-[a-z]+-?[a-z]*/v[0-9]+-[0-9]+"
+ r"|chart/v[0-9]+-[0-9]+x-(?:test|stable)"
+ r"|airflow-ctl/v[0-9]+-[0-9]+-(?:test|stable))"
+)
+
+
+@dataclass(frozen=True)
+class ForkChanges:
+ files: tuple[str, ...]
+ truncated: bool
+
+
+def is_fork_push(github_event: GithubEvents, github_repository: str) -> bool:
+ return github_event == GithubEvents.PUSH and github_repository != APACHE_AIRFLOW_GITHUB_REPOSITORY
+
+
+def get_compare_base(branch: str) -> str:
+ """Compare against main unless the branch is named after a release branch it targets."""
+ match = RELEASE_BRANCH_PREFIX.match(branch)
+ return match.group(1) if match else "main"
+
+
+def get_fork_changes(*, fork_owner: str, sha: str, base: str) -> ForkChanges:
+ """
+ Three-dot compare of the pushed commit against the upstream base branch.
+
+ Three-dot semantics give the same file list a pull request against upstream would show,
+ so a fork whose ``main`` is behind upstream does not inflate the change set.
+ """
+ result = run_gh_command(
+ [
+ "gh",
+ "api",
+ f"repos/{APACHE_AIRFLOW_GITHUB_REPOSITORY}/compare/{base}...{fork_owner}:{sha}",
+ "--jq",
+ "[.files[].filename]",
+ ],
+ capture_output=True,
+ text=True,
+ )
+ if result.returncode != 0:
+ raise RuntimeError(
+ f"Could not compare {fork_owner}:{sha} with {APACHE_AIRFLOW_GITHUB_REPOSITORY}:{base}: "
+ f"{result.stderr.strip()}"
+ )
+ files = tuple(json.loads(result.stdout or "[]"))
+ return ForkChanges(files=files, truncated=len(files) >= COMPARE_FILE_CAP)
Review Comment:
`>=` also flags exactly 300 files, while `04_selective_checks.md` says the run covers everything only when the change set *exceeds* the cap.
##########
dev/breeze/src/airflow_breeze/utils/selective_checks.py:
##########
@@ -2513,7 +2543,7 @@ def common_compat_changed_without_next_version(self) -> bool:
Check if common.compat provider changed and other providers changed don't have '# use next version'
comment for their common-compat dependency.
"""
- if self._github_event != GithubEvents.PULL_REQUEST:
+ if not self._is_pull_request_like():
Review Comment:
Same here: no test covers a fork push in `common_compat_changed_without_next_version`.
##########
dev/breeze/src/airflow_breeze/utils/selective_checks.py:
##########
@@ -2292,7 +2322,7 @@ def provider_dependency_bump(self) -> bool:
FileGroupForCi.ALL_PYPROJECT_TOML_FILES,
CI_FILE_GROUP_MATCHES,
)
- if not pyproject_files or not self._github_event == GithubEvents.PULL_REQUEST:
+ if not pyproject_files or not self._is_pull_request_like():
Review Comment:
No test exercises a fork push reaching `provider_dependency_bump`; reverting `_is_pull_request_like()` here passes every test in this PR (AGENTS.md: "Every changed or added behaviour must have a test; every test must fail without the PR's change.").
##########
dev/breeze/src/airflow_breeze/utils/fork_compare.py:
##########
@@ -0,0 +1,79 @@
+# 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.
+"""Changed files of a push in a contributor fork, computed against apache/airflow (AIP-120)."""
+
+from __future__ import annotations
+
+import json
+import re
+from dataclasses import dataclass
+
+from airflow_breeze.global_constants import APACHE_AIRFLOW_GITHUB_REPOSITORY, GithubEvents
+from airflow_breeze.utils.github import run_gh_command
+
+# The compare API lists at most this many files; a longer change set is reported as truncated.
+COMPARE_FILE_CAP = 300
+
+RELEASE_BRANCH_PREFIX = re.compile(
+ r"^(v[0-9]+-[0-9]+-(?:test|stable)"
+ r"|providers-[a-z]+-?[a-z]*/v[0-9]+-[0-9]+"
+ r"|chart/v[0-9]+-[0-9]+x-(?:test|stable)"
+ r"|airflow-ctl/v[0-9]+-[0-9]+-(?:test|stable))"
+)
+
+
+@dataclass(frozen=True)
+class ForkChanges:
+ files: tuple[str, ...]
+ truncated: bool
+
+
+def is_fork_push(github_event: GithubEvents, github_repository: str) -> bool:
+ return github_event == GithubEvents.PUSH and github_repository != APACHE_AIRFLOW_GITHUB_REPOSITORY
+
+
+def get_compare_base(branch: str) -> str:
Review Comment:
The compare base comes from a branch-name prefix only: `backport-<sha>-v3-1-test` is compared with `main` (hits the 300-file cap and runs everything), and a main-based `v3-1-test-foo` is compared with `v3-1-test`. The command already receives `default_branch` (`ci_commands.py:296` passes only `ref_name`); could that decide the base instead?
##########
dev/breeze/src/airflow_breeze/utils/fork_compare.py:
##########
@@ -0,0 +1,79 @@
+# 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.
+"""Changed files of a push in a contributor fork, computed against apache/airflow (AIP-120)."""
+
+from __future__ import annotations
+
+import json
+import re
+from dataclasses import dataclass
+
+from airflow_breeze.global_constants import APACHE_AIRFLOW_GITHUB_REPOSITORY, GithubEvents
+from airflow_breeze.utils.github import run_gh_command
+
+# The compare API lists at most this many files; a longer change set is reported as truncated.
+COMPARE_FILE_CAP = 300
+
+RELEASE_BRANCH_PREFIX = re.compile(
+ r"^(v[0-9]+-[0-9]+-(?:test|stable)"
+ r"|providers-[a-z]+-?[a-z]*/v[0-9]+-[0-9]+"
+ r"|chart/v[0-9]+-[0-9]+x-(?:test|stable)"
+ r"|airflow-ctl/v[0-9]+-[0-9]+-(?:test|stable))"
+)
+
+
+@dataclass(frozen=True)
+class ForkChanges:
+ files: tuple[str, ...]
+ truncated: bool
+
+
+def is_fork_push(github_event: GithubEvents, github_repository: str) -> bool:
+ return github_event == GithubEvents.PUSH and github_repository != APACHE_AIRFLOW_GITHUB_REPOSITORY
+
+
+def get_compare_base(branch: str) -> str:
+ """Compare against main unless the branch is named after a release branch it targets."""
+ match = RELEASE_BRANCH_PREFIX.match(branch)
+ return match.group(1) if match else "main"
+
+
+def get_fork_changes(*, fork_owner: str, sha: str, base: str) -> ForkChanges:
+ """
+ Three-dot compare of the pushed commit against the upstream base branch.
+
+ Three-dot semantics give the same file list a pull request against upstream would show,
+ so a fork whose ``main`` is behind upstream does not inflate the change set.
+ """
+ result = run_gh_command(
+ [
+ "gh",
+ "api",
+ f"repos/{APACHE_AIRFLOW_GITHUB_REPOSITORY}/compare/{base}...{fork_owner}:{sha}",
+ "--jq",
+ "[.files[].filename]",
+ ],
+ capture_output=True,
+ text=True,
+ )
+ if result.returncode != 0:
Review Comment:
Any compare failure (5xx, rate limit, a repository outside the apache/airflow network) fails the whole fork run at `build-info`. Falling back to `truncated=True` (run everything) would degrade instead of failing. If failing is intended, a line in the doc would help.
--
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: commits-unsubscribe@airflow.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Run fork pushes with pull-request selective checks [airflow]
Posted by "Andrushika (via GitHub)" <gi...@apache.org>.
Andrushika commented on code in PR #74820:
URL: https://github.com/apache/airflow/pull/74820#discussion_r4311746074
##########
dev/breeze/src/airflow_breeze/commands/ci_commands.py:
##########
@@ -286,7 +287,18 @@ def selective_check(
github_context = github_context_input.read()
github_context_dict = json.loads(github_context) if github_context else {}
github_event = GithubEvents(github_event_name)
- if commit_ref is not None:
+ fork_push = is_fork_push(github_event, github_repository)
Review Comment:
We might need to get the PR labels for a fork push here. `workflow_info` only reads labels on `pull_request`, so in the fork `full tests needed` and similar labels do nothing. Also `allow provider dependency bump` and `skip common compat check` can't let the two checks this PR enables pass, so such a PR stays red until someone adds `use project ci`.
---
Drafted-by: Claude Code (Opus 5.5); reviewed by @Andrushika before posting
--
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: commits-unsubscribe@airflow.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Run fork pushes with pull-request selective checks [airflow]
Posted by "potiuk (via GitHub)" <gi...@apache.org>.
potiuk commented on code in PR #74820:
URL: https://github.com/apache/airflow/pull/74820#discussion_r4330953723
##########
dev/breeze/src/airflow_breeze/commands/ci_commands.py:
##########
@@ -286,7 +287,18 @@ def selective_check(
github_context = github_context_input.read()
github_context_dict = json.loads(github_context) if github_context else {}
github_event = GithubEvents(github_event_name)
- if commit_ref is not None:
+ fork_push = is_fork_push(github_event, github_repository)
Review Comment:
Fixed. A fork push now reads the labels of the open apache/airflow pull request for `<fork-owner>:<branch>`, so `full tests needed`, `allow provider dependency bump`, `skip common compat check` and the rest work in fork CI. A label added later takes effect on the next fork run.
---
Drafted-by: Claude Code (Opus 5); reviewed by @potiuk before posting
##########
dev/breeze/src/airflow_breeze/utils/selective_checks.py:
##########
@@ -2292,7 +2322,7 @@ def provider_dependency_bump(self) -> bool:
FileGroupForCi.ALL_PYPROJECT_TOML_FILES,
CI_FILE_GROUP_MATCHES,
)
- if not pyproject_files or not self._github_event == GithubEvents.PULL_REQUEST:
+ if not pyproject_files or not self._is_pull_request_like():
Review Comment:
Fixed differently than skipping, so the check still runs for gated PRs. On a fork push, the previous version of each `pyproject.toml` now comes from the compare's merge base in apache/airflow, not from `commit_ref^`. A release manager's bump merged in from `main` no longer counts as the contributor's. The root `pyproject.toml` check uses the same path. A file missing at the merge base counts as new. Any other read failure fails selective checks rather than letting the check pass unseen. Test: `test_provider_dependency_bump_on_a_fork_push_compares_with_the_merge_base`.
---
Drafted-by: Claude Code (Opus 5); reviewed by @potiuk before posting
--
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: commits-unsubscribe@airflow.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Run fork pushes with pull-request selective checks [airflow]
Posted by "potiuk (via GitHub)" <gi...@apache.org>.
potiuk commented on code in PR #74820:
URL: https://github.com/apache/airflow/pull/74820#discussion_r4304445980
##########
dev/breeze/src/airflow_breeze/utils/fork_compare.py:
##########
@@ -0,0 +1,79 @@
+# 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.
+"""Changed files of a push in a contributor fork, computed against apache/airflow (AIP-120)."""
+
+from __future__ import annotations
+
+import json
+import re
+from dataclasses import dataclass
+
+from airflow_breeze.global_constants import APACHE_AIRFLOW_GITHUB_REPOSITORY, GithubEvents
+from airflow_breeze.utils.github import run_gh_command
+
+# The compare API lists at most this many files; a longer change set is reported as truncated.
+COMPARE_FILE_CAP = 300
+
+RELEASE_BRANCH_PREFIX = re.compile(
+ r"^(v[0-9]+-[0-9]+-(?:test|stable)"
+ r"|providers-[a-z]+-?[a-z]*/v[0-9]+-[0-9]+"
+ r"|chart/v[0-9]+-[0-9]+x-(?:test|stable)"
+ r"|airflow-ctl/v[0-9]+-[0-9]+-(?:test|stable))"
+)
+
+
+@dataclass(frozen=True)
+class ForkChanges:
+ files: tuple[str, ...]
+ truncated: bool
+
+
+def is_fork_push(github_event: GithubEvents, github_repository: str) -> bool:
+ return github_event == GithubEvents.PUSH and github_repository != APACHE_AIRFLOW_GITHUB_REPOSITORY
+
+
+def get_compare_base(branch: str) -> str:
Review Comment:
Agreed, thanks. The compare base is now the `default_branch` the command already receives, i.e. breeze's default branch at the pushed commit, so `backport-<sha>-v3-1-test` compares with `v3-1-test` and a main-based `v3-1-test-foo` with `main`. The branch-name guess is gone. Fixed in 62a84c89c0.
---
Drafted-by: Claude Code (Opus 5); reviewed by @potiuk before posting
##########
dev/breeze/src/airflow_breeze/utils/fork_compare.py:
##########
@@ -0,0 +1,79 @@
+# 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.
+"""Changed files of a push in a contributor fork, computed against apache/airflow (AIP-120)."""
+
+from __future__ import annotations
+
+import json
+import re
+from dataclasses import dataclass
+
+from airflow_breeze.global_constants import APACHE_AIRFLOW_GITHUB_REPOSITORY, GithubEvents
+from airflow_breeze.utils.github import run_gh_command
+
+# The compare API lists at most this many files; a longer change set is reported as truncated.
+COMPARE_FILE_CAP = 300
+
+RELEASE_BRANCH_PREFIX = re.compile(
+ r"^(v[0-9]+-[0-9]+-(?:test|stable)"
+ r"|providers-[a-z]+-?[a-z]*/v[0-9]+-[0-9]+"
+ r"|chart/v[0-9]+-[0-9]+x-(?:test|stable)"
+ r"|airflow-ctl/v[0-9]+-[0-9]+-(?:test|stable))"
+)
+
+
+@dataclass(frozen=True)
+class ForkChanges:
+ files: tuple[str, ...]
+ truncated: bool
+
+
+def is_fork_push(github_event: GithubEvents, github_repository: str) -> bool:
+ return github_event == GithubEvents.PUSH and github_repository != APACHE_AIRFLOW_GITHUB_REPOSITORY
+
+
+def get_compare_base(branch: str) -> str:
+ """Compare against main unless the branch is named after a release branch it targets."""
+ match = RELEASE_BRANCH_PREFIX.match(branch)
+ return match.group(1) if match else "main"
+
+
+def get_fork_changes(*, fork_owner: str, sha: str, base: str) -> ForkChanges:
+ """
+ Three-dot compare of the pushed commit against the upstream base branch.
+
+ Three-dot semantics give the same file list a pull request against upstream would show,
+ so a fork whose ``main`` is behind upstream does not inflate the change set.
+ """
+ result = run_gh_command(
+ [
+ "gh",
+ "api",
+ f"repos/{APACHE_AIRFLOW_GITHUB_REPOSITORY}/compare/{base}...{fork_owner}:{sha}",
+ "--jq",
+ "[.files[].filename]",
Review Comment:
Fixed in 62a84c89c0. The jq keeps `previous_filename`, and the cap counts files rather than paths, so a rename counts under both paths like `git diff-tree`. Test added.
---
Drafted-by: Claude Code (Opus 5); reviewed by @potiuk before posting
##########
dev/breeze/src/airflow_breeze/utils/fork_compare.py:
##########
@@ -0,0 +1,79 @@
+# 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.
+"""Changed files of a push in a contributor fork, computed against apache/airflow (AIP-120)."""
+
+from __future__ import annotations
+
+import json
+import re
+from dataclasses import dataclass
+
+from airflow_breeze.global_constants import APACHE_AIRFLOW_GITHUB_REPOSITORY, GithubEvents
+from airflow_breeze.utils.github import run_gh_command
+
+# The compare API lists at most this many files; a longer change set is reported as truncated.
+COMPARE_FILE_CAP = 300
+
+RELEASE_BRANCH_PREFIX = re.compile(
+ r"^(v[0-9]+-[0-9]+-(?:test|stable)"
+ r"|providers-[a-z]+-?[a-z]*/v[0-9]+-[0-9]+"
+ r"|chart/v[0-9]+-[0-9]+x-(?:test|stable)"
+ r"|airflow-ctl/v[0-9]+-[0-9]+-(?:test|stable))"
+)
+
+
+@dataclass(frozen=True)
+class ForkChanges:
+ files: tuple[str, ...]
+ truncated: bool
+
+
+def is_fork_push(github_event: GithubEvents, github_repository: str) -> bool:
+ return github_event == GithubEvents.PUSH and github_repository != APACHE_AIRFLOW_GITHUB_REPOSITORY
+
+
+def get_compare_base(branch: str) -> str:
+ """Compare against main unless the branch is named after a release branch it targets."""
+ match = RELEASE_BRANCH_PREFIX.match(branch)
+ return match.group(1) if match else "main"
+
+
+def get_fork_changes(*, fork_owner: str, sha: str, base: str) -> ForkChanges:
+ """
+ Three-dot compare of the pushed commit against the upstream base branch.
+
+ Three-dot semantics give the same file list a pull request against upstream would show,
+ so a fork whose ``main`` is behind upstream does not inflate the change set.
+ """
+ result = run_gh_command(
+ [
+ "gh",
+ "api",
+ f"repos/{APACHE_AIRFLOW_GITHUB_REPOSITORY}/compare/{base}...{fork_owner}:{sha}",
+ "--jq",
+ "[.files[].filename]",
+ ],
+ capture_output=True,
+ text=True,
+ )
+ if result.returncode != 0:
Review Comment:
Agreed that degrading beats failing. A failed compare now logs a warning and returns `truncated=True`, so the run covers everything. The selective-checks and design docs say so. Fixed in 62a84c89c0.
---
Drafted-by: Claude Code (Opus 5); reviewed by @potiuk before posting
##########
dev/breeze/src/airflow_breeze/utils/fork_compare.py:
##########
@@ -0,0 +1,79 @@
+# 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.
+"""Changed files of a push in a contributor fork, computed against apache/airflow (AIP-120)."""
+
+from __future__ import annotations
+
+import json
+import re
+from dataclasses import dataclass
+
+from airflow_breeze.global_constants import APACHE_AIRFLOW_GITHUB_REPOSITORY, GithubEvents
+from airflow_breeze.utils.github import run_gh_command
+
+# The compare API lists at most this many files; a longer change set is reported as truncated.
+COMPARE_FILE_CAP = 300
+
+RELEASE_BRANCH_PREFIX = re.compile(
+ r"^(v[0-9]+-[0-9]+-(?:test|stable)"
+ r"|providers-[a-z]+-?[a-z]*/v[0-9]+-[0-9]+"
+ r"|chart/v[0-9]+-[0-9]+x-(?:test|stable)"
+ r"|airflow-ctl/v[0-9]+-[0-9]+-(?:test|stable))"
+)
+
+
+@dataclass(frozen=True)
+class ForkChanges:
+ files: tuple[str, ...]
+ truncated: bool
+
+
+def is_fork_push(github_event: GithubEvents, github_repository: str) -> bool:
+ return github_event == GithubEvents.PUSH and github_repository != APACHE_AIRFLOW_GITHUB_REPOSITORY
+
+
+def get_compare_base(branch: str) -> str:
+ """Compare against main unless the branch is named after a release branch it targets."""
+ match = RELEASE_BRANCH_PREFIX.match(branch)
+ return match.group(1) if match else "main"
+
+
+def get_fork_changes(*, fork_owner: str, sha: str, base: str) -> ForkChanges:
+ """
+ Three-dot compare of the pushed commit against the upstream base branch.
+
+ Three-dot semantics give the same file list a pull request against upstream would show,
+ so a fork whose ``main`` is behind upstream does not inflate the change set.
+ """
+ result = run_gh_command(
+ [
+ "gh",
+ "api",
+ f"repos/{APACHE_AIRFLOW_GITHUB_REPOSITORY}/compare/{base}...{fork_owner}:{sha}",
+ "--jq",
+ "[.files[].filename]",
+ ],
+ capture_output=True,
+ text=True,
+ )
+ if result.returncode != 0:
+ raise RuntimeError(
+ f"Could not compare {fork_owner}:{sha} with {APACHE_AIRFLOW_GITHUB_REPOSITORY}:{base}: "
+ f"{result.stderr.strip()}"
+ )
+ files = tuple(json.loads(result.stdout or "[]"))
+ return ForkChanges(files=files, truncated=len(files) >= COMPARE_FILE_CAP)
Review Comment:
The `>=` is intended: a list of exactly 300 may be the API cutting it off, so it can't be trusted to be complete. I fixed the wording instead: `04_selective_checks.md` now says "reaches" the cap. 62a84c89c0.
---
Drafted-by: Claude Code (Opus 5); reviewed by @potiuk before posting
##########
dev/breeze/src/airflow_breeze/utils/selective_checks.py:
##########
@@ -2292,7 +2322,7 @@ def provider_dependency_bump(self) -> bool:
FileGroupForCi.ALL_PYPROJECT_TOML_FILES,
CI_FILE_GROUP_MATCHES,
)
- if not pyproject_files or not self._github_event == GithubEvents.PULL_REQUEST:
+ if not pyproject_files or not self._is_pull_request_like():
Review Comment:
Thanks. Added `test_provider_dependency_bump_check_fails_on_a_fork_push`, which fails without `_is_pull_request_like()`. 62a84c89c0.
---
Drafted-by: Claude Code (Opus 5); reviewed by @potiuk before posting
##########
dev/breeze/src/airflow_breeze/utils/selective_checks.py:
##########
@@ -2513,7 +2543,7 @@ def common_compat_changed_without_next_version(self) -> bool:
Check if common.compat provider changed and other providers changed don't have '# use next version'
comment for their common-compat dependency.
"""
- if self._github_event != GithubEvents.PULL_REQUEST:
+ if not self._is_pull_request_like():
Review Comment:
Added `test_common_compat_changed_without_next_version_fails_on_a_fork_push` too. 62a84c89c0.
---
Drafted-by: Claude Code (Opus 5); reviewed by @potiuk before posting
--
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: commits-unsubscribe@airflow.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Run fork pushes with pull-request selective checks [airflow]
Posted by "Andrushika (via GitHub)" <gi...@apache.org>.
Andrushika commented on code in PR #74820:
URL: https://github.com/apache/airflow/pull/74820#discussion_r4312033214
##########
dev/breeze/src/airflow_breeze/utils/selective_checks.py:
##########
@@ -2292,7 +2322,7 @@ def provider_dependency_bump(self) -> bool:
FileGroupForCi.ALL_PYPROJECT_TOML_FILES,
CI_FILE_GROUP_MATCHES,
)
- if not pyproject_files or not self._github_event == GithubEvents.PULL_REQUEST:
+ if not pyproject_files or not self._is_pull_request_like():
Review Comment:
We might need to skip this check on fork pushes. It reads the old file from `commit_ref^`, and when a contributor merges upstream `main` into their branch, that parent is their own old tip. So a release manager bump from `main` in the same `pyproject.toml` counts as their change, the run fails, and the label that bypasses it can't be read in a fork.
---
Drafted-by: Claude Code (Opus 5.5); reviewed by @Andrushika before posting
--
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: commits-unsubscribe@airflow.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Run fork pushes with pull-request selective checks [airflow]
Posted by "Andrushika (via GitHub)" <gi...@apache.org>.
Andrushika commented on code in PR #74820:
URL: https://github.com/apache/airflow/pull/74820#discussion_r4311746074
##########
dev/breeze/src/airflow_breeze/commands/ci_commands.py:
##########
@@ -286,7 +287,18 @@ def selective_check(
github_context = github_context_input.read()
github_context_dict = json.loads(github_context) if github_context else {}
github_event = GithubEvents(github_event_name)
- if commit_ref is not None:
+ fork_push = is_fork_push(github_event, github_repository)
Review Comment:
We might need to get the PR labels for a fork push here. `workflow_info` only reads labels on `pull_request`, so in the fork `full tests needed` and similar labels do nothing.
I think we also need to take some other tags into consideration, like `allow provider dependency bump` and `skip common compat check` can't let the two checks this PR enables pass, so such a PR stays red until someone adds `use project ci`.
---
Drafted-by: Claude Code (Opus 5.5); reviewed by @Andrushika before posting
--
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: commits-unsubscribe@airflow.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Run fork pushes with pull-request selective checks [airflow]
Posted by "shahar1 (via GitHub)" <gi...@apache.org>.
shahar1 commented on code in PR #74820:
URL: https://github.com/apache/airflow/pull/74820#discussion_r4367560926
##########
dev/breeze/tests/test_selective_check_command.py:
##########
@@ -0,0 +1,80 @@
+# 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.
+from __future__ import annotations
+
+import json
+from unittest import mock
+
+from click.testing import CliRunner
+
+from airflow_breeze.commands.ci_commands import selective_check
+from airflow_breeze.utils.fork_compare import ForkChanges
+
+
+def _invoke(repository: str, event: str = "push"):
+ return CliRunner().invoke(
+ selective_check,
+ [
+ "--commit-ref",
+ "abc123",
+ "--github-event-name",
+ event,
+ "--github-repository",
+ repository,
+ "--github-context",
+ json.dumps({"ref_name": "backport-abc-v3-1-test"}),
+ "--default-branch",
+ "v3-1-test",
+ ],
+ )
+
+
+@mock.patch("airflow_breeze.commands.ci_commands.get_fork_pull_request_labels", autospec=True)
+@mock.patch("airflow_breeze.commands.ci_commands.get_changed_files", autospec=True)
+@mock.patch("airflow_breeze.commands.ci_commands.get_fork_changes", autospec=True)
+def test_fork_push_takes_changed_files_from_compare(mock_fork_changes, mock_changed_files, mock_labels):
+ mock_fork_changes.return_value = ForkChanges(files=("README.md",), truncated=True, merge_base="base-sha")
+ mock_labels.return_value = ("full tests needed",)
+
+ with mock.patch("airflow_breeze.utils.selective_checks.SelectiveChecks", autospec=True) as mock_sc:
Review Comment:
nit: `dev/breeze/tests/test_ci_commands.py` already tests the `selective_check` command, with an `_invoke_selective_check` helper and `@patch("airflow_breeze.utils.selective_checks.SelectiveChecks", autospec=True)` as a decorator. These two tests could live there in the decorator form instead of a second CLI harness in a module that does not mirror the source.
> - Prefer `@mock.patch` decorators over `with mock.patch(...)` context managers for patching.
> - Test location mirrors source: `airflow/cli/cli_parser.py` → `tests/cli/test_cli_parser.py`.
(AGENTS.md, Testing Standards)
##########
dev/breeze/src/airflow_breeze/utils/fork_compare.py:
##########
@@ -0,0 +1,147 @@
+# 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.
+"""Changed files of a push in a contributor fork, computed against apache/airflow (AIP-120)."""
+
+from __future__ import annotations
+
+import json
+from dataclasses import dataclass
+
+from airflow_breeze.global_constants import APACHE_AIRFLOW_GITHUB_REPOSITORY, GithubEvents
+from airflow_breeze.utils.console import console_print
+from airflow_breeze.utils.github import run_gh_command
+
+# The compare API lists at most this many files: a list that reaches it may be missing some, so
+# the change set is treated as truncated.
+COMPARE_FILE_CAP = 300
+
+
+@dataclass(frozen=True)
+class ForkChanges:
+ files: tuple[str, ...]
+ truncated: bool
+ merge_base: str | None = None
+
+
+def is_fork_push(github_event: GithubEvents, github_repository: str) -> bool:
+ return github_event == GithubEvents.PUSH and github_repository != APACHE_AIRFLOW_GITHUB_REPOSITORY
+
+
+def get_fork_changes(*, fork_owner: str, sha: str, base: str) -> ForkChanges:
+ """
+ Three-dot compare of the pushed commit against the upstream base branch.
+
+ Three-dot semantics give the same file list a pull request against upstream would show,
+ so a fork whose ``main`` is behind upstream does not inflate the change set. ``base`` is the
+ branch line the pushed code belongs to (breeze's default branch at that commit), which a
+ branch name cannot be trusted to tell. A renamed file counts under both paths, as in
+ ``git diff``. When the compare fails the change set is reported as truncated, so the run
+ covers everything instead of failing.
+
+ ``merge_base`` is the upstream commit the changes are measured from. Checks that compare a
+ file with its previous version use it instead of the pushed commit's parent, which after a
+ merge of upstream ``main`` into the branch is the contributor's own previous tip.
+ """
+ result = run_gh_command(
+ [
+ "gh",
+ "api",
+ f"repos/{APACHE_AIRFLOW_GITHUB_REPOSITORY}/compare/{base}...{fork_owner}:{sha}",
+ "--jq",
+ "{files: [.files[] | [.filename, (.previous_filename // empty)]], "
+ "merge_base: .merge_base_commit.sha}",
+ ],
+ capture_output=True,
+ text=True,
+ )
+ if result.returncode != 0:
+ console_print(
+ f"[warning]Could not compare {fork_owner}:{sha} with {APACHE_AIRFLOW_GITHUB_REPOSITORY}:{base}, "
+ f"so everything runs: {result.stderr.strip()}[/]"
+ )
+ return ForkChanges(files=(), truncated=True)
+ compare = json.loads(result.stdout or "{}")
+ entries = compare.get("files", [])
+ files = tuple(path for entry in entries for path in entry)
+ return ForkChanges(
+ files=files, truncated=len(entries) >= COMPARE_FILE_CAP, merge_base=compare.get("merge_base")
+ )
+
+
+def get_fork_pull_request_labels(*, fork_owner: str, branch: str) -> tuple[str, ...]:
+ """
+ Labels of the open apache/airflow pull request from ``fork_owner:branch``.
+
+ A fork push carries no pull request, so labels such as ``full tests needed`` or
+ ``allow provider dependency bump`` are read here. A label added later takes effect on the
+ next fork run. No pull request, or a failed call, means no labels.
+ """
+ result = run_gh_command(
+ [
+ "gh",
+ "api",
+ "--method",
+ "GET",
+ f"repos/{APACHE_AIRFLOW_GITHUB_REPOSITORY}/pulls",
+ "-f",
+ f"head={fork_owner}:{branch}",
+ "-f",
+ "state=open",
+ "--jq",
+ "[.[0].labels[]?.name]",
Review Comment:
nit: GitHub allows several open pull requests from the same head branch when they target different bases (say `main` and a `vX-Y-test`), and `.[0]` then takes the labels of whichever the API lists first. Passing `-f base=<default_branch>` as well would pick the pull request that matches the compare base. Rare enough to leave if you'd rather not add the parameter.
--
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: commits-unsubscribe@airflow.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Run fork pushes with pull-request selective checks [airflow]
Posted by "potiuk (via GitHub)" <gi...@apache.org>.
potiuk merged PR #74820:
URL: https://github.com/apache/airflow/pull/74820
--
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: commits-unsubscribe@airflow.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Run fork pushes with pull-request selective checks [airflow]
Posted by "potiuk (via GitHub)" <gi...@apache.org>.
potiuk commented on code in PR #74820:
URL: https://github.com/apache/airflow/pull/74820#discussion_r4367843783
##########
dev/breeze/tests/test_selective_check_command.py:
##########
@@ -0,0 +1,80 @@
+# 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.
+from __future__ import annotations
+
+import json
+from unittest import mock
+
+from click.testing import CliRunner
+
+from airflow_breeze.commands.ci_commands import selective_check
+from airflow_breeze.utils.fork_compare import ForkChanges
+
+
+def _invoke(repository: str, event: str = "push"):
+ return CliRunner().invoke(
+ selective_check,
+ [
+ "--commit-ref",
+ "abc123",
+ "--github-event-name",
+ event,
+ "--github-repository",
+ repository,
+ "--github-context",
+ json.dumps({"ref_name": "backport-abc-v3-1-test"}),
+ "--default-branch",
+ "v3-1-test",
+ ],
+ )
+
+
+@mock.patch("airflow_breeze.commands.ci_commands.get_fork_pull_request_labels", autospec=True)
+@mock.patch("airflow_breeze.commands.ci_commands.get_changed_files", autospec=True)
+@mock.patch("airflow_breeze.commands.ci_commands.get_fork_changes", autospec=True)
+def test_fork_push_takes_changed_files_from_compare(mock_fork_changes, mock_changed_files, mock_labels):
+ mock_fork_changes.return_value = ForkChanges(files=("README.md",), truncated=True, merge_base="base-sha")
+ mock_labels.return_value = ("full tests needed",)
+
+ with mock.patch("airflow_breeze.utils.selective_checks.SelectiveChecks", autospec=True) as mock_sc:
Review Comment:
Moved both tests into `test_ci_commands.py`. They reuse `_invoke_selective_check` and patch `SelectiveChecks` with a decorator.
---
Drafted-by: Claude Code (Opus 5); reviewed by @potiuk before posting
##########
dev/breeze/src/airflow_breeze/utils/fork_compare.py:
##########
@@ -0,0 +1,147 @@
+# 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.
+"""Changed files of a push in a contributor fork, computed against apache/airflow (AIP-120)."""
+
+from __future__ import annotations
+
+import json
+from dataclasses import dataclass
+
+from airflow_breeze.global_constants import APACHE_AIRFLOW_GITHUB_REPOSITORY, GithubEvents
+from airflow_breeze.utils.console import console_print
+from airflow_breeze.utils.github import run_gh_command
+
+# The compare API lists at most this many files: a list that reaches it may be missing some, so
+# the change set is treated as truncated.
+COMPARE_FILE_CAP = 300
+
+
+@dataclass(frozen=True)
+class ForkChanges:
+ files: tuple[str, ...]
+ truncated: bool
+ merge_base: str | None = None
+
+
+def is_fork_push(github_event: GithubEvents, github_repository: str) -> bool:
+ return github_event == GithubEvents.PUSH and github_repository != APACHE_AIRFLOW_GITHUB_REPOSITORY
+
+
+def get_fork_changes(*, fork_owner: str, sha: str, base: str) -> ForkChanges:
+ """
+ Three-dot compare of the pushed commit against the upstream base branch.
+
+ Three-dot semantics give the same file list a pull request against upstream would show,
+ so a fork whose ``main`` is behind upstream does not inflate the change set. ``base`` is the
+ branch line the pushed code belongs to (breeze's default branch at that commit), which a
+ branch name cannot be trusted to tell. A renamed file counts under both paths, as in
+ ``git diff``. When the compare fails the change set is reported as truncated, so the run
+ covers everything instead of failing.
+
+ ``merge_base`` is the upstream commit the changes are measured from. Checks that compare a
+ file with its previous version use it instead of the pushed commit's parent, which after a
+ merge of upstream ``main`` into the branch is the contributor's own previous tip.
+ """
+ result = run_gh_command(
+ [
+ "gh",
+ "api",
+ f"repos/{APACHE_AIRFLOW_GITHUB_REPOSITORY}/compare/{base}...{fork_owner}:{sha}",
+ "--jq",
+ "{files: [.files[] | [.filename, (.previous_filename // empty)]], "
+ "merge_base: .merge_base_commit.sha}",
+ ],
+ capture_output=True,
+ text=True,
+ )
+ if result.returncode != 0:
+ console_print(
+ f"[warning]Could not compare {fork_owner}:{sha} with {APACHE_AIRFLOW_GITHUB_REPOSITORY}:{base}, "
+ f"so everything runs: {result.stderr.strip()}[/]"
+ )
+ return ForkChanges(files=(), truncated=True)
+ compare = json.loads(result.stdout or "{}")
+ entries = compare.get("files", [])
+ files = tuple(path for entry in entries for path in entry)
+ return ForkChanges(
+ files=files, truncated=len(entries) >= COMPARE_FILE_CAP, merge_base=compare.get("merge_base")
+ )
+
+
+def get_fork_pull_request_labels(*, fork_owner: str, branch: str) -> tuple[str, ...]:
+ """
+ Labels of the open apache/airflow pull request from ``fork_owner:branch``.
+
+ A fork push carries no pull request, so labels such as ``full tests needed`` or
+ ``allow provider dependency bump`` are read here. A label added later takes effect on the
+ next fork run. No pull request, or a failed call, means no labels.
+ """
+ result = run_gh_command(
+ [
+ "gh",
+ "api",
+ "--method",
+ "GET",
+ f"repos/{APACHE_AIRFLOW_GITHUB_REPOSITORY}/pulls",
+ "-f",
+ f"head={fork_owner}:{branch}",
+ "-f",
+ "state=open",
+ "--jq",
+ "[.[0].labels[]?.name]",
Review Comment:
Added `base=<default_branch>` to the lookup, the same branch line the compare uses, with a test.
---
Drafted-by: Claude Code (Opus 5); reviewed by @potiuk before posting
--
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: commits-unsubscribe@airflow.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org