Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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
2 changes: 1 addition & 1 deletion .github/workflows/pypi-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ jobs:
name: >-
Publish Python 🐍 distribution 📦 to PyPI
needs:
- publish-to-testpypi
Comment thread
janetkuo marked this conversation as resolved.
Outdated
- build
Comment thread
shrutiyam-glitch marked this conversation as resolved.
Outdated
runs-on: ubuntu-latest
environment:
name: pypi
Expand Down
170 changes: 170 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
name: Scheduled Release Automation
Comment thread
shrutiyam-glitch marked this conversation as resolved.

on:
# Version tag is incremented by patch update
schedule:
- cron: '0 6 * * 5' # Every Friday at 06:00 UTC
Comment thread
shrutiyam-glitch marked this conversation as resolved.
Outdated
# For Major/Minor version updates, enter tag manually
Comment thread
janetkuo marked this conversation as resolved.
workflow_dispatch:
inputs:
tag:
description: 'Tag for release (e.g., v0.2.0). Leave blank for auto-patch.'
required: false
type: string

permissions:
contents: write # Allows to create and push tags
Comment thread
shrutiyam-glitch marked this conversation as resolved.
Outdated
pull-requests: write
id-token: write # Required for Google Auth

jobs:
promote:
name: Promote Images
runs-on: ubuntu-latest
timeout-minutes: 30
Comment thread
shrutiyam-glitch marked this conversation as resolved.
Outdated
outputs:
pr_url: ${{ steps.extract-pr.outputs.pr_url }}
run_release: ${{ steps.set-tag.outputs.run_release || steps.auto-tag.outputs.run_release }}
new_tag: ${{ steps.set-tag.outputs.new_tag || steps.auto-tag.outputs.new_tag }}

steps:
- name: Checkout agent-sandbox
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # ratchet:actions/checkout@v4
with:
fetch-depth: 0
# To ensure pushing the tag during the workflow has the permission to trigger the PyPI publish workflow.
# Github secret has repo access.
token: ${{ secrets.GH_AUTOMATION_PAT }}

- name: Set up Go
uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # ratchet:actions/setup-go@v5
with:
go-version-file: 'go.mod'

- name: Clone k8s.io
env:
# Use a Fine-Grained PAT scoped to kubernetes/k8s.io with contents:write and pull_requests:write
GH_TOKEN: ${{ secrets.K8S_IO_PAT }}
Comment thread
shrutiyam-glitch marked this conversation as resolved.
run: |
cd ..
git clone https://github.com/kubernetes/k8s.io.git
cd k8s.io
git remote rename origin upstream
gh repo fork --clone=false || true # Ignore if already exists or fails to add remote if already there
# Ensure origin remote points to the fork
GH_USER=$(gh api user --jq .login)
git remote add origin "https://x-access-token:${{ secrets.K8S_IO_PAT }}@github.com/$GH_USER/k8s.io.git" || git remote set-url origin "https://x-access-token:${{ secrets.K8S_IO_PAT }}@github.com/$GH_USER/k8s.io.git"

- name: Authenticate to Google Cloud
uses: google-github-actions/auth@c200f3691d83b41bf9bbd8638997a462592937ed # ratchet:google-github-actions/auth@v2
with:
workload_identity_provider: '${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }}'
service_account: '${{ secrets.GCP_SERVICE_ACCOUNT }}'

- name: Set up Cloud SDK
uses: google-github-actions/setup-gcloud@e427ad8a34f8676edf47cf7d7925499adf3eb74f # ratchet:google-github-actions/setup-gcloud@v2

- name: Setup Git Identity
run: |
# Use the standard GitHub Actions Bot identity
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"

- name: Set Tag from Input
if: github.event.inputs.tag != ''
id: set-tag
run: |
echo "run_release=true" >> "$GITHUB_OUTPUT"
echo "new_tag=${{ github.event.inputs.tag }}" >> "$GITHUB_OUTPUT"

- name: Auto Tag if New Commits
if: github.event.inputs.tag == ''
id: auto-tag
run: |
./dev/tools/auto-tag

- name: Run Promotion Script
if: steps.set-tag.outputs.run_release == 'true' || steps.auto-tag.outputs.run_release == 'true'
id: extract-pr
env:
# Use a Fine-Grained PAT scoped to kubernetes/k8s.io with contents:write and pull_requests:write
GH_TOKEN: ${{ secrets.K8S_IO_PAT }}
run: |
# Run promotion script
TAG="${{ steps.set-tag.outputs.new_tag || steps.auto-tag.outputs.new_tag }}"
make release-promote TAG="$TAG" K8S_IO_DIR=../k8s.io

# Read the PR URL from the file generated by the script
if [ ! -f promote_pr.url ]; then
echo "❌ promote_pr.url file not found. Check make release-promote output."
exit 1
fi

