Update release process (#25002) * pypi_deploy.sh: dry run by default (`twine check`), `--publish` to upload. `--wheel-dir` publishes the wheels of a previous dry run without downloading them again. Missing packages need confirmation. * Add a release tracker template * release-management.md: document the missing tracker steps * RELEASING.md: fix the iree-turbine release docs link. Co-Authored-By: Claude --------- Signed-off-by: Tobias Fuchs <fuchs@roofline.ai> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
diff --git a/RELEASING.md b/RELEASING.md index 3a4d98c..14c75e3 100644 --- a/RELEASING.md +++ b/RELEASING.md
@@ -103,4 +103,4 @@ * Releasing IREE core packages: https://iree.dev/developers/general/release-management/ * Releasing iree-turbine packages: - https://github.com/iree-org/iree-turbine/blob/main/docs/releasing.md + https://github.com/iree-org/iree-turbine/blob/main/docs/infra/releasing.md
diff --git a/build_tools/python_deploy/pypi_deploy.sh b/build_tools/python_deploy/pypi_deploy.sh index 9c889f2..9af35a9 100755 --- a/build_tools/python_deploy/pypi_deploy.sh +++ b/build_tools/python_deploy/pypi_deploy.sh
@@ -22,15 +22,97 @@ # python -m venv .venv # source .venv/bin/activate # python -m pip install -r ./pypi_deploy_requirements.txt -# ./pypi_deploy.sh iree-3.4.0rc20250430 +# ./pypi_deploy.sh iree-3.4.0rc20250430 # dry run +# ./pypi_deploy.sh --publish iree-3.4.0rc20250430 # upload to PyPI +# +# By default the script runs as a dry run: wheels are downloaded, promoted and +# validated with `twine check`, but nothing is uploaded. Pass --publish to +# upload to PyPI. +# +# To publish the wheels from a previous dry run without downloading them again, +# pass the directory that the dry run printed: +# ./pypi_deploy.sh --publish --wheel-dir /tmp/iree_pypi_wheels.XXXXX +# +# Each package is uploaded separately. If an upload fails (e.g. because your +# credentials lack permission for that PyPI project), the remaining packages +# are still uploaded and the failures are listed in a summary at the end. set -euo pipefail -RELEASE="$1" +function print_usage() { + echo "Usage: $0 [--publish] <release tag, e.g. iree-3.4.0rc20250430>" + echo " $0 [--publish] --wheel-dir <directory from a previous run>" +} + +PUBLISH=0 +RELEASE="" +WHEEL_DIR="" +while (( $# > 0 )); do + case "$1" in + --publish) + PUBLISH=1 + ;; + --wheel-dir) + if (( $# < 2 )); then + echo "--wheel-dir requires a directory." + print_usage + exit 1 + fi + WHEEL_DIR="$2" + shift + ;; + --wheel-dir=*) + WHEEL_DIR="${1#--wheel-dir=}" + ;; + -h|--help) + print_usage + exit 0 + ;; + -*) + echo "Unknown option: $1" + print_usage + exit 1 + ;; + *) + if [[ -n "${RELEASE}" ]]; then + echo "Only one release may be given." + print_usage + exit 1 + fi + RELEASE="$1" + ;; + esac + shift +done +if [[ -n "${WHEEL_DIR}" && -n "${RELEASE}" ]]; then + echo "Pass either a release tag or --wheel-dir, not both." + print_usage + exit 1 +fi +if [[ -z "${WHEEL_DIR}" && -z "${RELEASE}" ]]; then + print_usage + exit 1 +fi +if [[ -n "${WHEEL_DIR}" && ! -d "${WHEEL_DIR}" ]]; then + echo "Wheel directory '${WHEEL_DIR}' does not exist." + exit 1 +fi + +# Packages that every release is expected to contain, as named in wheel files. +EXPECTED_PACKAGES=( + iree_base_compiler + iree_base_runtime + iree_tools_tf + iree_tools_tflite +) SCRIPT_DIR="$(dirname -- "$( readlink -f -- "$0"; )")"; REQUIREMENTS_FILE="${SCRIPT_DIR}/pypi_deploy_requirements.txt" -TMPDIR="$(mktemp --directory --tmpdir iree_pypi_wheels.XXXXX)" +if [[ -n "${WHEEL_DIR}" ]]; then + TMPDIR="$(readlink -f -- "${WHEEL_DIR}")" +else + TMPDIR="$(mktemp --directory --tmpdir iree_pypi_wheels.XXXXX)" +fi function check_command_exists() { if ! command -v "$1" > /dev/null; then @@ -76,6 +158,41 @@ ls } +function confirm_or_exit() { + local reply="" + if ! read -r -p "$1 [y/N] " reply < /dev/tty; then + echo "" + echo "Could not read confirmation, aborting." + exit 1 + fi + if [[ ! "${reply}" =~ ^[Yy]([Ee][Ss])?$ ]]; then + echo "Aborting." + exit 1 + fi +} + +function check_expected_packages() { + echo "" + echo "Checking for expected packages..." + local missing=() + local package + for package in "${EXPECTED_PACKAGES[@]}"; do + local wheels=( "${package}"-*.whl ) + if [[ -e "${wheels[0]}" ]]; then + echo " ${package}: ${#wheels[@]} wheel(s)" + else + echo " ${package}: MISSING" + missing+=( "${package}" ) + fi + done + + if (( ${#missing[@]} > 0 )); then + echo "" + echo "'${RELEASE:-${TMPDIR}}' has no wheels for: ${missing[*]}" + confirm_or_exit "Continue without them?" + fi +} + function edit_release_versions() { echo "" echo "Editing release versions..." @@ -89,27 +206,99 @@ } function upload_wheels() { + # In a dry run, `twine check` stands in for `twine upload` so the wheels are + # validated. + local twine_args=( upload --verbose ) + local action="Uploading" + local done_label="uploaded" echo "" - echo "Uploading wheels..." - twine upload --verbose * + if (( PUBLISH )); then + echo "Uploading wheels..." + else + twine_args=( check --strict ) + action="Checking" + done_label="ok" + echo "Dry run: checking wheels instead of uploading (pass --publish to upload)..." + fi + + # Group and upload wheels by package. + local packages=() + local file + for file in *.whl; do + packages+=( "${file%%-*}" ) + done + mapfile -t packages < <(printf '%s\n' "${packages[@]}" | sort -u) + + local succeeded=() + local failed=() + local package + for package in "${packages[@]}"; do + echo "" + echo "${action} ${package}..." + if twine "${twine_args[@]}" "${package}"-*.whl; then + succeeded+=( "${package}" ) + else + echo "${action} ${package} failed, continuing with remaining packages." + failed+=( "${package}" ) + fi + done + + echo "" + if (( PUBLISH )); then + echo "Upload summary:" + else + echo "Dry run. Wheels that would be uploaded:" + ls -1 + echo "" + echo "Check summary:" + fi + for package in "${succeeded[@]}"; do + echo " ${done_label}: ${package}" + done + for package in "${failed[@]}"; do + echo " FAILED: ${package}" + done + if (( PUBLISH && ${#failed[@]} > 0 )); then + echo "" + echo "Some packages were not uploaded. Their wheels are kept in ${TMPDIR}." + echo " twine upload ${TMPDIR}/<package>-*.whl" + fi + if (( ! PUBLISH )); then + echo "" + echo "To upload these wheels without downloading them again, run:" + echo " $0 --publish --wheel-dir ${TMPDIR}" + fi } function main() { + local source="${RELEASE:-${TMPDIR}}" + if (( PUBLISH )); then + echo "Publishing '${source}' to PyPI." + else + echo "Dry run for '${source}', nothing will be uploaded (pass --publish to upload)." + fi echo "Changing into ${TMPDIR}" cd "${TMPDIR}" set +e check_requirements - if ! check_command_exists gh; then + if [[ -z "${WHEEL_DIR}" ]] && ! check_command_exists gh; then echo "The GitHub CLI 'gh' is required. See https://github.com/cli/cli#installation." echo " Googlers, the PPA should already be on your linux machine." exit 1 fi set -e - download_wheels + if [[ -z "${WHEEL_DIR}" ]]; then + download_wheels + else + echo "" + echo "Using wheels from '${TMPDIR}' instead of downloading:" + ls + fi + check_expected_packages edit_release_versions upload_wheels }
diff --git a/docs/website/docs/developers/general/release-management.md b/docs/website/docs/developers/general/release-management.md index 0bf7482..b4b8029 100644 --- a/docs/website/docs/developers/general/release-management.md +++ b/docs/website/docs/developers/general/release-management.md
@@ -203,10 +203,24 @@ ## :octicons-rocket-16: Running a release -A pinned issue tracking the next release should be filed like -<https://github.com/iree-org/iree/issues/18380>. Developers authoring patches -that include major or breaking changes should coordinate merge timing and -contribute release notes on those issues. +Developers authoring patches that include major or breaking changes should +coordinate merge timing and contribute release notes on the pinned issue that +tracks the next release. A pinned issue tracking the release should be filed, +based on +[release-tracker-template.md](https://github.com/iree-org/iree/blob/main/docs/website/docs/developers/general/release-tracker-template.md): +copy it, replace the placeholders, and file it with the GitHub CLI: + +```bash +cp docs/website/docs/developers/general/release-tracker-template.md /tmp/release-tracker.md +# Fill in the version, release date and previous release in /tmp/release-tracker.md. +gh issue create --repo iree-org/iree \ + --title "Release Tracker vX.Y.Z - (YYYY-MM-DD)" \ + --body-file /tmp/release-tracker.md +gh issue pin <issue number> --repo iree-org/iree +``` + +Until the release, watch for major or breaking changes and decide whether to +batch them with this release or defer them until the next one. ### :material-check-all: Picking a candidate to promote @@ -223,44 +237,113 @@ the proposal and solicit feedback. People may point out known regressions or request that some feature make the cut. +### :octicons-note-16: Compiling release notes + +Release notes are collected on the tracking issue, where either contributors add +announcements and notable changes as they land, or they are collected on release +day. + +Generate the "New contributors" list and the full changelog for the range from +the previous stable release to the candidate, e.g. with the "Generate release +notes" button when drafting a GitHub release, or: + +```bash +gh api repos/iree-org/iree/releases/generate-notes \ + -f tag_name=v3.12.0 \ + -f target_commitish=iree-3.12.0rc20260917 \ + -f previous_tag_name=v3.11.0 --jq .body +``` + +The "Release notes" section of the +[release tracker template](https://github.com/iree-org/iree/blob/main/docs/website/docs/developers/general/release-tracker-template.md) +has the expected structure. + +The header of the release notes lists the VMFB bytecode version and the HAL +module version, and whether they changed since the previous release: + +* VMFB bytecode version: `IREE_VM_BYTECODE_VERSION_MAJOR` and + `IREE_VM_BYTECODE_VERSION_MINOR`. +* HAL module version: `IREE_HAL_MODULE_VERSION_LATEST`. + ### :octicons-package-dependents-16: Promoting a candidate to stable 1. (Authorized users only) Push to PyPI using - [pypi_deploy.sh](https://github.com/iree-org/iree/blob/main//build_tools/python_deploy/pypi_deploy.sh) + [pypi_deploy.sh](https://github.com/iree-org/iree/blob/main//build_tools/python_deploy/pypi_deploy.sh). + The script is a dry run by default. Check its output, then pass + `--publish` to upload. Keep the whl folder for upload to GitHub. 2. Create a new release on GitHub: - * Set the tag to be created and select a target commit. For example, if the - candidate release was tagged `iree-3.1.0rc20241119` at commit `3ed07da`, - set the new release tag `v3.1.0` and use the same commit. + * Create a new GitHub draft release (via the WebUI or CLI). Set the tag to + be created and select a target commit. For example, if the candidate + release was tagged `iree-3.1.0rc20241119` at commit `3ed07da`, set the + new release tag `v3.1.0` and use the same commit. GitHub creates the + tag when the release is published, so nothing needs to be pushed + beforehand.  - If the commit does not appear in the list, create and push a tag - manually: + The target picker only lists recent commits. If the candidate's commit + does not appear there, create and push the tag yourself, then select it + as an existing tag: ```bash - git checkout iree-3.1.0rc20250107 - git tag -a v3.1.0 -m "Version 3.1.0 release." - git push upstream v3.1.0 + git tag -a v3.1.0 iree-3.1.0rc20241119 -m "Version 3.1.0 release." + git rev-parse 'v3.1.0^{commit}' # Check that this is the candidate's commit. + git push upstream refs/tags/v3.1.0 # Pushes only the tag. + ``` + + The release can also be created from the command line. `gh` creates + the tag on publish, the same way the web UI does. The command also sets + the title and release notes from the next two steps. The assets are + uploaded to the draft later. You can extract the release notes + `notes.md` from the release tracking issue. + + ```bash + gh issue view <issue number> --repo iree-org/iree --json body --jq .body \ + | tr -d '\r' \ + | awk 'started { print; next } prev == "---" && $0 == "---" { started = 1 } { prev = $0 }' \ + > notes.md + gh release create v3.1.0 --repo iree-org/iree \ + --target "$(git rev-parse 'iree-3.1.0rc20241119^{commit}')" \ + --draft --title "Release v3.1.0" --notes-file notes.md ``` * Set the title to `Release vX.Y.Z`. * Paste the release notes from the release tracking issue. - * Upload the `.whl` files produced by the `pypy_deploy.sh` script (look for - them in your `/tmp/` directory). These have the stable release versions - in them. + * Upload the `iree-dist-*.tar.xz` files of the release candidate and + `.whl` files of `pypi_deploy.sh` to the release draft. - * Download the `iree-dist-.*.tar.xz` files from the candidate release and - upload them to the new stable release. + Download the `iree-dist-*.tar.xz` files from the candidate release. - * Uncheck the option for "pre-release", and check the option for "latest". + ```bash + WHEEL_DIR=/tmp/iree_pypi_wheels.XXXXX + gh release download iree-3.1.0rc20241119 --repo iree-org/iree \ + --pattern 'iree-dist-*' --dir "${WHEEL_DIR}" + gh release upload v3.1.0 --repo iree-org/iree \ + "${WHEEL_DIR}"/*.whl "${WHEEL_DIR}"/iree-dist-*.tar.xz + ``` + + * Publish the release. Uncheck the option for "pre-release", and check the + option for "latest" and hit publish.  -3. Complete any remaining checkbox items on the release tracking issue then + Or via the CLI: + `gh release edit v3.1.0 --repo iree-org/iree --draft=false --latest`. + +3. Release the iree-turbine packages, following + [iree-turbine's release docs](https://github.com/iree-org/iree-turbine/blob/main/docs/infra/releasing.md). + +4. Increment the versions in source code to the next minor release, so that + nightly releases sort after the new stable release (see + [Versioning scheme](./versioning-scheme.md)): set `package-version` to + `X.{Y+1}.0.dev` in both `compiler/version.json` and `runtime/version.json`, + e.g. <https://github.com/iree-org/iree/pull/23866>. + +5. Complete any remaining checkbox items on the release tracking issue then close it and open a new one for the next release. ## :octicons-stack-16: Creating a patch release @@ -273,8 +356,8 @@ <!-- TODO(scotttodd): Does this need a branch, or would just a tag work? --> ```shell - git checkout v3.0.0 - git checkout -b v3.0.1 + git checkout v3.12.0 + git checkout -b v3.12.1 ``` 2. Apply and commit the patches.
diff --git a/docs/website/docs/developers/general/release-tracker-template.md b/docs/website/docs/developers/general/release-tracker-template.md new file mode 100644 index 0000000..5cbc38f --- /dev/null +++ b/docs/website/docs/developers/general/release-tracker-template.md
@@ -0,0 +1,66 @@ +<!-- markdownlint-disable-file line-length --> + +## Overview + +> [!IMPORTANT] +> The X.YY.Z release is scheduled for YYYY-MM-DD. + +Previous stable release: [vA.BB.C](https://github.com/iree-org/iree/releases/tag/vA.BB.C). +The process follows <https://iree.dev/developers/general/release-management/>. + +## Release checklist + +- [ ] Watch for major/breaking changes and decide to either batch them with this release or defer them until the next release +- [ ] Choose release candidates from nightly releases to promote. The candidates should contain no major regressions and should include all packages, even those marked `experimental` in [`.github/workflows/build_package.yml`](https://github.com/iree-org/iree/blob/main/.github/workflows/build_package.yml) like macOS and Windows packages. + - Chosen: [`iree-X.YY.ZrcYYYYMMDD`](https://github.com/iree-org/iree/releases/tag/iree-X.YY.ZrcYYYYMMDD) at <https://github.com/iree-org/iree/commit/COMMIT> (YYYY-MM-DD). It has packages for all platforms, including macOS and Windows, and no major regressions vs vA.BB.C. +- [ ] Compile release notes + - Changes since the previous release: <https://github.com/iree-org/iree/compare/vA.BB.C...iree-X.YY.ZrcYYYYMMDD> +- [ ] Push IREE packages to PyPI +- [ ] Create a new release on GitHub +- [ ] Push iree-turbine packages to PyPI and create a new release on GitHub +- [ ] Increment version.json files + - `X.YY.Z.dev` -> `X.(YY+1).0.dev` +- [ ] Close this issue and open a new one for the next release + +## Testing instructions + +(Update this once release candidates are selected. Versions should match within each repository.) + +```shell +pip install \ + --find-links https://iree.dev/pip-release-links.html \ + iree-base-compiler==X.YY.ZrcYYYYMMDD \ + iree-base-runtime==X.YY.ZrcYYYYMMDD \ + iree-tools-tf==YYYYMMDD.NNNN \ + iree-tools-tflite==YYYYMMDD.NNNN +``` + +## Release notes + +--- +--- + +# IREE vX.YY.Z Release Notes + +**Release Candidate:** `iree-X.YY.ZrcYYYYMMDD` +**PRs:** k since vA.BB.C +**VMFB Bytecode Version:** N.M ([un]changed from vA.BB.C) +**HAL Module Version:** O.P ([un]changed from vA.BB.C) + +## Breaking changes + +* **ITEM** DESCRIPTION <https://github.com/iree-org/iree/pull/NNNNN> + +## Notable changes + +### Compiler + +### Runtime + +### Developer tools + +## New contributors + +## Full changelog + +**Commit history**: <https://github.com/iree-org/iree/compare/vA.BB.C...vX.YY.Z>