From e485cbb6a500ac1cc52fff83fcf4820907ef36ec Mon Sep 17 00:00:00 2001 From: Julian Hofer Date: Tue, 3 Sep 2024 11:28:40 +0200 Subject: [PATCH 1/3] feat: add release script This script automates the release docs and should work on all operating systems and shells --- docs/dev-release-steps.md | 31 ----------- pixi.toml | 1 + scripts/release.py | 106 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+), 31 deletions(-) delete mode 100644 docs/dev-release-steps.md create mode 100644 scripts/release.py diff --git a/docs/dev-release-steps.md b/docs/dev-release-steps.md deleted file mode 100644 index 58674314c..000000000 --- a/docs/dev-release-steps.md +++ /dev/null @@ -1,31 +0,0 @@ -# Making a release of pixi - -## Prep -- Make sure main is up-to-date and ci passes: [![build-badge](https://img.shields.io/github/actions/workflow/status/prefix-dev/pixi/rust.yml?style=flat-square&branch=main)](https://github.com/prefix-dev/pixi/actions/workflows/rust.yml?query=branch%3Amain+) -- Set the variable `export RELEASE_VERSION=X.Y.Z` in your shell -- Make a new branch for the release: `git checkout main && git pull upstream main && git checkout -b bump/prepare-v$RELEASE_VERSION` -- Bump all versions: `pixi run bump` -- Update the changelog: `pixi run bump-changelog` - - Don't forget to update the "Highlights" section. -- Commit the changes: `git commit -am "chore: version to $RELEASE_VERSION"` -- Push the changes: `git push origin` - -## Release prep PR -- Create a PR to check off the change with the peers -- Merge that PR - -## Tagging the release -- Checkout main: `git fetch && git checkout upstream/main` -- Tag the release: `git tag v$RELEASE_VERSION -m "Release $RELEASE_VERSION"` -- Push the tag: `git push upstream v$RELEASE_VERSION` - -## Publishing the release -- After that, update the Release which has CI created for you (after the first build) and add the changelog to the release notes. -- Make sure all the artifacts are there and the CI is green!!! -- Publish the release and make sure it is set as latest. - -## Test the release using the install script: -- `curl -fsSL https://pixi.sh/install.sh | bash` or `iwr -useb https://pixi.sh/install.ps1 | iex` -- `pixi --version` should show the new version - -DONE! diff --git a/pixi.toml b/pixi.toml index 1c16a176f..52a6f65bf 100644 --- a/pixi.toml +++ b/pixi.toml @@ -23,6 +23,7 @@ run-all-examples = "python ./tests/run_all_examples.py" run-all-examples-dev = "python ./tests/run_all_examples.py --pixi-exec ./target/debug/pixi" test = "cargo test" test-all = "cargo test --all-features" +release = "python scripts/release.py" [feature.pytest.dependencies] mypy = ">=1.11,<1.12" diff --git a/scripts/release.py b/scripts/release.py new file mode 100644 index 000000000..cfeb63ae0 --- /dev/null +++ b/scripts/release.py @@ -0,0 +1,106 @@ +import subprocess +import re +import os + + +def run_command(command: list[str], capture_stdout=False) -> str | None: + print(f"Running command: {' '.join(command)}") + result = subprocess.run( + command, stdout=subprocess.PIPE if capture_stdout else None, stderr=None, text=True + ) + if result.returncode != 0: + print(f"Error running command: {' '.join(command)}") + exit(result.returncode) + if capture_stdout: + return result.stdout.strip() + return None + + +def get_release_version(): + pattern = re.compile(r"^\d+\.\d+\.\d+$") + while True: + release_version = input("Enter the release version (X.Y.Z): ") + if pattern.match(release_version): + return release_version + else: + print( + "Invalid format. Please enter the version in the format X.Y.Z where X, Y, and Z are integers." + ) + + +def main(): + print("# Making a release of pixi") + + # Prep + print("\n## Prep") + input("Make sure main is up-to-date and CI passes. Press Enter to continue...") + + release_version = get_release_version() + os.environ["RELEASE_VERSION"] = release_version + + print("\nCreating a new branch for the release...") + run_command(["git", "checkout", "main"]) + run_command(["git", "pull", "upstream", "main"]) + branch = f"bump/prepare-v{release_version}" + + branch_exists = run_command(["git", "branch", "--list", branch], capture_stdout=True) + if branch_exists: + run_command(["git", "branch", "--delete", branch]) + run_command(["git", "switch", "--create", branch]) + + print("\nBumping all versions...") + run_command(["pixi", "run", "bump"]) + + print("\nUpdating the changelog...") + run_command(["pixi", "run", "bump-changelog"]) + input( + "Don't forget to update the 'Highlights' section in `CHANGELOG.md`. Press Enter to continue..." + ) + + print("\nCommitting the changes...") + run_command(["git", "commit", "-am", f"chore: version to {release_version}"]) + + print("\nPushing the changes...") + run_command(["git", "push", "origin"]) + + # Release prep PR + print("\n## Release prep PR") + input("Create a PR to check off the change with the peers. Press Enter to continue...") + input("Merge that PR. Press Enter to continue...") + + # Tagging the release + print("\n## Tagging the release") + print("\nChecking out main...") + run_command(["git", "fetch"]) + run_command(["git", "checkout", "upstream/main"]) + + print("\nTagging the release...") + run_command(["git", "tag", f"v{release_version}", "-m", f"Release {release_version}"]) + + print("\nPushing the tag...") + run_command(["git", "push", "upstream", f"v{release_version}"]) + + # Publishing the release + print("\n## Publishing the release") + input( + "After that, update the Release which has CI created for you (after the first build) and add the changelog to the release notes. Press Enter to continue..." + ) + input("Make sure all the artifacts are there and the CI is green!!! Press Enter to continue...") + input("Publish the release and make sure it is set as latest. Press Enter to continue...") + + # Test the release using the install script + print("Testing the release using `pixi self-update`...") + run_command(["pixi", "self-update"]) + + version_output = run_command(["pixi", "--version"], capture_stdout=True) + expected_version_output = f"pixi {release_version}" + if version_output == expected_version_output: + print(f"Version check passed: {version_output}") + else: + print(f"Version check failed: expected {expected_version_output}, got {version_output}") + + print("\nDONE!") + + +if __name__ == "__main__": + main() From d2fc97d8efca2875807f6ff7efbe76f657b66ace Mon Sep 17 00:00:00 2001 From: Julian Hofer Date: Tue, 3 Sep 2024 11:34:05 +0200 Subject: [PATCH 2/3] Pre-commit --- pixi.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pixi.toml b/pixi.toml index 52a6f65bf..bf03b57c6 100644 --- a/pixi.toml +++ b/pixi.toml @@ -19,11 +19,11 @@ build = "cargo build --release" bump = "tbump --only-patch $RELEASE_VERSION" install = "cargo install --path . --locked" pypi-proxy = "python ./tests/pypi_proxy.py" +release = "python scripts/release.py" run-all-examples = "python ./tests/run_all_examples.py" run-all-examples-dev = "python ./tests/run_all_examples.py --pixi-exec ./target/debug/pixi" test = "cargo test" test-all = "cargo test --all-features" -release = "python scripts/release.py" [feature.pytest.dependencies] mypy = ">=1.11,<1.12" From 99219ffb516861935801b800cdcd3c8a590be5f8 Mon Sep 17 00:00:00 2001 From: Julian Hofer Date: Tue, 3 Sep 2024 12:17:48 +0200 Subject: [PATCH 3/3] Remove headings --- scripts/release.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/release.py b/scripts/release.py index cfeb63ae0..78d09e076 100644 --- a/scripts/release.py +++ b/scripts/release.py @@ -29,10 +29,10 @@ def get_release_version(): def main(): - print("# Making a release of pixi") + print("Making a release of pixi") # Prep - print("\n## Prep") + print("\nPrep") input("Make sure main is up-to-date and CI passes. Press Enter to continue...") release_version = get_release_version() @@ -64,12 +64,12 @@ def main(): run_command(["git", "push", "origin"]) # Release prep PR - print("\n## Release prep PR") + print("\nRelease prep PR") input("Create a PR to check off the change with the peers. Press Enter to continue...") input("Merge that PR. Press Enter to continue...") # Tagging the release - print("\n## Tagging the release") + print("\nTagging the release") print("\nChecking out main...") run_command(["git", "fetch"]) run_command(["git", "checkout", "upstream/main"]) @@ -81,7 +81,7 @@ def main(): run_command(["git", "push", "upstream", f"v{release_version}"]) # Publishing the release - print("\n## Publishing the release") + print("\nPublishing the release") input( "After that, update the Release which has CI created for you (after the first build) and add the changelog to the release notes. Press Enter to continue..." )