PR_URL=$(cat promote_pr.url)
echo "Found PR: $PR_URL"
echo "pr_url=$PR_URL" >> "$GITHUB_OUTPUT"

# Restart release from this step if the job times out
poll-merge:
name: Poll PR Status
needs: promote
if: needs.promote.outputs.run_release == 'true'
runs-on: ubuntu-latest
timeout-minutes: 720 # 12-hour limit for long k8s.io merge cycles
Comment thread
shrutiyam-glitch marked this conversation as resolved.
Outdated
steps:
- name: Wait for PR Merge
env:
# Use a Fine-Grained PAT scoped to kubernetes/k8s.io with contents:write and pull_requests:write
GH_TOKEN: ${{ secrets.K8S_IO_PAT }}
PR_URL: ${{ needs.promote.outputs.pr_url }}
run: |
echo "Waiting for $PR_URL to be merged..."

# 144 attempts, every 5 minutes = 12 hours total
for i in {1..144}; do
# Explicitly check the kubernetes/k8s.io repo context
STATE=$(gh pr view "$PR_URL" --repo kubernetes/k8s.io --json state --jq '.state')

echo "Attempt $i: PR is currently $STATE"

if [ "$STATE" == "MERGED" ]; then
echo "✅ PR merged. Proceeding."
exit 0
elif [ "$STATE" == "CLOSED" ]; then
echo "❌ PR was closed without merging."
exit 1
fi

sleep 300
done

echo "❌ Timed out waiting for merge."
Comment thread
shrutiyam-glitch marked this conversation as resolved.
Outdated
exit 1

publish-draft:
name: Publish Draft Release
needs: [promote, poll-merge]
if: needs.promote.outputs.run_release == 'true'
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # ratchet:actions/checkout@v4
Comment thread
shrutiyam-glitch marked this conversation as resolved.

- name: Set up Go
uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # ratchet:actions/setup-go@v5
with:
go-version-file: 'go.mod'

- name: Set up Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # ratchet:actions/setup-python@v5
with:
python-version: '3.10'

- name: Generate Draft Release and Manifests
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
run: |
# This finally publishes the draft for your review
make release-publish TAG="${{ needs.promote.outputs.new_tag }}"
69 changes: 69 additions & 0 deletions dev/tools/auto-tag
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#!/usr/bin/env python3
# Copyright 2026 The Kubernetes Authors.
#
# Licensed 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.

import os
import sys
import re
import subprocess

from shared.git_ops import run_command, validate_tag, check_tag_exists, create_and_push_tag
Comment thread
shrutiyam-glitch marked this conversation as resolved.
Outdated

def increment_patch(version_str):
Comment thread
shrutiyam-glitch marked this conversation as resolved.
"""Increments the patch version of a SemVer string (e.g., v0.1.0 -> v0.1.1)."""
match = re.match(r'^v(\d+)\.(\d+)\.(\d+)$', version_str)
Comment thread
shrutiyam-glitch marked this conversation as resolved.
Outdated
if not match:
print(f"❌ Cannot parse version {version_str} for auto-increment.")
sys.exit(1)
major, minor, patch = map(int, match.groups())
return f"v{major}.{minor}.{patch + 1}"

def main():

print("🔍 Checking for new commits since last release...")

# Get latest tag
try:
latest_tag = run_command(["git", "describe", "--tags", "--abbrev=0", "--match=v*"], capture_output=True)
except Exception as e:
print(f"⚠️ Error running git describe: {e}")
Comment thread
shrutiyam-glitch marked this conversation as resolved.
Outdated
print(" Assuming no tags exist or history is missing. Defaulting to v0.1.0 base.")
latest_tag = "v0.1.0"

print(f"Found latest tag: {latest_tag}")

# Check for commits since latest tag
commits = run_command(["git", "log", f"{latest_tag}..HEAD", "--oneline"], capture_output=True)

if not commits:
print("ℹ️ No new commits since last release. Skipping release.")
if "GITHUB_OUTPUT" in os.environ:
with open(os.environ["GITHUB_OUTPUT"], "a") as f:
f.write("run_release=false\n")
sys.exit(0)

print(f"Found new commits since {latest_tag}:\n{commits}")

new_tag = increment_patch(latest_tag)
print(f"🚀 Proposing new tag: {new_tag}")

# Set output for GitHub Actions
if "GITHUB_OUTPUT" in os.environ:
with open(os.environ["GITHUB_OUTPUT"], "a") as f:
f.write("run_release=true\n")
f.write(f"new_tag={new_tag}\n")
print(f"SUCCESS: Auto-patched to tag {new_tag}")

if __name__ == '__main__':
main()
Loading