# Marin Release Cycle: Daily Development Builds, Stable Releases, and Canary Testing

> Discover the Marin release cycle: daily development builds, stable releases via Git tags, and canary tests for validated PyPI publication.

- Repository: [The Marin Project/marin](https://github.com/marin-community/marin)
- Tags: release-cycle
- Published: 2026-08-29

---

**Marin employs a hybrid release cycle that combines automated daily development builds at 06:00 UTC, manually triggered stable releases via Git tags or workflow dispatch, and scheduled canary smoke tests to validate artifacts before PyPI publication.**

The marin-community/marin repository orchestrates its release process through GitHub Actions workflows and Python automation scripts. This release cycle supports multiple Python library families with distinct versioning strategies, balancing continuous integration for rapid iteration with explicit gates for production stability.

## Automated Daily Development Builds

Marin produces **daily development builds** automatically through the [`.github/workflows/marin-release-libs-wheels.yaml`](https://github.com/marin-community/marin/blob/main/.github/workflows/marin-release-libs-wheels.yaml) workflow. This workflow triggers on a cron schedule defined at line 10 of the workflow file:

```yaml
on:
  schedule:
    - cron: "0 6 * * *"   # runs every day at 06:00 UTC

```

When this schedule fires, the workflow executes in **development mode**, building all Python library families and bumping development versions. The system calculates the next development version using the `next_development_version` function in [`scripts/ci/package_release.py`](https://github.com/marin-community/marin/blob/main/scripts/ci/package_release.py) (lines 99–110). This function generates versions following the `X.Y.Z.devN` pattern based on the declared version in each package's [`pyproject.toml`](https://github.com/marin-community/marin/blob/main/pyproject.toml) and the latest published version on PyPI. The resulting wheels publish to an internal artifact store rather than PyPI.

## Stable Release Triggers

**Stable releases** require explicit human intervention or version tags. The release workflow supports two primary triggers:

- **Workflow dispatch**: A maintainer navigates to the Actions tab, selects "Marin – Release Packages," and configures the package family, mode (`stable`), and target version.
- **Tag push**: Pushing a Git tag matching a package's `tag_prefix` (for example, `marin-libs-v1.2.3`) automatically triggers the stable release pipeline.

In stable mode, the workflow demands an explicit version supplied via the `--version` parameter. The `resolve_version` function in [`scripts/ci/package_release.py`](https://github.com/marin-community/marin/blob/main/scripts/ci/package_release.py) validates and canonicalizes this version string before the build matrix executes. After successful verification, artifacts publish directly to PyPI.

## Version Resolution Logic

The [`scripts/ci/package_release.py`](https://github.com/marin-community/marin/blob/main/scripts/ci/package_release.py) script implements three distinct versioning strategies depending on the release mode:

- **Development mode**: The `next_development_version` function computes the next `X.Y.Z.devN` suffix by incrementing the patch component of the declared version and appending the build serial number.

- **Stable mode**: The version passes through unchanged after canonicalization, requiring the user to explicitly define the semantic version.

- **Manual mode**: The version string formats as `declared_version+<short_rev>`, where `short_rev` represents the first eight characters of the commit SHA. This produces versions like `1.2.3+abc123de`, enabling quick internal testing without incrementing the public version number.

## Canary Testing and Quality Assurance

Before artifacts reach the main release channel, Marin runs **canary smoke tests** through dedicated workflows such as [`.github/workflows/marin-canary-grug-multislice.yaml`](https://github.com/marin-community/marin/blob/main/.github/workflows/marin-canary-grug-multislice.yaml). These workflows trigger on two cadences:

1. **Weekly scheduled runs** to catch environment drift and dependency issues
2. **Pull request validation** for the corresponding package changes

These canary workflows execute quick sanity checks on newly built wheels, verifying that critical import paths and basic functionality remain intact. This layer of automated testing prevents regressions from reaching either the internal artifact store or PyPI.

## On-Demand and Patch Release Modes

Beyond scheduled automation, Marin supports two manual workflow dispatch modes for specialized scenarios:

**Manual development releases** occur when a maintainer triggers the workflow with `mode=development` through the GitHub UI. This bypasses the daily schedule to produce an immediate development build for a specific package family, useful for testing emergency fixes.

**Patch-only manual releases** activate with `mode=manual`, typically following a PR merge. This mode uses the repository revision (first eight characters of the commit SHA) to construct a version string like `X.Y.Z+<rev>`. This approach facilitates rapid internal deployment and integration testing without publishing a new semantic version to public repositories.

```bash

# Trigger a one-off manual development build for the 'iris' package

gh workflow run marin-release-libs-wheels.yaml \
  -f package=iris \
  -f mode=development

# Trigger a stable release by pushing a version tag

git tag -a marin-libs-v1.2.3 -m "Marin stable release 1.2.3"
git push origin marin-libs-v1.2.3

```

## Summary

- **Daily automation**: The [`marin-release-libs-wheels.yaml`](https://github.com/marin-community/marin/blob/main/marin-release-libs-wheels.yaml) workflow triggers at 06:00 UTC daily to produce development builds with auto-incremented `X.Y.Z.devN` versions.
- **Stable gates**: Production releases require either manual workflow dispatch with explicit versioning or pushing a tag matching the package's `tag_prefix` pattern.
- **Version strategies**: Development mode auto-calculates versions, stable mode uses explicit inputs, and manual mode appends commit SHAs for internal testing.
- **Quality safeguards**: Weekly canary workflows and PR-triggered smoke tests validate wheels before publication to PyPI.
- **Flexible triggers**: Repository maintainers can bypass schedules using `mode=development` for urgent builds or `mode=manual` for patch testing.

## Frequently Asked Questions

### How often does Marin publish development builds?

Marin publishes development builds **every day at 06:00 UTC** through an automated GitHub Actions cron schedule defined in [`.github/workflows/marin-release-libs-wheels.yaml`](https://github.com/marin-community/marin/blob/main/.github/workflows/marin-release-libs-wheels.yaml). Additionally, maintainers can trigger on-demand development builds manually through the workflow dispatch interface when immediate testing is required outside the normal schedule.

### What triggers a stable release in the Marin repository?

Stable releases trigger through **two mechanisms**: pushing a Git tag that matches a package's designated `tag_prefix` (such as `marin-libs-v1.2.3`), or manually dispatching the release workflow via the GitHub Actions UI with `mode=stable`. Both methods require an explicit version number and execute the full build verification matrix before publishing to PyPI.

### How does Marin determine version numbers for development releases?

The `next_development_version` function in [`scripts/ci/package_release.py`](https://github.com/marin-community/marin/blob/main/scripts/ci/package_release.py) (lines 99–110) determines development versions by reading the declared version from [`pyproject.toml`](https://github.com/marin-community/marin/blob/main/pyproject.toml), comparing it against the latest PyPI release, and appending a `.devN` suffix where `N` represents the build serial number. This ensures chronological ordering of daily snapshots while maintaining semantic versioning compatibility.

### What is the difference between manual and stable release modes?

**Stable mode** publishes canonical semantic versions (like `1.2.3`) to PyPI and requires explicit version input. **Manual mode** (activated with `mode=manual`) creates versions formatted as `X.Y.Z+<short_rev>` using the commit SHA, publishing to internal artifact stores for rapid integration testing without incrementing the public version number or triggering PyPI uploads.