UniteLabs
Guides

Deploy a workflow

Bundle a workflow from a workflow-template repo and ship it to the UniteLabs platform with scripts/deploy.py.

In this guide you will use the scripts/deploy.py CLI shipped with the workflow template to bundle one (or every) workflow in your repo and register it with the UniteLabs platform.

Prerequisites

  • A repo cloned (or scaffolded) from the workflow template. Each top-level w*-*/ directory is a standalone workflow package with its own pyproject.toml declaring [project].name, [project].version, and [tool.unitelabs.workflow] metadata.
  • uv (latest) and Python 3.12+.
  • UniteLabs API credentials: BASE_URL, AUTH_URL, CLIENT_ID, CLIENT_SECRET.

Store your credentials

Copy .env.example to .env at the repo root and fill in the four values:

.env
BASE_URL=https://api.<your-tenant>.unitelabs.io/
AUTH_URL=https://auth.<your-tenant>.unitelabs.io/realms/<tenant-id>/protocol/openid-connect/
CLIENT_ID=<your-client-id>
CLIENT_SECRET=<your-client-secret>

scripts/deploy.py loads .env automatically when run locally. CI/CD pipelines set the same four variables as platform secrets — see CI/CD for workflows.

.env is in the template's .gitignore. Never commit credentials.

What the deploy script does

scripts/deploy.py is a PEP-723 single-file CLI — its own dependencies (requests, python-dotenv) are declared inline so uv run scripts/deploy.py … resolves them into an ephemeral env. There is no project-level pyproject.toml at the repo root.

For each workflow you deploy it:

  1. Discovers the workflow by globbing w*-*/pyproject.toml and matching [project].name (the slug).
  2. Builds a bundle as a zip with the workflow directory verbatim plus the shared/ library flat-vendored at the bundle root, so import shared.steps.X resolves on the platform without sys.path tricks.
  3. Stitches install-time dependencies from the workflow's [project].dependencies + shared's [project].dependencies (deduped, workflow wins, the local shared entry dropped) and sends the merged list to the platform's dependencies field. The platform installs the runtime venv from this list.
  4. Authenticates with OAuth2 client credentials.
  5. Looks up the workflow by its display_name (paginated, client-side match). If found, PATCHes it; otherwise POSTs a new record. The lookup refuses to deploy on duplicate display names, so you can never silently overwrite the wrong record.
  6. Patches metadata — description, tags, enabled: true — so a "soft-deleted" record (the UI delete only flips enabled=false) is resurrected on redeploy rather than living on as a hidden duplicate.

Commands

All five forms are mutually exclusive; pick whichever fits your need.

Deploy a single workflow

uv run scripts/deploy.py w03-liquid-handling

The positional argument is the workflow's slug[project].name in its pyproject.toml.

Deploy every workflow

uv run scripts/deploy.py --all

Deploy only what changed

uv run scripts/deploy.py --changed-from origin/main

Runs git diff --name-only <ref>..HEAD and deploys only the workflows whose directory was touched. Use it locally to preview what a merge would redeploy, or in CI on push-to-main (see CI/CD for workflows).

The "everything affected" rule. A change under shared/ or to scripts/deploy.py itself marks every workflow as affected — shared/ is vendored into every bundle and scripts/deploy.py rewrites every bundle's contents. The rule lives in the Python helper affected_workflows() so it's testable and single-source.

Deploy a tagged release

uv run scripts/deploy.py --git-tag w03-liquid-handling/v1.2.0

Parses the tag (<slug>/v<X.Y.Z>), verifies the workflow's [project].version matches 1.2.0, and adds v1.2.0 to the platform tags. Refuses to deploy if pyproject and tag disagree — the pyproject is the source of truth.

uv run scripts/deploy.py --list
uv run scripts/deploy.py --list --changed-from origin/main

Prints the selected slugs (one per line) and exits before loading .env or authenticating. CI pipelines (or you, locally) can use this to sanity-check the selection without needing credentials.

Channels: DEV / STG / PRD

uv run scripts/deploy.py w03-liquid-handling --channel dev
uv run scripts/deploy.py --all --channel stg
uv run scripts/deploy.py --git-tag w03-liquid-handling/v1.2.0 --channel prd

--channel layers on top of any of the forms above. It prepends [DEV] /[STG] /[PRD] to the platform display_name and adds the channel name as a platform tag. The result: a single tenant can host parallel DEV, STG, and PRD records of the same workflow as distinct platform entries — bench testing on DEV doesn't risk the production record.

The channel-to-trigger mapping is established by CI; locally you'll usually omit --channel (deploys land as untagged "main" records) or pass --channel dev when iterating against a shared DEV tenant. The full mapping is in CI/CD for workflows.

Extra platform tags

uv run scripts/deploy.py w03-liquid-handling --tag prod --tag stable

--tag/-t is repeatable and stacks with --git-tag's version label and --channel's channel tag. Use it for ad-hoc labels (a feature flag, a customer name) without bumping the workflow's version.

Release a workflow

Each workflow ships independently. To release w03-liquid-handling v1.2.0:

  1. Bump the version. Edit w03-liquid-handling/pyproject.toml and set [project].version = "1.2.0".
  2. Commit:
    git add w03-liquid-handling/pyproject.toml
    git commit -m "release: w03-liquid-handling v1.2.0"
    
  3. Tag and push:
    git tag w03-liquid-handling/v1.2.0
    git push origin main --tags
    

The tag push triggers deploy-prd in CI — see CI/CD for workflows. You can also run the release locally:

uv run scripts/deploy.py --git-tag w03-liquid-handling/v1.2.0 --channel prd
The tag form <slug>/v<X.Y.Z> is mandatory — bare v1.2.0 tags won't match the deploy regex, intentionally. A repo with several workflows needs per-workflow tags so bumping one doesn't pretend every other workflow changed.

Verify

Open the UniteLabs Workflows page and confirm the workflow appears with the expected [CHANNEL] prefix, version, and tags.

The script's stdout shows what landed on the platform — display name, resolved entrypoint, bundle size, dep count, and whether the record was created or updated.

Next steps

  • Set up CI/CD — wire scripts/deploy.py into GitHub Actions or GitLab CI with the three-channel (dev/stg/prd) model.
  • Trigger a workflow run — confirm the deployment end-to-end.
  • AGENTS.md — authoring rules for adding new workflows to the template.