Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatically manage pre-release branches #916

Merged
merged 3 commits into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Auto-update branches
name: Auto-update flavor branches

on:
push:
Expand Down Expand Up @@ -45,3 +45,4 @@ jobs:
- name: Push to ${{ steps.determine.outputs.branch }}
run: |
git push origin --force ${{ steps.determine.outputs.branch }}

77 changes: 77 additions & 0 deletions .github/workflows/update-pre-release-branches.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
name: Auto-update pre-release branches

on:
push:
branches:
- main
pull_request:
paths:
- .github/workflows/update-pre-release-branches.yaml
schedule:
- cron: "0 0 * * *" # Runs every midnight

permissions:
contents: read

jobs:
update-branches:
permissions:
contents: write # for Git to git push
runs-on: ubuntu-latest
outputs:
preRelease: ${{ steps.determine.outputs.preRelease }}
branch: ${{ steps.determine.outputs.branch }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
ssh-key: ${{ secrets.BOT_SSH_KEY }}
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Install Python dependencies
run: pip3 install packaging requests
- name: Determine outstanding pre-release
id: determine
run: |
preRelease=`python3 ./build-scripts/k8s_releases.py get_outstanding_prerelease`
echo "preRelease=$preRelease" >> "$GITHUB_OUTPUT"

if [[ -n "$preRelease" ]]; then
branch="autoupdate/$preRelease"
fi
echo "branch=$branch" >> "$GITHUB_OUTPUT"
- name: Define git credentials
run: |
# Needed to create commits.
git config --global user.name "Github Actions"
git config --global user.email "[email protected]"
- name: 'Update k8s component version: ${{ steps.determine.outputs.preRelease }}'
if: ${{ steps.determine.outputs.preRelease }} != ''
run: |
echo ${{ steps.determine.outputs.preRelease }} > ./build-scripts/components/kubernetes/version
git add ./build-scripts/components/kubernetes/version
git commit -m "Update k8s version to ${{ steps.determine.outputs.preRelease }}"
bschimke95 marked this conversation as resolved.
Show resolved Hide resolved
- name: Create pre-release branch ${{ steps.determine.outputs.branch }}
if: ${{ steps.determine.outputs.preRelease }} != ''
run: |
git checkout -b ${{ steps.determine.outputs.branch }}
git push origin --force ${{ steps.determine.outputs.branch }}
- name: Clean obsolete branches
run: |
git fetch origin

# Log the latest release for reference.
latestRelease=`python3 ./build-scripts/k8s_releases.py get_latest_release`
echo "Latest k8s release: $latestRelease"

for outstandingPreRelease in `python3 ./build-scripts/k8s_releases.py get_obsolete_prereleases`; do
branch="autoupdate/${outstandingPreRelease}"
if git branch -r | grep "origin/$branch"; then
echo "Cleaning up obsolete pre-release branch: $branch"
bschimke95 marked this conversation as resolved.
Show resolved Hide resolved
git push origin --delete $branch
else
echo "Obsolete branch not found, skpping: $branch"
fi
done
87 changes: 87 additions & 0 deletions build-scripts/k8s_releases.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#!/usr/bin/env python3
bschimke95 marked this conversation as resolved.
Show resolved Hide resolved

import json
import sys
from typing import List, Optional

import requests
from packaging.version import Version

K8S_TAGS_URL = "https://api.github.com/repos/kubernetes/kubernetes/tags"


def _url_get(url: str) -> str:
r = requests.get(url, timeout=5)
r.raise_for_status()
return r.text


def get_k8s_tags() -> List[str]:
"""Retrieve semantically ordered k8s releases, newest to oldest."""
response = _url_get(K8S_TAGS_URL)
tags_json = json.loads(response)
if len(tags_json) == 0:
raise Exception("No k8s tags retrieved.")
tag_names = [tag['name'] for tag in tags_json]
# Github already sorts the tags semantically but let's not rely on that.
tag_names.sort(key=lambda x: Version(x), reverse=True)
return tag_names


# k8s release naming:
# * alpha: v{major}.{minor}.{patch}-alpha.{version}
# * beta: v{major}.{minor}.{patch}-beta.{version}
# * rc: v{major}.{minor}.{patch}-rc.{version}
# * stable: v{major}.{minor}.{patch}
def is_stable_release(release: str):
return "-" not in release


def get_latest_stable() -> str:
k8s_tags = get_k8s_tags()
for tag in k8s_tags:
if is_stable_release(tag):
return tag
raise Exception(
"Couldn't find stable release, received tags: %s" % k8s_tags)


def get_latest_release() -> str:
k8s_tags = get_k8s_tags()
return k8s_tags[0]


def get_outstanding_prerelease() -> Optional[str]:
latest_release = get_latest_release()
if not is_stable_release(latest_release):
return latest_release
# The latest release is a stable release, no outstanding pre-release.
return None


def get_obsolete_prereleases() -> List[str]:
"""Return obsolete K8s pre-releases.

We only keep the latest pre-release if there is no corresponding stable
release. All previous pre-releases are discarded.
"""
k8s_tags = get_k8s_tags()
if not is_stable_release(k8s_tags[0]):
# Valid pre-release
k8s_tags = k8s_tags[1:]
# Discard all other pre-releases.
return [tag for tag in k8s_tags if not is_stable_release(tag)]


# Rudimentary CLI that exposes these functions to shell scripts or GH actions.
if __name__ == "__main__":
if len(sys.argv) != 2:
sys.stderr.write(f"Usage: {sys.argv[0]} <function>\n")
sys.exit(1)
f = locals()[sys.argv[1]]
out = f()
if isinstance(out, (list, tuple)):
for item in out:
print(item)
else:
print(out or "")
Loading