Skip to content

Commit

Permalink
Merge pull request #474 from xing-yang/update_release-tools
Browse files Browse the repository at this point in the history
Update release tools
  • Loading branch information
k8s-ci-robot committed Nov 27, 2023
2 parents a35eba5 + 18c7845 commit 9774cb0
Show file tree
Hide file tree
Showing 6 changed files with 341 additions and 63 deletions.
89 changes: 40 additions & 49 deletions release-tools/SIDECAR_RELEASE_PROCESS.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ The release manager must:
Whenever a new Kubernetes minor version is released, our kubernetes-csi CI jobs
must be updated.

[Our CI jobs](https://k8s-testgrid.appspot.com/sig-storage-csi-ci) have the
[Our CI jobs](https://testgrid.k8s.io/sig-storage-csi-ci) have the
naming convention `<hostpath-deployment-version>-on-<kubernetes-version>`.

1. Jobs should be actively monitored to find and fix failures in sidecars and
Expand Down Expand Up @@ -46,63 +46,54 @@ naming convention `<hostpath-deployment-version>-on-<kubernetes-version>`.
## Release Process
1. Identify all issues and ongoing PRs that should go into the release, and
drive them to resolution.
1. Download the latest version of the
[K8s release notes generator](https://github.com/kubernetes/release/tree/HEAD/cmd/release-notes)
1. Create a
[Github personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token)
with `repo:public_repo` access
1. Generate release notes for the release. Replace arguments with the relevant
information.
* Clean up old cached information (also needed if you are generating release
notes for multiple repos)
```bash
rm -rf /tmp/k8s-repo
```
* For new minor releases on master:
```bash
GITHUB_TOKEN=<token> release-notes \
--discover=mergebase-to-latest \
--org=kubernetes-csi \
--repo=external-provisioner \
--required-author="" \
--markdown-links \
--output out.md
```
* For new patch releases on a release branch:
```bash
GITHUB_TOKEN=<token> release-notes \
--discover=patch-to-latest \
--branch=release-1.1 \
--org=kubernetes-csi \
--repo=external-provisioner \
--required-author="" \
--markdown-links \
--output out.md
```
1. Compare the generated output to the new commits for the release to check if
any notable change missed a release note.
1. Reword release notes as needed. Make sure to check notes for breaking
changes and deprecations.
1. If release is a new major/minor version, create a new `CHANGELOG-<major>.<minor>.md`
file. Otherwise, add the release notes to the top of the existing CHANGELOG
file for that minor version.
1. Submit a PR for the CHANGELOG changes.
1. Submit a PR for README changes, in particular, Compatibility, Feature status,
and any other sections that may need updating.
1. Check that all [canary CI
jobs](https://k8s-testgrid.appspot.com/sig-storage-csi-ci) are passing,
jobs](https://testgrid.k8s.io/sig-storage-csi-ci) are passing,
and that test coverage is adequate for the changes that are going into the release.
1. Check that the post-\<sidecar\>-push-images builds are succeeding.
[Example](https://k8s-testgrid.appspot.com/sig-storage-image-build#post-external-snapshotter-push-images)
[Example](https://testgrid.k8s.io/sig-storage-image-build#post-external-snapshotter-push-images)
1. Generate release notes.
1. Download the latest version of the [K8s release notes generator](https://github.com/kubernetes/release/tree/HEAD/cmd/release-notes)
1. Create a
[Github personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token)
with `repo:public_repo` access
1. For patch release, use the script generate_patch_release_notes.sh. Read the instructions at the top of the
script. The script also creates PRs for each branch.
1. For new minor releases, follow these steps and replace arguments with the relevant
information.
* Clean up old cached information (also needed if you are generating release
notes for multiple repos)
```bash
rm -rf /tmp/k8s-repo
```
* For new minor releases on master:
```bash
GITHUB_TOKEN=<token> release-notes \
--discover=mergebase-to-latest \
--org=kubernetes-csi \
--repo=external-provisioner \
--required-author="" \
--markdown-links \
--output out.md
```
1. Compare the generated output to the new commits for the release to check if
any notable change missed a release note.
1. Reword release notes as needed, ideally in the original PRs so that the
release notes can be regnerated. Make sure to check notes for breaking
changes and deprecations.
1. If release is a new major/minor version, create a new `CHANGELOG-<major>.<minor>.md`
file.
1. Submit a PR for the CHANGELOG changes.
1. Submit a PR for README changes, in particular, Compatibility, Feature status,
and any other sections that may need updating.
1. Make sure that no new PRs have merged in the meantime, and no PRs are in
flight and soon to be merged.
1. Create a new release following a previous release as a template. Be sure to select the correct
branch. This requires Github release permissions as required by the prerequisites.
[external-provisioner example](https://github.com/kubernetes-csi/external-provisioner/releases/new)
1. If release was a new major/minor version, create a new `release-<minor>`
branch at that commit.
1. Check [image build status](https://k8s-testgrid.appspot.com/sig-storage-image-build).
1. Promote images from k8s-staging-sig-storage to k8s.gcr.io/sig-storage. From
1. Check [image build status](https://testgrid.k8s.io/sig-storage-image-build).
1. Promote images from k8s-staging-sig-storage to registry.k8s.io/sig-storage. From
the [k8s image
repo](https://github.com/kubernetes/k8s.io/tree/HEAD/registry.k8s.io/images/k8s-staging-sig-storage),
run `./generate.sh > images.yaml`, and send a PR with the updated images.
Expand All @@ -120,7 +111,7 @@ naming convention `<hostpath-deployment-version>-on-<kubernetes-version>`.

The following jobs are triggered after tagging to produce the corresponding
image(s):
https://k8s-testgrid.appspot.com/sig-storage-image-build
https://testgrid.k8s.io/sig-storage-image-build

Clicking on a failed build job opens that job in https://prow.k8s.io. Next to
the job title is a rerun icon (circle with arrow). Clicking it opens a popup
Expand Down
4 changes: 2 additions & 2 deletions release-tools/cloudbuild.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# See https://github.com/kubernetes/test-infra/blob/HEAD/config/jobs/image-pushing/README.md
# for more details on image pushing process in Kubernetes.
#
# To promote release images, see https://github.com/kubernetes/k8s.io/tree/HEAD/k8s.gcr.io/images/k8s-staging-sig-storage.
# To promote release images, see https://github.com/kubernetes/k8s.io/tree/HEAD/registry.k8s.io/images/k8s-staging-sig-storage.

# This must be specified in seconds. If omitted, defaults to 600s (10 mins).
# Building three images in external-snapshotter takes more than an hour.
Expand All @@ -26,7 +26,7 @@ steps:
# The image must contain bash and curl. Ideally it should also contain
# the desired version of Go (currently defined in release-tools/prow.sh),
# but that just speeds up the build and is not required.
- name: 'gcr.io/k8s-testimages/gcb-docker-gcloud:v20230424-910a2a439d'
- name: 'gcr.io/k8s-testimages/gcb-docker-gcloud:v20230623-56e06d7c18'
entrypoint: ./.cloudbuild.sh
env:
- GIT_TAG=${_GIT_TAG}
Expand Down
170 changes: 170 additions & 0 deletions release-tools/contrib/get_supported_version_csi-sidecar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
# Copyright 2023 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 argparse
import datetime
import re
from collections import defaultdict
import subprocess
import shutil
from dateutil.relativedelta import relativedelta

def check_gh_command():
"""
Pretty much everything is processed from `gh`
Check that the `gh` command is in the path before anything else
"""
if not shutil.which('gh'):
print("Error: The `gh` command is not available in the PATH.")
print("Please install the GitHub CLI (https://cli.github.com/) and try again.")
exit(1)

def duration_ago(dt):
"""
Humanize duration outputs
"""
delta = relativedelta(datetime.datetime.now(), dt)
if delta.years > 0:
return f"{delta.years} year{'s' if delta.years > 1 else ''} ago"
elif delta.months > 0:
return f"{delta.months} month{'s' if delta.months > 1 else ''} ago"
elif delta.days > 0:
return f"{delta.days} day{'s' if delta.days > 1 else ''} ago"
elif delta.hours > 0:
return f"{delta.hours} hour{'s' if delta.hours > 1 else ''} ago"
elif delta.minutes > 0:
return f"{delta.minutes} minute{'s' if delta.minutes > 1 else ''} ago"
else:
return "just now"

def parse_version(version):
"""
Parse version assuming it is in the form of v1.2.3
"""
pattern = r"v(\d+)\.(\d+)\.(\d+)"
match = re.match(pattern, version)
if match:
major, minor, patch = map(int, match.groups())
return (major, minor, patch)

def end_of_life_grouped_versions(versions):
"""
Calculate the end of life date for a minor release version according to : https://kubernetes-csi.github.io/docs/project-policies.html#support
The input is an array of tuples of:
* grouped versions (e.g. 1.0, 1.1)
* array of that contains all versions and their release date (e.g. 1.0.0, 01-01-2013)
versions structure example :
[((3, 5), [('v3.5.0', datetime.datetime(2023, 4, 27, 22, 28, 6))]),
((3, 4),
[('v3.4.1', datetime.datetime(2023, 4, 5, 17, 41, 15)),
('v3.4.0', datetime.datetime(2022, 12, 27, 23, 43, 41))])]
"""
supported_versions = []
# Prepare dates for later calculation
now = datetime.datetime.now()
one_year = datetime.timedelta(days=365)
three_months = datetime.timedelta(days=90)

# get the newer versions on top
sorted_versions_list = sorted(versions.items(), key=lambda x: x[0], reverse=True)

# the latest version is always supported no matter the release date
latest = sorted_versions_list.pop(0)
supported_versions.append(latest[1][-1])

for v in sorted_versions_list:
first_release = v[1][-1]
last_release = v[1][0]
# if the release is less than a year old we support the latest patch version
if now - first_release[1] < one_year:
supported_versions.append(last_release)
# if the main release is older than a year and has a recent path, this is supported
elif now - last_release[1] < three_months:
supported_versions.append(last_release)
return supported_versions

def get_release_docker_image(repo, version):
"""
Extract docker image name from the release page documentation
"""
output = subprocess.check_output(['gh', 'release', '-R', repo, 'view', version], text=True)
#Extract matching image name excluding `
match = re.search(r"docker pull ([\.\/\-\:\w\d]*)", output)
docker_image = match.group(1) if match else ''
return((version, docker_image))

def get_versions_from_releases(repo):
"""
Using `gh` cli get the github releases page details then
create a list of grouped version on major.minor
and for each give all major.minor.patch with release dates
"""
# Run the `gh release` command to get the release list
output = subprocess.check_output(['gh', 'release', '-R', repo, 'list'], text=True)
# Parse the output and group by major and minor version numbers
versions = defaultdict(lambda: [])
for line in output.strip().split('\n'):
parts = line.split('\t')
# pprint.pprint(parts)
version = parts[0]
parsed_version = parse_version(version)
if parsed_version is None:
continue
major, minor, patch = parsed_version

published = datetime.datetime.strptime(parts[3], '%Y-%m-%dT%H:%M:%SZ')
versions[(major, minor)].append((version, published))
return(versions)


def main():
manual = """
This script lists the supported versions Github releases according to https://kubernetes-csi.github.io/docs/project-policies.html#support
It has been designed to help to update the tables from : https://kubernetes-csi.github.io/docs/sidecar-containers.html\n\n
It can take multiple repos as argument, for all CSI sidecars details you can run:
./get_supported_version_csi-sidecar.py -R kubernetes-csi/external-attacher -R kubernetes-csi/external-provisioner -R kubernetes-csi/external-resizer -R kubernetes-csi/external-snapshotter -R kubernetes-csi/livenessprobe -R kubernetes-csi/node-driver-registrar -R kubernetes-csi/external-health-monitor\n
With the output you can then update the documentation manually.
"""
parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, description=manual)
parser.add_argument('--repo', '-R', required=True, action='append', dest='repos', help='The name of the repository in the format owner/repo.')
parser.add_argument('--display', '-d', action='store_true', help='(default) Display EOL versions with their dates', default=True)
parser.add_argument('--doc', '-D', action='store_true', help='Helper to https://kubernetes-csi.github.io/docs/ that prints Docker image for each EOL version')

args = parser.parse_args()

# Verify pre-reqs
check_gh_command()

# Process all repos
for repo in args.repos:
versions = get_versions_from_releases(repo)
eol_versions = end_of_life_grouped_versions(versions)

if args.display:
print(f"Supported versions with release date and age of `{repo}`:\n")
for version in eol_versions:
print(f"{version[0]}\t{version[1].strftime('%Y-%m-%d')}\t{duration_ago(version[1])}")

# TODO : generate proper doc output for the tables of: https://kubernetes-csi.github.io/docs/sidecar-containers.html
if args.doc:
print("\nSupported Versions with docker images for each end of life version:\n")
for version in eol_versions:
_, image = get_release_docker_image(repo, version[0])
print(f"{version[0]}\t{image}")
print()

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

0 comments on commit 9774cb0

Please sign in to comment.