name: Release run-name: Release (${{ inputs.release-type }}) on: workflow_dispatch: inputs: release-type: description: "What kind of release is this?" type: choice options: - alpha - rc - final required: true publish-crates: description: "Publish crates (ignored for non-alpha release)" type: boolean required: false default: true concurrency: group: ${{ github.ref_name }} cancel-in-progress: true defaults: run: shell: bash --noprofile --norc -euo pipefail {0} # wants to push commits and create a PR permissions: contents: write # Allow pushing commits pull-requests: write # Allow creating PRs packages: read # Allow reading packages id-token: "write" jobs: # Re-entrancy: # - `version` is re-entrant because it doesn't commit/create PR if the version doesn't change, # and the version doesn't change if we're already on the final version specified by the branch name. # - `update-docs` is re-entrant because it overwrites history of the `gh-pages` branch, so any # previous partial update will just be overwritten by the next successful run. # - `publish-crates` is re-entrant because the `crates.py` script correctly handles publish failures # by first checking if a crate has already been published before attempting to publish it. # - `build-and-publish-wheels` is re-entrant because all the uploaded artifacts will be overwritten # by any subsequent runs, and the final upload to PyPI has the `--skip-existing` flag, which ignores # any wheels already uploaded. # - `build-and-publish-web` is re-entrant for the same reason as `build-and-publish-wheels`, # except that uploads are done to GCS instead of PyPI. checks: timeout-minutes: 60 name: "Checks" runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 - name: Setup Python uses: actions/setup-python@v5 with: python-version: 3.11 - name: Check links for `?speculative-link` # This checks that we have no links with `?speculative-link` in its query params. # We use those markers to get our link checker to ignore links to unreleased docs. # # NOTE: For alpha releases, we won't fully publish all our docs, # so we skip the check here, because we won't be able to # remove the markers yet. run: | if [ ${{ inputs.release-type }} != "alpha" ]; then python3 scripts/ci/check_speculative_links.py fi # NOTE: When updating this job, also remember to update `post-release-version-bump`. version: name: "Versioning" runs-on: ubuntu-latest outputs: previous: ${{ steps.versioning.outputs.previous }} current: ${{ steps.versioning.outputs.current }} final: ${{ steps.versioning.outputs.final }} git_tag: ${{ steps.versioning.outputs.git_tag }} # will be set to `github.sha` if the pull request already exists # this is the last (and not merge) commit in the release branch release-commit: ${{ steps.commit.outputs.version_bump_commit_sha || github.sha }} steps: - name: Checkout repository uses: actions/checkout@v4 with: fetch-depth: 0 token: ${{ secrets.RERUN_BOT_TOKEN }} - uses: actions/setup-node@v4 with: node-version: "22.x" - uses: prefix-dev/setup-pixi@v0.10.0 with: pixi-version: v0.71.3 - name: Update crate versions id: versioning run: | echo Check that the release version matches expected format… pixi run python scripts/ci/crates.py check-git-branch-name echo Parse the release version from the branch name… # `prepare-release-0.8.1-alpha.N` -> `0.8.1` release_version=$(pixi run python scripts/ci/crates.py get-version --from git --finalize) echo "release_version: $release_version" echo Store version before the update, so we can later detect if it changed… previous=$(pixi run python scripts/ci/crates.py get-version) echo If the version minus prerelease/build metadata is not the same as the release version, then update it.… if [ $(pixi run python scripts/ci/crates.py get-version --finalize) != $release_version ]; then pixi run python scripts/ci/crates.py version --exact $release_version fi echo If this is an 'rc', additionally set add '-rc.N'. This will also bump the 'N' if '-rc.N' is already set… if [ ${{ inputs.release-type }} = "rc" ]; then pixi run python scripts/ci/crates.py version --bump prerelease --pre-id=rc fi echo If this is an 'alpha', set the version to whatever is in the git branch name.… if [ ${{ inputs.release-type }} = "alpha" ]; then pixi run python scripts/ci/crates.py version --exact $(pixi run python scripts/ci/crates.py get-version --from git) fi echo If this is a 'final', set the version to the final release version… if [ ${{ inputs.release-type }} = "final" ]; then pixi run python scripts/ci/crates.py version --exact $release_version fi echo Store version after the update, and the expected "final" release version… current=$(pixi run python scripts/ci/crates.py get-version) final=$(pixi run python scripts/ci/crates.py get-version --finalize) echo Output everything for use in other steps… echo "previous=$previous" echo "current=$current" echo "final=$final" echo "previous=$previous" >> "$GITHUB_OUTPUT" echo "current=$current" >> "$GITHUB_OUTPUT" echo "final=$final" >> "$GITHUB_OUTPUT" # Pick what version we use for creating a github tag. if [ ${{ inputs.release-type }} = "final" ]; then git_tag=$final else git_tag=$current fi # Verify that it wasn't created yet. if [ $(git tag -l "$git_tag") ]; then echo "Error: Version tag $git_tag already exists!" exit 1 fi echo "git_tag=$git_tag" >> "$GITHUB_OUTPUT" - name: Update rerun_py & rerun_c version run: | pixi run python scripts/ci/update_rerun_py_and_c_version.py "${{ steps.versioning.outputs.current }}" - name: Update rerun_notebook package version run: | pixi run python scripts/ci/update_rerun_notebook_version.py "${{ steps.versioning.outputs.current }}" - name: Update JS package versions run: | pixi run node rerun_js/scripts/version.mjs "${{ steps.versioning.outputs.current }}" - run: pixi run toml-fmt # We must re-sync uv after bumping the version - run: pixi run uv lock - name: Commit new version id: commit if: steps.versioning.outputs.previous != steps.versioning.outputs.current run: | git pull git config --global user.name "rerun-bot" git config --global user.email "bot@rerun.io" git commit -am "Bump versions to ${{ steps.versioning.outputs.current }}" git push echo "version_bump_commit_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" - name: Create pull request env: GH_TOKEN: ${{ secrets.RERUN_BOT_TOKEN }} run: | pr=$(gh pr view --json headRefName 2>/dev/null || echo "{}") if echo "$pr" | jq -e '.headRefName' >/dev/null 2>&1; then echo "PR already exists" exit 0 fi echo "PR does not exist, creating…" cat <<'EOF' > pr-body.txt ### Next steps - Test the release - For alpha releases: - [ ] If a GH release should be published for this alpha (give extra love to alphas deployed to Rerun Hub or when external testing is required): - [ ] Create the GitHub release manually from the UI - [ ] Stretch goal: generate a raw changelog and add it to the GH release with this disclaimer: **DISCLAIMER**: This is an unreviewed, automatically generated changelog. We only provide fully reviewed changelogs for final releases. - [ ] Merge or close the release PR - **IFF** the release job succeeds and the link checker is happy, you may merge the PR. - Otherwise, close the PR without merging. Cherrypick any interesting commit to `main`, but *not* the version bump one (it would introduce bad links). - For non-alpha releases: - For any added commits, run the release workflow in 'rc' mode again - After testing, _ensure that this PR is mergeable to `main`_, then run the release workflow in 'final' mode - Once the final release workflow finishes it will create a GitHub release for you. Then: - [ ] Sanity check the build artifacts: - [ ] pip install: does it install and run? - [ ] cargo install of cli tool: does it install and run? - [ ] C++ SDK zip: does it contain rerun_c for all platforms? - [ ] Web Viewer: does it run and connect to Rerun Hub servers? - [ ] Edit and publish the GitHub release: - Do NOT create a GitHub release draft yourself! Let the release job do it. - Populate the release with the changelog and a nice header video/picture - Make sure `Set as latest release` is checked - Click `Publish release` - Once published, the release assets will sync to it automatically. - [ ] Update the [google colab notebooks](https://colab.research.google.com/drive/1R9I7s4o6wydQC_zkybqaSRFTtlEaked_) to install this version and re-execute the notebook. - [ ] Verify the auto-triggered [gradio-rerun-viewer release workflow](https://github.com/rerun-io/gradio-rerun-viewer/actions/workflows/auto_release_on_rerun.yml) succeeded (dispatched from `on_gh_release.yml` for final releases) - [ ] Create a new branch from the prepare-release branch, rebase that one on main and sync it to reality so it can be merged. - [ ] (A few hours later) Check on the [conda feedstock PR](https://github.com/conda-forge/rerun-sdk-feedstock/pulls) (ping Antoine and/or Nick if necessary) - [ ] Tests - [ ] Windows - [ ] Linux - [ ] MacOS EOF gh pr create \ --base main \ --head $(git branch --show-current) \ --title "Release ${{ (inputs.release-type == 'alpha' && steps.versioning.outputs.current) || steps.versioning.outputs.final }}" \ --label "⛴ release" \ --label "exclude from changelog" \ --fill \ --body-file pr-body.txt uv-lock-check: name: "Check uv.lock" needs: [version] runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 with: ref: ${{ needs.version.outputs.release-commit }} - uses: prefix-dev/setup-pixi@v0.10.0 with: pixi-version: v0.71.3 - name: Check uv.lock is up-to-date run: pixi run uv-lock-check update-docs: name: "Update Docs" needs: [version, publish-web] uses: ./.github/workflows/reusable_deploy_docs.yml with: CONCURRENCY: ${{ github.ref_name }} PY_DOCS_VERSION_NAME: ${{ inputs.release-type == 'final' && needs.version.outputs.final || 'dev' }} CPP_DOCS_VERSION_NAME: ${{ inputs.release-type == 'final' && 'stable' || 'dev' }} JS_DOCS_VERSION_NAME: ${{ inputs.release-type == 'final' && needs.version.outputs.final || 'dev' }} MARKDOWN_DOCS_VERSION_NAME: ${{ inputs.release-type == 'final' && needs.version.outputs.final || 'dev' }} RELEASE_COMMIT: ${{ needs.version.outputs.release-commit }} RELEASE_VERSION: ${{ needs.version.outputs.final }} UPDATE_LATEST: ${{ inputs.release-type == 'final' }} secrets: inherit publish-crates: name: "Publish Crates" if: inputs.release-type != 'alpha' || inputs.publish-crates needs: [version, uv-lock-check] uses: ./.github/workflows/reusable_release_crates.yml with: CONCURRENCY: ${{ github.ref_name }} RELEASE_COMMIT: ${{ needs.version.outputs.release-commit }} secrets: inherit publish-rerun_c: name: "Build and Publish C/C++ SDKs" needs: [version, uv-lock-check] uses: ./.github/workflows/reusable_publish_rerun_c.yml with: release-version: ${{ needs.version.outputs.current }} release-commit: ${{ needs.version.outputs.release-commit }} concurrency: ${{ github.ref_name }} secrets: inherit publish-rerun-cli: name: "Publish rerun-cli" needs: [version, uv-lock-check] uses: ./.github/workflows/reusable_publish_rerun_cli.yml with: release-version: ${{ needs.version.outputs.current }} release-commit: ${{ needs.version.outputs.release-commit }} concurrency: ${{ github.ref_name }} secrets: inherit publish-wheels: name: "Build and Publish Wheels" needs: [version, publish-rerun-cli] uses: ./.github/workflows/reusable_publish_wheels.yml with: release-version: ${{ needs.version.outputs.current }} concurrency: ${{ github.ref_name }} release-commit: ${{ needs.version.outputs.release-commit }} secrets: inherit web-test: name: "Web tests" needs: [version] uses: ./.github/workflows/reusable_web_test.yml with: CONCURRENCY: ${{ github.ref_name }} REF: ${{ needs.version.outputs.release-commit }} secrets: inherit publish-web: name: "Build and Publish Web" needs: [version, publish-wheels, web-test] uses: ./.github/workflows/reusable_publish_web.yml with: release-version: ${{ needs.version.outputs.current }} release-commit: ${{ needs.version.outputs.release-commit }} concurrency: ${{ github.ref_name }} wheel-artifact-name: linux-x64-wheel update-latest: ${{ inputs.release-type == 'final' }} secrets: inherit publish-js: name: "Publish JS" needs: [version, uv-lock-check] uses: ./.github/workflows/reusable_publish_js.yml with: release-version: ${{ needs.version.outputs.current }} release-commit: ${{ needs.version.outputs.release-commit }} concurrency: ${{ github.ref_name }} secrets: inherit # Force-pushes `latest` and `docs-latest` to the contents of the release branch. # The push to `docs-latest` also triggers a re-deploy of `rerun.io`. update-latest-branch: name: "Update Latest Branch" if: inputs.release-type == 'final' && !cancelled() && !failure() needs: [ version, update-docs, publish-crates, publish-wheels, publish-web, publish-rerun_c, publish-rerun-cli, publish-js, ] runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 with: fetch-depth: 0 token: ${{ secrets.RERUN_BOT_TOKEN }} ref: ${{ needs.version.outputs.release-commit }} - name: Update latest branch run: | git config --global user.name "rerun-bot" git config --global user.email "bot@rerun.io" git fetch git checkout ${{ github.ref_name }} git push --force origin refs/heads/${{ github.ref_name }}:refs/heads/latest git push --force origin refs/heads/${{ github.ref_name }}:refs/heads/docs-latest tag-release: name: "Tag Release" if: ${{ !cancelled() && !failure() }} needs: [ version, update-docs, publish-crates, publish-wheels, publish-web, publish-rerun_c, publish-rerun-cli, publish-js, ] runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 with: fetch-depth: 0 token: ${{ secrets.RERUN_BOT_TOKEN }} - name: Release tag env: GH_TOKEN: ${{ secrets.RERUN_BOT_TOKEN }} run: | version="${{ needs.version.outputs.git_tag }}" commit="${{ needs.version.outputs.release-commit }}" git tag $version $commit git push origin $version - name: Create GH release if: inputs.release-type == 'rc' || inputs.release-type == 'final' env: GH_TOKEN: ${{ secrets.RERUN_BOT_TOKEN }} run: | version="${{ needs.version.outputs.git_tag }}" commit="${{ needs.version.outputs.release-commit }}" if [ ${{ inputs.release-type }} = "final" ]; then pre_arg="" else pre_arg="--prerelease" fi gh release create $version --verify-tag --draft --title $version $pre_arg - name: Create comment if: inputs.release-type == 'rc' || inputs.release-type == 'final' # GitHub API is very unreliable. We don't want to fail the entire workflow just because we couldn't post a comment. continue-on-error: true env: GH_TOKEN: ${{ secrets.RERUN_BOT_TOKEN }} run: | pr_number=$(gh pr view --json number | jq '.number') version="${{ needs.version.outputs.final }}" cat < comment-body.txt The GitHub release draft for $version can be found in the [release list](https://github.com/rerun-io/rerun/releases). Add a description, changelog, and a nice header video/picture, then click 'Publish release'. EOF gh pr comment $pr_number --body-file comment-body.txt # Bump versions to next minor+alpha after the release has finished, # so that the release PR can be merged. post-release-version-bump: name: "Post-Release Version Bump" # We don't need to bump versions for `rc` releases, because we don't merge those. if: (inputs.release-type == 'alpha' || inputs.release-type == 'final') && !cancelled() && !failure() needs: [ version, update-docs, publish-crates, publish-wheels, publish-web, publish-rerun_c, publish-rerun-cli, publish-js, ] runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 with: fetch-depth: 0 token: ${{ secrets.RERUN_BOT_TOKEN }} - uses: actions/setup-node@v4 with: node-version: "22.x" - uses: prefix-dev/setup-pixi@v0.10.0 with: pixi-version: v0.71.3 - name: git config run: | git config --global user.name "rerun-bot" git config --global user.email "bot@rerun.io" git checkout ${{ github.ref_name }} git pull --rebase - name: Update crate versions id: crates run: | pixi run python scripts/ci/crates.py version --bump auto version="$(pixi run python scripts/ci/crates.py get-version)" echo "version=$version" >> "$GITHUB_OUTPUT" - name: Update rerun_notebook package version run: | pixi run python scripts/ci/update_rerun_notebook_version.py "${{ steps.crates.outputs.version }}" - name: Update JS package versions run: | pixi run node rerun_js/scripts/version.mjs "${{ steps.crates.outputs.version }}" - name: Update rerun_py & rerun_c version run: | pixi run python scripts/ci/update_rerun_py_and_c_version.py "${{ steps.crates.outputs.version }}" - run: pixi run toml-fmt # We must re-sync uv after bumping the version - run: pixi run uv lock - name: Commit new version run: | git commit -am "Bump versions to ${{ steps.crates.outputs.version }}" git push comment-artifact-links: name: "Link to artifacts" if: ${{ !cancelled() && !failure() }} needs: [ version, update-docs, publish-crates, publish-wheels, publish-web, publish-rerun_c, publish-rerun-cli, publish-js, ] runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 with: fetch-depth: 0 token: ${{ secrets.RERUN_BOT_TOKEN }} - name: Create comment # GitHub API is very unreliable. We don't want to fail the entire workflow just because we couldn't post a comment. continue-on-error: true env: GH_TOKEN: ${{ secrets.RERUN_BOT_TOKEN }} run: | pr_number=$(gh pr view --json number | jq '.number') echo "pr_number: $pr_number" short_commit_hash=$(echo ${{ needs.version.outputs.release-commit }} | cut -c1-7) if [ ${{ inputs.release-type }} = "final" ]; then web_app_link="https://rerun.io/viewer/version/${{ needs.version.outputs.final }}" rerun_io_docs_link="https://rerun.io/docs" py_docs_link="https://ref.rerun.io/docs/python/${{ needs.version.outputs.final }}" else web_app_link="https://rerun.io/viewer/commit/$short_commit_hash" rerun_io_docs_link="https://rerun.io/preview/$short_commit_hash/docs" py_docs_link="https://ref.rerun.io/docs/python/dev" fi wheels_link="https://pypi.org/project/rerun-sdk/${{ needs.version.outputs.current }}" crates_link="https://crates.io/crates/rerun/${{ needs.version.outputs.current }}" npm_link="https://www.npmjs.com/package/@rerun-io/web-viewer/v/${{ needs.version.outputs.current }}" rs_docs_link="https://docs.rs/rerun/${{ needs.version.outputs.current }}" cpp_sdk_zip_link="https://build.rerun.io/commit/$short_commit_hash/rerun_cpp_sdk.zip" pip_install="pip install rerun-sdk==${{ needs.version.outputs.current }}" cargo_install="cargo install rerun-cli@${{ needs.version.outputs.current }} --locked" npm_install="npm install @rerun-io/web-viewer@${{ needs.version.outputs.current }}" cat < comment-body.txt Version ${{ needs.version.outputs.current }} published successfully. | artifact | install | | --------------------------------- | -------------- | | [web app]($web_app_link) | | | [wheels]($wheels_link) | $pip_install | | [crates]($crates_link) | $cargo_install | | [npm]($npm_link) | $npm_install | | [docs]($rerun_io_docs_link) | | | [py docs]($py_docs_link) | | | [rs docs]($rs_docs_link) | | | [cpp_sdk zip]($cpp_sdk_zip_link) | | EOF gh pr comment $pr_number --body-file comment-body.txt