Delivery module for ps-poetry: automated version stamping, dependency resolution, and topologically-ordered publishing
Project description
Overview
The ps-plugin-module-delivery module automates building and publishing packages across a monorepo. It extends Poetry's build and publish commands with unified version stamping, dependency constraint resolution, and topologically-ordered publish waves. A standalone delivery command displays the planned build and publish dependency tree without executing it.
The module is registered as a ps.module entry point and activates when included in the host project's [tool.ps-plugin] configuration. Requires ps-plugin-core as the plugin host.
For working project examples, see the ps-poetry-examples repository.
Installation
pip install ps-plugin-module-delivery
Or with Poetry:
poetry add ps-plugin-module-delivery
Enable it in the plugin configuration:
[tool.ps-plugin]
modules = ["ps-delivery"]
Quick Start
Build all deliverable projects with a specific version:
poetry build -b 1.2.3
Publish all deliverable projects:
poetry publish -b 1.2.3
Preview the delivery plan without executing:
poetry delivery
Configuration
The module reads its settings from the [tool.ps-plugin] section of the host project's pyproject.toml. Individual projects may also define their own [tool.ps-plugin] section to override the host-level defaults for that specific project.
[tool.ps-plugin]
version-patterns = [
"[{in}] {in}",
"[{env:BUILD_VERSION}] {env:BUILD_VERSION}",
"{spec}"
]
version-pinning = "compatible"
deliver = true
version-patterns— Ordered list of version expression patterns. Each pattern is evaluated in sequence; the first one whose condition matches and whose expression produces a valid version wins. See Version Patterns below.version-pinning— Constraint mode applied when resolving inter-project dependency versions. Acceptscompatible,exact,minimum-only,range-next-major,range-next-minor, orrange-next-patch. Defaults tocompatible.deliver— Controls whether a project is included in the delivery scope. Set tofalseto exclude a project from all delivery operations. Defaults totruewhen not specified.
Individual projects can also opt out of delivery by setting package-mode = false in their [tool.poetry] section. When package-mode = false, the project is excluded regardless of the deliver setting.
Command-Line Usage
Build
poetry build [INPUTS...] [--build-version VERSION]
INPUTS— Optional list of project names or paths. When omitted, all deliverable projects are built.--build-version/-b— Provide a version value accessible as the{in}token in version patterns.
The build stage patches all pyproject.toml files with resolved versions and dependency constraints, executes builds in parallel, then restores the original files.
Publish
poetry publish [INPUTS...] [--build-version VERSION] [--repository REPO] [--dry-run] [--skip-existing]
INPUTS— Optional list of project names or paths.--build-version/-b— Provide a version value accessible as the{in}token in version patterns.- Standard Poetry publish options (
--repository,--username,--password,--cert,--client-cert,--dist-dir,--dry-run,--skip-existing) are passed through.
The publish stage processes projects in topological order, respecting inter-project dependencies so that each package is available before its dependents are published.
Delivery
poetry delivery [--json] [--projects] [--dependency-tree] [--publish-order]
Displays the delivery plan for the workspace without modifying any files.
--json— Output in JSON format instead of formatted text.--projects— Show project resolution details only.--dependency-tree— Show the dependency tree only.--publish-order— Show the publish wave ordering only.
When no filter flags are provided, all three sections are displayed. Filter flags may be combined to show multiple sections.
The formatted output includes project resolution details with per-project version, deliverable status, matched version pattern, and resolved dependencies. Verbosity flags (-v, -vv, -vvv) control the level of detail shown in formatted output:
- Normal — project name, path, deliverable status, resolved version, project dependencies (name only), and external dependencies with constraints.
- Verbose (
-v) — additionally shows all evaluated version patterns with numbered results (skipped,matched,ignored), the matched pattern string and pinning rule, dependency paths, and constraint resolution sources.
JSON output includes full resolution data regardless of verbosity level.
Version Patterns
Version patterns control how project versions are resolved during delivery. Each pattern follows the format:
[CONDITION] EXPRESSION
- The optional
[CONDITION]is a boolean expression that must evaluate to true for the pattern to be selected. - The
EXPRESSIONis a template string containing tokens in curly braces that is expanded to produce the version.
Patterns are evaluated in order. The first pattern whose condition is satisfied and whose expression produces a valid parseable version is used. If no pattern matches, the project's existing version remains unchanged.
Pattern Syntax
A pattern expression may contain one or more tokens:
{source}
{source:accessor}
{source:accessor<fallback>}
Multiple tokens may be combined to compose a version string:
{git:version:major}.{git:version:minor}.{git:distance}
Default Patterns
When version-patterns is not configured, the following defaults apply:
version-patterns = [
"[{in}] {in}",
"[{env:BUILD_VERSION}] {env:BUILD_VERSION}",
"{spec}"
]
This means: use the --build-version input if provided, otherwise check the BUILD_VERSION environment variable, otherwise fall back to the version declared in pyproject.toml.
Fallback Values
Each token may declare a fallback value inside angle brackets. Fallbacks are applied when the source is absent, fails to parse, or the requested field does not exist:
{in:minor<0>}
{env:VERSION<'1.0.0'>}
{git:version:distance<0>}
When no fallback is specified, the following type-based defaults are used:
| Type | Default |
|---|---|
| Number | 0 |
| String | "" |
| Boolean | false |
Token Resolvers
Patterns use the token expression syntax from ps-token-expressions with several built-in resolvers:
{in}— The input version passed via--build-version. Supports all version accessors.{spec}— The project's version from itspyproject.toml. Falls back to the host project version when the project version is0.0.0.{env:VAR_NAME}— Value of the environment variableVAR_NAME.{git:ACCESSOR}— Git repository metadata. See Git Resolver below.{v:VERSION:ACCESSOR}— Parse a literal or computed version string and extract a component. See Parse Version Resolver below.{date:FORMAT}— Current date and time formatted according toFORMAT. See Date Formats below.{rand:KIND}— Random values. See Random Values below.
Version Accessors
The following accessors apply to any version-bearing source (in, spec, git:version, v:...). These correspond to the fields of the Version model from ps-version:
| Accessor | Meaning |
|---|---|
major |
First version number |
minor |
Second version number |
patch |
Third version number |
rev |
Fourth version number |
core |
All core numbers joined as a string |
pre |
Full pre-release label |
pre:name |
Pre-release label name |
pre:number |
Pre-release label number |
dev |
PEP 440 dev segment number |
post |
PEP 440 post segment number |
metadata |
Full build metadata string |
metadata:parts:N |
Nth part of build metadata (zero-indexed) |
standards |
Set of detected format names (for conditions) |
Examples:
{in:major}.{in:minor}.{in:patch}
{spec:major}.{spec:pre:name}{spec:pre:number}
{spec:metadata:parts:0}
Git Resolver
The {git} token provides access to Git repository state. Used without an accessor, it returns the version string parsed from the most recent annotated tag.
| Accessor | Meaning |
|---|---|
version |
Parsed version from the most recent tag |
sha |
Short commit hash |
distance |
Commits since the last tag |
dirty |
True when there are uncommitted changes |
branch |
Current branch name |
main |
Default branch name resolved from origin/HEAD |
mainline |
True when the current branch is the main branch |
The version accessor supports all standard version accessors:
{git:version:major}.{git:version:minor}.{git:distance}
{git:version:pre:name}{git:version:pre:number}
Parse Version Resolver
The {v:VERSION:ACCESSOR} token parses an arbitrary version string and extracts a single component. The VERSION argument may be a literal string or a nested token expression, making it possible to parse environment variables or other dynamic values and extract individual parts.
{v:3.5.1:major} → 3
{v:{env:BUILD_VERSION}:minor} → minor component of BUILD_VERSION
{v:{in}:patch} → patch component of the input version
Date Formats
The {date:FORMAT} token resolves to the current date and time. When no format is given, it returns the current Unix timestamp as an integer.
Standard named formats:
| Name | Aliases | Example output |
|---|---|---|
unix |
1741791909 (integer timestamp) |
|
ticks |
638780915090000000 (.NET ticks) |
|
iso |
2026-03-12T16:05:09+00:00 |
|
iso-round |
o, O |
2026-03-12T16:05:09.123456+00:00 |
sortable |
s |
2026-03-12T16:05:09 |
universal |
u |
2026-03-12 16:05:09Z |
C#-style custom tokens:
| Token | Meaning | Example |
|---|---|---|
yyyy |
4-digit year | 2026 |
yy |
2-digit year | 26 |
MM |
2-digit month | 03 |
dd |
2-digit day | 12 |
HH |
24-hour hour | 14 |
mm |
2-digit minute | 05 |
ss |
2-digit second | 09 |
Python strftime directives (e.g., %Y, %m, %H) are also accepted and may be mixed with C#-style tokens in the same format string.
The {date:from:VALUE} token parses VALUE as a date string or Unix timestamp and returns an integer Unix timestamp. VALUE may contain nested token expressions, enabling comparisons against dates from environment variables or other sources.
Examples:
{date:yyyy.MM.dd}
{date:yyyyMMdd}
{date:yyyy-%m-dd}
{date:iso}
{date:sortable}
{date:o}
{date:from:{env:BUILD_DATE}}
{date:from:2026-03-12}
Random Values
The {rand} resolver generates non-deterministic values. A kind argument is always required.
| Token | Description |
|---|---|
{rand:uuid} |
UUID v4 as 32-character hex string |
{rand:hash} |
8-character lowercase hex string |
{rand:num} |
Random non-negative integer |
{rand:num:MIN..MAX} |
Random integer in the inclusive range |
Examples:
{rand:uuid}
{rand:num:1..100}
Custom Token Resolvers
Additional token resolvers can be registered through the DI container. Implement a BaseResolver subclass from ps.token_expressions, then register a TokenResolverEntry tuple with the desired token source name. The delivery module collects all registered entries and passes them to the expression factory.
Returning a string value — the simplest resolver returns a string directly:
class MyResolver(BaseResolver):
def __call__(self, args: list[str]) -> Optional[str]:
return args[0] if args else None
Once registered, the token {my:value} becomes available in all version patterns.
Returning a dictionary — register the dict directly as the resolver value. The token expression engine routes accessor args through it automatically, enabling {token:key} syntax:
def poetry_activate(di: DI) -> bool:
di.register(TokenResolverEntry).factory(lambda: ("meta", {"channel": "stable", "environment": "production"}))
return True
With this registration, {meta:channel} resolves to stable.
Returning an object — register an object instance directly. The engine walks attributes via getattr, enabling {token:attr} syntax. Implement __str__ to control what {token} (with no accessor) resolves to:
@dataclass
class BuildContext:
author: str
revision: int
def __str__(self) -> str:
return f"{self.author}@{self.revision}"
def poetry_activate(di: DI) -> bool:
di.register(TokenResolverEntry).factory(lambda: ("build", BuildContext(author="Alice", revision=7)))
return True
With this registration, {build} resolves to Alice@7, {build:author} to Alice, and {build:revision} to 7.
Condition Syntax
Conditions appear in the optional [...] block at the start of a pattern and evaluate to a boolean. A source token evaluates to true when the source exists and parses successfully. Conditions support boolean operators:
and or not ( )
Condition examples:
[{in}]
[{git} and not {git:dirty}]
[{git:mainline} and {git:dirty}]
[{env:BUILD_NUMBER} or {env:CI}]
[not {git:dirty} and ({in} or {env:BUILD_VERSION})]
Format Checks
To verify that a source version conforms to a specific standard, use the in operator with the standards accessor:
['pep440' in {in:standards}] {in}
['semver' in {git:version:standards}] {git:version}
['nuget' in {env:VERSION:standards}] {env:VERSION}
Supported format names: pep440, semver, nuget, calver, loose.
Example: Git-Based Versioning
version-patterns = [
"[{in}] {in}",
"[{git:mainline} and {git:dirty}] {git:version:major}.{git:version:minor<0>}.{git:distance}+{date:%Y%m%d%H%M}",
"[{git:mainline}] {git:version:major}.{git:version:minor<0>}.{git:distance}",
"[{git:dirty}] {git:version:major}.{git:version:minor<0>}.{git:distance}+{git:branch}.{date:%Y%m%d%H%M}",
"{git:version:major}.{git:version:minor<0>}.{git:distance}+{git:branch}"
]
This configuration resolves versions as follows:
- If
--build-versionis provided, use it directly. - On the main branch with uncommitted changes, produce a version with a date-based build metadata suffix.
- On the main branch without uncommitted changes, produce a clean version from the git tag and distance.
- On a feature branch with uncommitted changes, include the branch name and date in build metadata.
- On a feature branch without uncommitted changes, include only the branch name in build metadata.
Dependency Ordering
The delivery module resolves inter-project dependencies and organizes projects into publish waves. Each wave contains projects whose dependencies have all been published in previous waves.
Publish order:
├── Wave 1
│ ├── ps-version
│ └── ps-token-expressions
└── Wave 2
├── ps-plugin-sdk
└── ps-plugin-module-delivery
Projects within the same wave are processed in parallel during the build stage. During the publish stage, topological ordering ensures each project is published only after all of its dependencies are available.
Project Backup and Restore
Before any delivery operation, the module backs up all pyproject.toml files in the environment. After the operation completes — whether successfully or with an error — the original files are restored. This prevents accidental corruption of source projects during version patching.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ps_plugin_module_delivery-0.2.15.tar.gz.
File metadata
- Download URL: ps_plugin_module_delivery-0.2.15.tar.gz
- Upload date:
- Size: 25.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.3.2 CPython/3.13.12 Linux/6.17.0-1010-azure
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
162d3265b614ecd88ec5d22d994261b75ce4d6deb6a3635c7a23e2494e140893
|
|
| MD5 |
f330b1605b5d48ec423b921131b71416
|
|
| BLAKE2b-256 |
833f2eceaa235321d377d9e6d9b9cc24915730c7176796ab056fe332ac8eed66
|
File details
Details for the file ps_plugin_module_delivery-0.2.15-py3-none-any.whl.
File metadata
- Download URL: ps_plugin_module_delivery-0.2.15-py3-none-any.whl
- Upload date:
- Size: 28.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.3.2 CPython/3.13.12 Linux/6.17.0-1010-azure
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ffcfbf4b9b1a1bb39ffdfe0841417811f6c400ab92f4e158631aaaf6d9d9594d
|
|
| MD5 |
396a37a14c7ee3a52a58729528235985
|
|
| BLAKE2b-256 |
6d7a6b433cf59d7335784c88393d914c937f7bda64078eee063baa2fdd778634
|