diff --git a/docs/development/releasing.md b/docs/development/releasing.md new file mode 100644 index 0000000000..d7fef50ee5 --- /dev/null +++ b/docs/development/releasing.md @@ -0,0 +1,70 @@ +# Releasing + +How to cut a dimos release. + +Throughout this document, replace `X.Y.Z` with the version you are releasing (e.g. `0.0.13`). + +## 1. Preparing for a release + +1. Check for an existing `release/*` branch on the remote (`git ls-remote --heads origin 'release/*'`, or the Branches page). If one is still around from a previous release, complete section 3 for that branch before continuing. +2. Bump the version on `main`. `uv version --bump patch` (or `minor` / `major`). Open a PR, squash-merge. +3. Create the temporary release branch from the version-bump commit: + + ```bash + git fetch origin + git checkout -b release/X.Y.Z origin/main + git push -u origin release/X.Y.Z + ``` + +4. Create a backport label for this release. Repo → Issues → Labels → New label, named `backport release/X.Y.Z`. (Or `gh label create "backport release/X.Y.Z" --repo dimensionalOS/dimos`.) The backport bot only runs when this label exists. +5. To backport a fix from `main`: add the `backport release/X.Y.Z` label to a PR targeting `main` (before or after merging). The backport bot will open a cherry-pick PR onto the release branch; review it and squash-merge. + +## 2. Creating the release + +1. Run the full test suite locally on the release branch. + + ```bash + uv run pytest -m 'not tool' --error-for-skips + ``` + +2. [Run](https://docs.github.com/en/actions/how-tos/manage-workflow-runs/manually-run-a-workflow#running-a-workflow) the `release` workflow on the `release/X.Y.Z` branch. +3. Monitor the CI run. When it reaches the publish-pypi step, you'll need other team members to approve the release. +4. After completion, a merge-back PR will have been created. Find the PR titled `Merge release/X.Y.Z back to main` and merge-commit. + + > **Warning** — pick **"Create a merge commit"** from the merge-button dropdown. NOT "Squash and merge", NOT "Rebase and merge". Squashing collapses the two-parent topology and the tag stops being reachable from main. + +5. Confirm `vX.Y.Z` shows on https://github.com/dimensionalOS/dimos/releases and on https://pypi.org/project/dimos/. + +## 3. Cleanup + +At this point, you can leave the branch around for potential patch releases, or cleanup immediately (recreating the branch from the tag if needed). + +When ready to cleanup the branch, follow these steps: + +1. Rename the release branch out of the protected `release/*` namespace, then delete it. The `release/*` ruleset blocks direct deletion; renaming takes the branch out of scope. + + Web UI: Repo → Branches → find `release/X.Y.Z` → ⋯ → **Rename branch** → e.g. `archived/X.Y.Z`. Then delete it from the same Branches page. + + Or via `gh`: + + ```bash + gh api -X POST "repos/dimensionalOS/dimos/branches/release/X.Y.Z/rename" -f new_name=archived/X.Y.Z + git push origin --delete archived/X.Y.Z + ``` + +2. Delete the `backport release/X.Y.Z` label from the Labels page (or `gh label delete "backport release/X.Y.Z"`). + +## Patch fix on a released version + +When you need to ship a patch fix: + +1. If the release branch has already been deleted, re-cut from the tag and recreate the label (step 1.4): + + ```bash + git fetch origin --tags + git checkout -b release/X.Y.Z vX.Y.Z + git push -u origin release/X.Y.Z + ``` + +2. Apply a patch version bump: `uv version --bump patch` +3. Apply any fixes and follow sections 2 and 3 as before. diff --git a/pyproject.toml b/pyproject.toml index ece6134bf4..aa77708fb3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -384,6 +384,7 @@ tests = [ "pytest-timeout==2.4.0", "pytest-xdist>=3.5.0", "pytest-cov>=5.0", + "pytest-error-for-skips>=2.0.2", "coverage>=7.0", "requests-mock==1.12.1", diff --git a/uv.lock b/uv.lock index 144a1db36d..b7bf2a8386 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 2 +revision = 3 requires-python = ">=3.10" resolution-markers = [ "python_full_version >= '3.14' and platform_machine == 'x86_64' and sys_platform == 'darwin'", @@ -40,7 +40,7 @@ resolution-markers = [ ] [options] -exclude-newer = "2026-05-09T15:39:50.162457Z" +exclude-newer = "2026-05-15T14:23:08.518016Z" exclude-newer-span = "P7D" [manifest] @@ -2285,6 +2285,7 @@ tests = [ { name = "pytest-asyncio" }, { name = "pytest-cov" }, { name = "pytest-env" }, + { name = "pytest-error-for-skips" }, { name = "pytest-mock" }, { name = "pytest-timeout" }, { name = "pytest-xdist" }, @@ -2326,6 +2327,7 @@ tests-self-hosted = [ { name = "pytest-asyncio" }, { name = "pytest-cov" }, { name = "pytest-env" }, + { name = "pytest-error-for-skips" }, { name = "pytest-mock" }, { name = "pytest-timeout" }, { name = "pytest-xdist" }, @@ -2571,6 +2573,7 @@ tests = [ { name = "pytest-asyncio", specifier = "==0.26.0" }, { name = "pytest-cov", specifier = ">=5.0" }, { name = "pytest-env", specifier = "==1.1.5" }, + { name = "pytest-error-for-skips", specifier = ">=2.0.2" }, { name = "pytest-mock", specifier = "==3.15.0" }, { name = "pytest-timeout", specifier = "==2.4.0" }, { name = "pytest-xdist", specifier = ">=3.5.0" }, @@ -2614,6 +2617,7 @@ tests-self-hosted = [ { name = "pytest-asyncio", specifier = "==0.26.0" }, { name = "pytest-cov", specifier = ">=5.0" }, { name = "pytest-env", specifier = "==1.1.5" }, + { name = "pytest-error-for-skips", specifier = ">=2.0.2" }, { name = "pytest-mock", specifier = "==3.15.0" }, { name = "pytest-timeout", specifier = "==2.4.0" }, { name = "pytest-xdist", specifier = ">=3.5.0" }, @@ -8671,6 +8675,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/de/b8/87cfb16045c9d4092cfcf526135d73b88101aac83bc1adcf82dfb5fd3833/pytest_env-1.1.5-py3-none-any.whl", hash = "sha256:ce90cf8772878515c24b31cd97c7fa1f4481cd68d588419fd45f10ecaee6bc30", size = 6141, upload-time = "2024-09-17T22:39:16.942Z" }, ] +[[package]] +name = "pytest-error-for-skips" +version = "2.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5a/96/3d77e3446430168df51af2033cb24675301b29c474b64a10c1abc5f2577a/pytest-error-for-skips-2.0.2.tar.gz", hash = "sha256:d4d0c89036a4105cabbc5e488c4078756c29cc2e5805b0694c3d9e49e52c0d11", size = 3612, upload-time = "2019-12-19T20:31:17.401Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/0b/1408ee0604b2cdb7f095149751809403622abd6ebec2566db88ad8314de1/pytest_error_for_skips-2.0.2-py3-none-any.whl", hash = "sha256:ccc4029c04f1737aa81445c5f3599247f8a64d279741b4a66bf003782fa00492", size = 3965, upload-time = "2019-12-19T20:31:15.715Z" }, +] + [[package]] name = "pytest-mock" version = "3.15.0" @@ -10717,19 +10733,12 @@ name = "triton" version = "3.6.0" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/44/ba/b1b04f4b291a3205d95ebd24465de0e5bf010a2df27a4e58a9b5f039d8f2/triton-3.6.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6c723cfb12f6842a0ae94ac307dba7e7a44741d720a40cf0e270ed4a4e3be781", size = 175972180, upload-time = "2026-01-20T16:15:53.664Z" }, { url = "https://files.pythonhosted.org/packages/8c/f7/f1c9d3424ab199ac53c2da567b859bcddbb9c9e7154805119f8bd95ec36f/triton-3.6.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a6550fae429e0667e397e5de64b332d1e5695b73650ee75a6146e2e902770bea", size = 188105201, upload-time = "2026-01-20T16:00:29.272Z" }, - { url = "https://files.pythonhosted.org/packages/0f/2c/96f92f3c60387e14cc45aed49487f3486f89ea27106c1b1376913c62abe4/triton-3.6.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49df5ef37379c0c2b5c0012286f80174fcf0e073e5ade1ca9a86c36814553651", size = 176081190, upload-time = "2026-01-20T16:16:00.523Z" }, { url = "https://files.pythonhosted.org/packages/e0/12/b05ba554d2c623bffa59922b94b0775673de251f468a9609bc9e45de95e9/triton-3.6.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8e323d608e3a9bfcc2d9efcc90ceefb764a82b99dea12a86d643c72539ad5d3", size = 188214640, upload-time = "2026-01-20T16:00:35.869Z" }, - { url = "https://files.pythonhosted.org/packages/17/5d/08201db32823bdf77a0e2b9039540080b2e5c23a20706ddba942924ebcd6/triton-3.6.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:374f52c11a711fd062b4bfbb201fd9ac0a5febd28a96fb41b4a0f51dde3157f4", size = 176128243, upload-time = "2026-01-20T16:16:07.857Z" }, { url = "https://files.pythonhosted.org/packages/ab/a8/cdf8b3e4c98132f965f88c2313a4b493266832ad47fb52f23d14d4f86bb5/triton-3.6.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74caf5e34b66d9f3a429af689c1c7128daba1d8208df60e81106b115c00d6fca", size = 188266850, upload-time = "2026-01-20T16:00:43.041Z" }, - { url = "https://files.pythonhosted.org/packages/3c/12/34d71b350e89a204c2c7777a9bba0dcf2f19a5bfdd70b57c4dbc5ffd7154/triton-3.6.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:448e02fe6dc898e9e5aa89cf0ee5c371e99df5aa5e8ad976a80b93334f3494fd", size = 176133521, upload-time = "2026-01-20T16:16:13.321Z" }, { url = "https://files.pythonhosted.org/packages/f9/0b/37d991d8c130ce81a8728ae3c25b6e60935838e9be1b58791f5997b24a54/triton-3.6.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10c7f76c6e72d2ef08df639e3d0d30729112f47a56b0c81672edc05ee5116ac9", size = 188289450, upload-time = "2026-01-20T16:00:49.136Z" }, - { url = "https://files.pythonhosted.org/packages/ce/4e/41b0c8033b503fd3cfcd12392cdd256945026a91ff02452bef40ec34bee7/triton-3.6.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1722e172d34e32abc3eb7711d0025bb69d7959ebea84e3b7f7a341cd7ed694d6", size = 176276087, upload-time = "2026-01-20T16:16:18.989Z" }, { url = "https://files.pythonhosted.org/packages/35/f8/9c66bfc55361ec6d0e4040a0337fb5924ceb23de4648b8a81ae9d33b2b38/triton-3.6.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d002e07d7180fd65e622134fbd980c9a3d4211fb85224b56a0a0efbd422ab72f", size = 188400296, upload-time = "2026-01-20T16:00:56.042Z" }, - { url = "https://files.pythonhosted.org/packages/49/55/5ecf0dcaa0f2fbbd4420f7ef227ee3cb172e91e5fede9d0ecaddc43363b4/triton-3.6.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef5523241e7d1abca00f1d240949eebdd7c673b005edbbce0aca95b8191f1d43", size = 176138577, upload-time = "2026-01-20T16:16:25.426Z" }, { url = "https://files.pythonhosted.org/packages/df/3d/9e7eee57b37c80cec63322c0231bb6da3cfe535a91d7a4d64896fcb89357/triton-3.6.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a17a5d5985f0ac494ed8a8e54568f092f7057ef60e1b0fa09d3fd1512064e803", size = 188273063, upload-time = "2026-01-20T16:01:07.278Z" }, - { url = "https://files.pythonhosted.org/packages/48/db/56ee649cab5eaff4757541325aca81f52d02d4a7cd3506776cad2451e060/triton-3.6.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b3a97e8ed304dfa9bd23bb41ca04cdf6b2e617d5e782a8653d616037a5d537d", size = 176274804, upload-time = "2026-01-20T16:16:31.528Z" }, { url = "https://files.pythonhosted.org/packages/f6/56/6113c23ff46c00aae423333eb58b3e60bdfe9179d542781955a5e1514cb3/triton-3.6.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46bd1c1af4b6704e554cad2eeb3b0a6513a980d470ccfa63189737340c7746a7", size = 188397994, upload-time = "2026-01-20T16:01:14.236Z" }, ]