# How Release-Plz Manages Unified Workspace Versions and Immutable Tags in OpenLogi

> Discover how Release-Plz simplifies unified workspace versioning and immutable tags in OpenLogi. Learn to create atomic, workspace-wide releases and automate artifact builds and publishing.

- Repository: [Xuan Zhang/OpenLogi](https://github.com/AprilNEA/OpenLogi)
- Tags: internals
- Published: 2026-08-30

---

**Release-Plz centralizes version bumps in OpenLogi’s Cargo workspace by assigning all crates to a single version group, then creates one immutable Git tag that triggers CI to build artifacts and publish the GitHub Release, ensuring atomic workspace-wide releases.**

OpenLogi is organized as a single Cargo workspace where every crate inherits its version from the root [`Cargo.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/Cargo.toml). By configuring **Release-Plz** with unified versioning rules and delegating release creation to GitHub Actions, the project guarantees that all crates move forward as a unit and that release tags remain immutable once pushed.

## Unified Workspace Versioning via Root Cargo.toml

OpenLogi enforces a single source of truth for versioning through the workspace root. In the root [`Cargo.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/Cargo.toml), the `version` field is defined once, and all member crates reference it via the workspace inheritance feature.

This configuration ensures that bumping the version in one location propagates to every crate in the workspace. When **Release-Plz** calculates the next semantic version based on conventional commits, it updates the root manifest, and all dependent crates automatically inherit the new version number without requiring individual [`Cargo.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/Cargo.toml) edits.

## Version Groups for Synchronized Bumping

The [`release-plz.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/release-plz.toml) configuration file implements synchronized versioning through the `version_group` directive. At line 41, every package is assigned to the same group:

```toml
[[package]]
name = "openlogi"
version_group = "openlogi"
git_tag_enable = true
git_tag_name = "v{{ version }}"
git_release_enable = false

```

By assigning `version_group = "openlogi"` to all workspace members, **Release-Plz** calculates the next version from the highest-bumped crate within the group and applies that single version to the entire workspace. This prevents version drift between crates and ensures that `cargo` resolves dependencies correctly across the workspace.

## Single Immutable Tag Strategy

To maintain tag immutability, only the pseudo-crate named `openlogi` (the top-level package) is permitted to create Git tags. In [`release-plz.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/release-plz.toml), line 31 explicitly disables tagging for individual crates with `git_tag_enable = false`, while line 32 sets `git_release_enable = false` to prevent **Release-Plz** from creating GitHub Releases directly.

The `openlogi` package configuration overrides this with:

- `git_tag_enable = true` – Enables tag creation
- `git_tag_name = "v{{ version }}"` – Defines the immutable tag format
- `git_release_enable = false` – Ensures **Release-Plz** only pushes the tag without creating a release

This design creates the Git tag *before* any CI workflow runs. Because the tag exists prior to the GitHub Release draft, the `target_commitish` cannot be altered later, avoiding the "target_commitish cannot be changed" error that occurs when workflows attempt to modify tags after release creation.

## CI Workflow Integration for Release Publishing

When **Release-Plz** pushes the single workspace tag (e.g., `v1.2.3`), the CI workflow defined in [`.github/workflows/release.yml`](https://github.com/AprilNEA/OpenLogi/blob/main/.github/workflows/release.yml) triggers on the pattern `v*`. The workflow uses `softprops/action-gh-release` to create the GitHub Release as a draft, uploads macOS DMG artifacts built via `cargo xtask package`, and finally publishes the release.

```yaml

# .github/workflows/release.yml (excerpt)

on:
  push:
    tags:
      - 'v*'

jobs:
  build-and-publish:
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v3
      - name: Build DMG
        run: cargo xtask package --target macos
      - name: Create draft release
        uses: softprops/action-gh-release@v1
        with:
          tag_name: ${{ github.ref_name }}
          draft: true
          files: target/*.dmg
      - name: Publish release
        run: gh release edit ${{ github.ref_name }} --draft=false

```

The build orchestration logic resides in [`xtask/src/commands/release.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/xtask/src/commands/release.rs), which implements the `cargo xtask release` command invoked by the CI pipeline. This separation of concerns—**Release-Plz** handling versioning and tagging, while CI handles artifact building and release publication—ensures that the Git tag remains immutable throughout the process.

## Summary

- **Unified versioning**: The root [`Cargo.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/Cargo.toml) sets `version.workspace = true`, propagating a single version to all workspace members.
- **Version groups**: The `version_group = "openlogi"` directive in [`release-plz.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/release-plz.toml) (line 41) synchronizes version bumps across all crates.
- **Single tag**: Only the `openlogi` package creates tags via `git_tag_enable = true`, while other crates have `git_tag_enable = false` (line 31).
- **Immutable tags**: By setting `git_release_enable = false` (line 32), **Release-Plz** creates the tag before CI runs, preventing modifications to `target_commitish`.
- **CI delegation**: The workflow in [`.github/workflows/release.yml`](https://github.com/AprilNEA/OpenLogi/blob/main/.github/workflows/release.yml) reacts to the `v*` tag, builds artifacts using [`xtask/src/commands/release.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/xtask/src/commands/release.rs), and publishes the release via `softprops/action-gh-release`.

## Frequently Asked Questions

### How does OpenLogi ensure all workspace crates share the same version?

OpenLogi uses Cargo's workspace inheritance feature in the root [`Cargo.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/Cargo.toml) combined with **Release-Plz** version groups. By assigning `version_group = "openlogi"` to every package in [`release-plz.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/release-plz.toml), the tool calculates the highest version bump required by any crate and applies that single version to the entire workspace.

### Why does Release-Plz create only one Git tag instead of per-crate tags?

The configuration explicitly disables per-crate tagging with `git_tag_enable = false` for all packages except the root `openlogi` package. This ensures a single tag like `v1.2.3` represents the entire workspace state, preventing tag pollution and ensuring atomic releases where all crates share the identical version.

### What prevents the release workflow from modifying the Git tag after creation?

**Release-Plz** creates the Git tag with `git_tag_enable = true` while having `git_release_enable = false`, meaning the tag exists before the CI workflow in [`.github/workflows/release.yml`](https://github.com/AprilNEA/OpenLogi/blob/main/.github/workflows/release.yml) triggers. Since the tag points to a specific commit SHA before the workflow runs, the `target_commitish` property cannot be changed, avoiding the "target_commitish cannot be changed" error.

### How is the GitHub Release actually created if Release-Plz has git_release_enable set to false?

The release creation is delegated entirely to CI. The workflow in [`.github/workflows/release.yml`](https://github.com/AprilNEA/OpenLogi/blob/main/.github/workflows/release.yml) listens for pushes to tags matching `v*`, then uses `softprops/action-gh-release` to create a draft release, upload DMG artifacts, and finally publish the release. This separation ensures **Release-Plz** handles versioning and tagging while CI handles distribution.