mirror of
https://github.com/apache/airflow.git
synced 2026-11-04 07:12:48 +00:00
* Group Dependabot security updates Dependabot groups only apply to security updates when they declare `applies-to: security-updates`; without it a group covers version updates only. Every group in our config relied on that default, so alert-driven bumps bypassed grouping entirely and opened one PR each - axios in edge3 (#70145), mermaid (#69132, #69137), gitpython (#70428), zeep (#68780), @hey-api/openapi-ts (#69265) and the two /go-sdk Go bumps (#70226, #69214). Add a `security-updates` group to every entry that targets the default branch. Entries carrying `target-branch: v3-3-test` are deliberately left alone: Dependabot raises security updates against the default branch only, so a security group there would never match. Comments note this so it does not look like an oversight. The three existing `*-major-version-updates` groups were already `applies-to: security-updates` but restricted to `update-types: [major]`, so minor and patch security fixes fell through ungrouped. They are widened to cover all security updates and renamed to `*-security-updates` to match what they actually do. Also adds a `gomod` entry for /go-sdk, which had no configuration at all - its Go security bumps were arriving individually because Dependabot raises security updates for ecosystems with no entry, but can only group them when one exists. Generated-by: Claude Opus 5 (1M context) * Mirror remaining dependency directories onto v3-3-test Dependabot raises security updates against the default branch only, so a fix that lands on main does not reach a maintenance branch by itself. Cherry-picking one over is unreliable: these commits carry lock file diffs, and main's lock files have long diverged from v3-3-test's, so the pick conflicts more often than not. Letting Dependabot maintain the branch directly avoids that entirely - it resolves the dependency and regenerates the lock file on v3-3-test itself. Six directories were tracked on main but not on the maintenance branch, so a fixed version had no way of reaching it: - npm: edge3 www, fab www, /registry, react plugin template - uv: /dev/breeze - gomod: /go-sdk They now have `target-branch: v3-3-test` entries following the policy the branch already uses for core-ui and auth-ui: minor and patch only, majors ignored. A fix that needs a major bump still has to be backported by hand. `dev/update_github_branch_config.py` generates the same six entries, so cutting the next release branch does not silently reintroduce the gap. Generated-by: Claude Opus 5 (1M context)
504 lines
18 KiB
Python
Executable File
504 lines
18 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# 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.
|
|
# /// script
|
|
# requires-python = ">=3.9"
|
|
# ///
|
|
"""
|
|
Update .github/ configuration files on ``main`` when a new major/minor
|
|
release branch (vX-Y-test) is created.
|
|
|
|
Adds the new branch to dependabot, milestone-tag-assistant, basic-tests,
|
|
ci-notification workflows, boring-cyborg auto-labelling, and the
|
|
README.md build status badge.
|
|
|
|
Usage::
|
|
|
|
uv run dev/update_github_branch_config.py <NEW_MAJOR> <NEW_MINOR>
|
|
|
|
Example — after creating the v3-3-test branch::
|
|
|
|
uv run dev/update_github_branch_config.py 3 3
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
|
|
|
|
def _read(path: Path) -> str:
|
|
return path.read_text()
|
|
|
|
|
|
def _write(path: Path, content: str) -> None:
|
|
path.write_text(content)
|
|
|
|
|
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
# dependabot.yml
|
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
def update_dependabot(new_branch: str, prev_branch: str, new_dash: str) -> None:
|
|
path = REPO_ROOT / ".github" / "dependabot.yml"
|
|
content = _read(path)
|
|
|
|
if f"target-branch: {new_branch}" in content:
|
|
print(f"dependabot.yml: {new_branch} already present, skipping.")
|
|
return
|
|
|
|
print(f"dependabot.yml: adding {new_branch} entries...")
|
|
|
|
# 1. Add github-actions entry before the existing entry for prev_branch
|
|
gh_actions_new_entry = f"""\
|
|
- package-ecosystem: "github-actions"
|
|
directory: "/"
|
|
cooldown:
|
|
default-days: 4
|
|
schedule:
|
|
# Check for updates to GitHub Actions every week
|
|
interval: "weekly"
|
|
target-branch: {new_branch}
|
|
groups:
|
|
github-actions-updates:
|
|
patterns:
|
|
- "*"
|
|
|
|
"""
|
|
# Match the full existing block for prev_branch
|
|
prev_gh_actions = f"""\
|
|
- package-ecosystem: "github-actions"
|
|
directory: "/"
|
|
cooldown:
|
|
default-days: 4
|
|
schedule:
|
|
# Check for updates to GitHub Actions every week
|
|
interval: "weekly"
|
|
target-branch: {prev_branch}
|
|
"""
|
|
content = content.replace(prev_gh_actions, gh_actions_new_entry + prev_gh_actions, 1)
|
|
|
|
# 2. Add pip + npm entries before the previous branch's comment block
|
|
pip_npm_block = f"""\
|
|
# Repeat dependency updates on {new_branch} branch as well
|
|
- package-ecosystem: pip
|
|
cooldown:
|
|
default-days: 4
|
|
directories:
|
|
- /airflow-core
|
|
- /airflow-ctl
|
|
- /clients/python
|
|
- /dev/breeze
|
|
- /docker-tests
|
|
- /kubernetes-tests
|
|
- /chart
|
|
- /task-sdk
|
|
- /
|
|
schedule:
|
|
interval: daily
|
|
target-branch: {new_branch}
|
|
groups:
|
|
pip-dependency-updates:
|
|
patterns:
|
|
- "*"
|
|
|
|
- package-ecosystem: npm
|
|
cooldown:
|
|
default-days: 4
|
|
directories:
|
|
- /airflow-core/src/airflow/ui
|
|
schedule:
|
|
interval: "weekly"
|
|
target-branch: {new_branch}
|
|
groups:
|
|
{new_dash}-core-ui-package-updates:
|
|
patterns:
|
|
- "*"
|
|
update-types:
|
|
- "minor"
|
|
- "patch"
|
|
ignore:
|
|
- dependency-name: "*"
|
|
update-types: ["version-update:semver-major"]
|
|
|
|
- package-ecosystem: npm
|
|
cooldown:
|
|
default-days: 4
|
|
directories:
|
|
- /airflow-core/src/airflow/api_fastapi/auth/managers/simple/ui
|
|
schedule:
|
|
interval: "weekly"
|
|
target-branch: {new_branch}
|
|
groups:
|
|
{new_dash}-auth-ui-package-updates:
|
|
patterns:
|
|
- "*"
|
|
update-types:
|
|
- "minor"
|
|
- "patch"
|
|
ignore:
|
|
- dependency-name: "*"
|
|
update-types: ["version-update:semver-major"]
|
|
|
|
# The remaining directories are mirrored onto {new_branch} as well. Dependabot raises security
|
|
# updates against the default branch only, so a fixed version reaches the maintenance branch
|
|
# through its own version updates - Dependabot resolves and regenerates the lock file on that
|
|
# branch, which a cherry-picked lock file diff from main cannot do. Majors stay ignored, so a
|
|
# fix that needs a major bump still has to be backported by hand.
|
|
- package-ecosystem: npm
|
|
cooldown:
|
|
default-days: 4
|
|
directories:
|
|
- /providers/edge3/src/airflow/providers/edge3/plugins/www
|
|
schedule:
|
|
interval: "weekly"
|
|
target-branch: {new_branch}
|
|
groups:
|
|
{new_dash}-edge-ui-package-updates:
|
|
patterns:
|
|
- "*"
|
|
update-types:
|
|
- "minor"
|
|
- "patch"
|
|
ignore:
|
|
- dependency-name: "*"
|
|
update-types: ["version-update:semver-major"]
|
|
|
|
- package-ecosystem: npm
|
|
cooldown:
|
|
default-days: 4
|
|
directories:
|
|
- /providers/fab/src/airflow/providers/fab/www
|
|
schedule:
|
|
interval: daily
|
|
target-branch: {new_branch}
|
|
groups:
|
|
{new_dash}-fab-ui-package-updates:
|
|
patterns:
|
|
- "*"
|
|
update-types:
|
|
- "minor"
|
|
- "patch"
|
|
ignore:
|
|
- dependency-name: "*"
|
|
update-types: ["version-update:semver-major"]
|
|
|
|
- package-ecosystem: npm
|
|
cooldown:
|
|
default-days: 4
|
|
directories:
|
|
- /registry
|
|
schedule:
|
|
interval: "weekly"
|
|
target-branch: {new_branch}
|
|
groups:
|
|
{new_dash}-registry-package-updates:
|
|
patterns:
|
|
- "*"
|
|
update-types:
|
|
- "minor"
|
|
- "patch"
|
|
ignore:
|
|
- dependency-name: "*"
|
|
update-types: ["version-update:semver-major"]
|
|
|
|
- package-ecosystem: npm
|
|
cooldown:
|
|
default-days: 4
|
|
directories:
|
|
- /dev/react-plugin-tools/react_plugin_template
|
|
schedule:
|
|
interval: "weekly"
|
|
target-branch: {new_branch}
|
|
groups:
|
|
{new_dash}-ui-plugin-template-package-updates:
|
|
patterns:
|
|
- "*"
|
|
update-types:
|
|
- "minor"
|
|
- "patch"
|
|
ignore:
|
|
- dependency-name: "*"
|
|
update-types: ["version-update:semver-major"]
|
|
|
|
- package-ecosystem: "uv"
|
|
cooldown:
|
|
default-days: 4
|
|
directory: "/dev/breeze"
|
|
schedule:
|
|
interval: "weekly"
|
|
target-branch: {new_branch}
|
|
groups:
|
|
{new_dash}-uv-dependency-updates:
|
|
patterns:
|
|
- "*"
|
|
update-types:
|
|
- "minor"
|
|
- "patch"
|
|
ignore:
|
|
- dependency-name: "*"
|
|
update-types: ["version-update:semver-major"]
|
|
|
|
- package-ecosystem: "gomod"
|
|
cooldown:
|
|
default-days: 4
|
|
directory: "/go-sdk"
|
|
schedule:
|
|
interval: "weekly"
|
|
target-branch: {new_branch}
|
|
groups:
|
|
{new_dash}-go-sdk-dependency-updates:
|
|
patterns:
|
|
- "*"
|
|
update-types:
|
|
- "minor"
|
|
- "patch"
|
|
ignore:
|
|
- dependency-name: "*"
|
|
update-types: ["version-update:semver-major"]
|
|
|
|
"""
|
|
prev_comment = f" # Repeat dependency updates on {prev_branch} branch as well"
|
|
content = content.replace(prev_comment, pip_npm_block + prev_comment, 1)
|
|
|
|
_write(path, content)
|
|
|
|
|
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
# milestone-tag-assistant.yml
|
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
def update_milestone_tag_assistant(new_branch: str, prev_branch: str) -> None:
|
|
path = REPO_ROOT / ".github" / "workflows" / "milestone-tag-assistant.yml"
|
|
content = _read(path)
|
|
|
|
if f"- {new_branch}" in content:
|
|
print(f"milestone-tag-assistant.yml: {new_branch} already present, skipping.")
|
|
return
|
|
|
|
print(f"milestone-tag-assistant.yml: adding {new_branch}...")
|
|
content = content.replace(
|
|
f" - {prev_branch}",
|
|
f" - {new_branch}\n - {prev_branch}",
|
|
1,
|
|
)
|
|
_write(path, content)
|
|
|
|
|
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
# basic-tests.yml
|
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
def update_basic_tests(
|
|
new_branch: str,
|
|
prev_branch: str,
|
|
new_version: str,
|
|
prev_version: str,
|
|
new_dash: str,
|
|
prev_dash: str,
|
|
) -> None:
|
|
path = REPO_ROOT / ".github" / "workflows" / "basic-tests.yml"
|
|
content = _read(path)
|
|
|
|
print(f"basic-tests.yml: updating release-management dry-run versions to {new_version}...")
|
|
|
|
content = content.replace(
|
|
f"create-minor-branch --version-branch {prev_dash}",
|
|
f"create-minor-branch --version-branch {new_dash}",
|
|
)
|
|
content = content.replace(
|
|
f"--sync-branch {prev_branch}",
|
|
f"--sync-branch {new_branch}",
|
|
)
|
|
|
|
# Update start-rc-process version and previous-version
|
|
content = content.replace(
|
|
f"start-rc-process --version {prev_version}.0rc1",
|
|
f"start-rc-process --version {new_version}.0rc1",
|
|
)
|
|
content = re.sub(
|
|
r"--previous-version \d+\.\d+\.0(\s+.*--task-sdk-version)",
|
|
f"--previous-version {prev_version}.0\\1",
|
|
content,
|
|
)
|
|
# Bump task-sdk version: increment minor
|
|
content = re.sub(
|
|
r"--task-sdk-version \d+\.\d+\.0rc1",
|
|
f"--task-sdk-version 1.{int(new_version.split('.')[1]) - 1}.0rc1",
|
|
content,
|
|
)
|
|
|
|
# Update start-release version
|
|
content = re.sub(
|
|
rf"start-release --version {re.escape(prev_version)}\.\d+",
|
|
f"start-release --version {new_version}.7",
|
|
content,
|
|
)
|
|
|
|
_write(path, content)
|
|
|
|
|
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
# ci-notification.yml
|
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
def update_ci_notification(new_branch: str, prev_branch: str) -> None:
|
|
path = REPO_ROOT / ".github" / "workflows" / "ci-notification.yml"
|
|
content = _read(path)
|
|
|
|
print(f"ci-notification.yml: switching branch to {new_branch}...")
|
|
content = content.replace(
|
|
f'branch: ["{prev_branch}"]',
|
|
f'branch: ["{new_branch}"]',
|
|
)
|
|
_write(path, content)
|
|
|
|
|
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
# boring-cyborg.yml
|
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
BORING_CYBORG_BACKPORT_BLOCK = """\
|
|
# This should be copy of the "area:dev-tools" above minus contributing docs and some files that should
|
|
# only make sense in main - it should be updated when we switch maintenance branch
|
|
backport-to-{new_branch}:
|
|
- scripts/**/*
|
|
- dev/**/*
|
|
- .github/**/*
|
|
- Dockerfile.ci
|
|
- yamllint-config.yml
|
|
- .dockerignore
|
|
- .hadolint.yaml
|
|
- .pre-commit-config.yaml
|
|
- .rat-excludes
|
|
|
|
"""
|
|
|
|
|
|
def update_boring_cyborg(new_branch: str, prev_branch: str) -> None:
|
|
path = REPO_ROOT / ".github" / "boring-cyborg.yml"
|
|
content = _read(path)
|
|
|
|
if f"backport-to-{new_branch}:" in content:
|
|
print(f"boring-cyborg.yml: backport-to-{new_branch} already present, skipping.")
|
|
return
|
|
|
|
print(f"boring-cyborg.yml: adding backport-to-{new_branch}...")
|
|
new_block = BORING_CYBORG_BACKPORT_BLOCK.format(new_branch=new_branch)
|
|
anchor = f" backport-to-{prev_branch}:"
|
|
# Find the comment block preceding the previous backport entry
|
|
prev_comment = f"""\
|
|
# This should be copy of the "area:dev-tools" above minus contributing docs and some files that should
|
|
# only make sense in main - it should be updated when we switch maintenance branch
|
|
backport-to-{prev_branch}:"""
|
|
if prev_comment in content:
|
|
content = content.replace(prev_comment, new_block + anchor, 1)
|
|
else:
|
|
content = content.replace(anchor, new_block + anchor, 1)
|
|
|
|
_write(path, content)
|
|
|
|
|
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
# README.md — build status badge
|
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
def update_readme_badge(new_branch: str, prev_branch: str, new_version: str, prev_version: str) -> None:
|
|
path = REPO_ROOT / "README.md"
|
|
content = _read(path)
|
|
|
|
old_badge = (
|
|
f"[](https://github.com/apache/airflow/actions)"
|
|
)
|
|
new_badge = (
|
|
f"[](https://github.com/apache/airflow/actions)"
|
|
)
|
|
|
|
if new_branch in content:
|
|
print(f"README.md: {new_branch} badge already present, skipping.")
|
|
return
|
|
|
|
if old_badge not in content:
|
|
print(f"README.md: could not find existing {prev_branch} badge, skipping.")
|
|
return
|
|
|
|
print(f"README.md: updating 3.x build status badge to {new_branch}...")
|
|
content = content.replace(old_badge, new_badge)
|
|
_write(path, content)
|
|
|
|
|
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
# dev/README_RELEASE_AIRFLOW.md
|
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
def update_release_readme(new_branch: str, prev_branch: str) -> None:
|
|
path = REPO_ROOT / "dev" / "README_RELEASE_AIRFLOW.md"
|
|
content = _read(path)
|
|
|
|
print(f"README_RELEASE_AIRFLOW.md: updating apache/{prev_branch} references to apache/{new_branch}...")
|
|
content = content.replace(f"apache/{prev_branch}", f"apache/{new_branch}")
|
|
_write(path, content)
|
|
|
|
|
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
# main
|
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
def main() -> None:
|
|
if len(sys.argv) != 3:
|
|
print(f"Usage: {sys.argv[0]} <NEW_MAJOR> <NEW_MINOR>")
|
|
print(f"Example: {sys.argv[0]} 3 3 (for v3-3-test branch)")
|
|
sys.exit(1)
|
|
|
|
new_major = int(sys.argv[1])
|
|
new_minor = int(sys.argv[2])
|
|
prev_minor = new_minor - 1
|
|
|
|
new_branch = f"v{new_major}-{new_minor}-test"
|
|
prev_branch = f"v{new_major}-{prev_minor}-test"
|
|
new_version = f"{new_major}.{new_minor}"
|
|
prev_version = f"{new_major}.{prev_minor}"
|
|
new_dash = f"{new_major}-{new_minor}"
|
|
prev_dash = f"{new_major}-{prev_minor}"
|
|
|
|
print(f"Updating .github/ configs: adding {new_branch} (previous: {prev_branch})")
|
|
print(f"Repository root: {REPO_ROOT}")
|
|
print()
|
|
|
|
update_dependabot(new_branch, prev_branch, new_dash)
|
|
update_milestone_tag_assistant(new_branch, prev_branch)
|
|
update_basic_tests(new_branch, prev_branch, new_version, prev_version, new_dash, prev_dash)
|
|
update_ci_notification(new_branch, prev_branch)
|
|
update_boring_cyborg(new_branch, prev_branch)
|
|
update_readme_badge(new_branch, prev_branch, new_version, prev_version)
|
|
update_release_readme(new_branch, prev_branch)
|
|
|
|
print()
|
|
print("Done! Please review the changes with 'git diff' before committing.")
|
|
print()
|
|
print("Additional manual steps:")
|
|
print(f" - Create the 'backport-to-{new_branch}' label:")
|
|
print(
|
|
f" gh label create 'backport-to-{new_branch}' --repo apache/airflow"
|
|
f" --description 'Backport to {new_branch}' --color 0e8a16"
|
|
)
|
|
print(" - Update test references in dev/breeze/tests/test_set_milestone.py if needed")
|
|
print(" - Update dev/README_AIRFLOW3_DEV.md references if needed")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|