Release and deploy
Publish splent_feature_notes to PyPI, pin a versioned release, and deploy your product to production.
Table of contents
- Prerequisites
- Step 1 – Verify credentials
- Step 2 – Write the feature contract
- Step 3 – Commit and push to GitHub
- Step 4 – Release the feature
- Step 5 – Pin a version manually (optional)
- Step 6 – Resolve the product
- Step 7 – Validate the product
- Step 8 – Build the production image
- Step 9 – Deploy
- Step 10 – Shut down production
- Step 11 – Back to development
- What you learned
- Next
Prerequisites
You have completed Tutorial 4 – Testing. You are inside the CLI container with my_first_app selected and splent_feature_notes working with passing tests.
(my_first_app) /workspace$
Part 1 — Release a feature
This part publishes real packages. It requires a GitHub account with a repository for the feature and a PyPI account with an API token. The package published to PyPI is permanent — it cannot be fully deleted. If you are just exploring SPLENT and do not want to publish anything, skip to Part 2 — Deploy to production.
Step 1 – Verify credentials
Before releasing, you need GitHub and PyPI tokens. If you don’t have them yet, run:
splent tokens:setup
This prints step-by-step instructions for obtaining:
- A GitHub personal access token (for creating releases and pushing tags)
- A PyPI API token (for publishing packages)
After adding the tokens to your .env file, reload them in your shell:
source .env
Without
source .env, the CLI cannot read the tokens you just configured. This is required every time you add or change variables in.env.
Once configured, verify they work:
splent check:github
✅ GitHub credentials OK.
splent check:pypi
✅ PyPI credentials OK.
Both checks must pass before
feature:releasewill work. If either fails, follow the instructions fromsplent tokens:setupto fix them.
Step 2 – Write the feature contract
The contract is the machine-readable summary of what your feature provides and requires. It lives in pyproject.toml and is used by other features, the CLI, and the release pipeline.
splent feature:contract splent_feature_notes --write
You will see output like:
feature:contract -- splent_feature_notes
────────────────────────────────────────────────────
Scanning source code...
Inferred contract:
[tool.splent.contract.provides]
routes = ["/notes/", "/notes/create", "/notes/<int:id>/edit", "/notes/<int:id>/delete"]
blueprints = ["notes_bp"]
models = ["Notes"]
commands = []
hooks = ["layout.authenticated_sidebar"]
services = ["NotesService"]
docker = []
[tool.splent.contract.requires]
features = ["auth"]
env_vars = []
Written to pyproject.toml.
The contract scanner reads your routes, models, hooks, services, and imports automatically. No manual bookkeeping required.
This step is optional —
feature:releaseautomatically updates the contract before publishing. Running it manually lets you review the contract before the release.
Step 3 – Commit and push to GitHub
feature:release needs a git repository with a clean working tree. If you have not initialized one yet:
cd /workspace/splent_feature_notes
git init
git add .
git commit -m "Initial release of splent_feature_notes"
git remote add origin git@github.com:splent-io/splent_feature_notes.git
git push -u origin main
Replace the remote URL with your own GitHub repository. The feature needs to be hosted on GitHub for the release pipeline to tag and publish it.
Step 4 – Release the feature
Back in the CLI container:
splent feature:release splent-io/splent_feature_notes
The semver wizard fetches the latest tag from GitHub and offers bump options:
Fetching current version from GitHub...
No tags found — this will be the first release.
Bump type:
[1] patch v0.0.1 bug fixes, no new features
[2] minor v0.1.0 new features, backward compatible
[3] major v1.0.0 breaking changes
[4] cancel
Choice: 3
Will release as v1.0.0
Proceed? [y/N]: y
After confirmation, the full pipeline runs:
Releasing splent_io/splent_feature_notes v1.0.0
contract updating from source code...
contract written to pyproject.toml
version 1.0.0 written to pyproject.toml
commit changes committed and pushed
tag v1.0.0 created
tag v1.0.0 pushed to origin
github release created: https://github.com/...
pypi building package...
pypi upload complete
snapshot splent_feature_notes@v1.0.0 (read-only)
splent_io/splent_feature_notes v1.0.0 released.
Attach splent_feature_notes@v1.0.0 to my_first_app? [Y/n]: y
attach linking to product...
reinstalling in web container...
done.
At the end, the release asks whether to attach the new version to the active product. Press Y — this pins the release in pyproject.toml (replacing the editable reference) and reinstalls it in the web container automatically.
You can also pass the version explicitly to skip the wizard: splent feature:release splent-io/splent_feature_notes v1.0.0
Step 5 – Pin a version manually (optional)
If you pressed Y at the attach prompt in the previous step, the feature is already pinned. This step is only needed if you skipped the prompt or want to attach a different version later.
To manually pin a released version:
splent feature:attach splent-io/splent_feature_notes v1.0.0
Your product’s pyproject.toml now says splent-io/splent_feature_notes@v1.0.0 instead of splent-io/splent_feature_notes. The feature is pinned – it will not change unless you explicitly upgrade.
Step 6 – Resolve the product
splent product:resolve
This re-creates all symlinks. The notes symlink now points into .splent_cache/ (read-only) instead of the workspace root (editable):
my_first_app/features/splent_io/
splent_feature_notes@v1.0.0 -> ../../../.splent_cache/features/splent_io/splent_feature_notes@v1.0.0
Step 7 – Validate the product
splent product:validate
This checks that all UVL constraints are satisfied and all feature contracts are compatible. You should see:
product:validate -- my_first_app
────────────────────────────────
UVL constraints: PASS
Feature contracts: PASS
Missing features: 0
Product is valid.
Part 2 — Deploy to production
This part does not require GitHub or PyPI accounts. You can run it even if you skipped Part 1.
If you skipped Part 1,
splent_feature_notesis not published on PyPI. The production build installs features from PyPI, so it will fail onsplent_feature_notes. Remove it from your product before building:splent feature:remove splent_feature_notes. The product will deploy with the remaining features (auth, profile, redis, session_redis, public).
Step 8 – Build the production image
splent product:build
This runs pre-flight checks (UVL validation, contract consistency, missing features) and then builds a multi-stage Docker image optimized for production. The image installs pinned features from PyPI, compiles frontend assets with Webpack in production mode, and strips dev dependencies.
At the end, two files are generated in my_first_app/docker/:
.env.deploy— production environment variables (database credentials, secret key, Redis URL, etc.). Review and edit this file before deploying.docker-compose.deploy.yml— production Compose file that merges the product and all feature services into a single stack.
Step 9 – Deploy
splent product:deploy
The command prompts for production credentials (database host, Redis URL, secret key) if .env.deploy is not yet configured. Once confirmed, it starts the production containers and runs a health check to verify the app is responding.
Open the URL shown in the terminal and verify the product works — log in, navigate through the features, and confirm everything is functional.
Step 10 – Shut down production
When you are done verifying:
splent product:down --prod
Step 11 – Back to development
Restart the dev environment:
splent product:up --dev
splent product:run --dev
What you learned
| Concept | What it means |
|---|---|
| Feature contract | Machine-readable summary of routes, models, hooks, services, and dependencies – inferred from source code |
| Release pipeline | One command bumps, tags, publishes to PyPI, and snapshots to cache |
| Pinned vs editable | Editable lives at workspace root (read-write); pinned lives in .splent_cache/ (read-only) |
| Production deployment | Multi-stage Docker build, .env.deploy, and product:deploy |
| Dev/prod isolation | --dev and --prod flags keep environments fully separate |
Next
Tutorial 6 – Extend with refinement: override a service, extend a model, and add new routes without forking the base feature.