Release and deploy

Publish splent_feature_notes to PyPI, pin a versioned release, and deploy your product to production.


Table of contents

  1. Prerequisites
  2. Step 1. Verify credentials
  3. Step 2. Write the feature contract
  4. Step 3. Commit and push to GitHub
  5. Step 4. Release the feature
  6. Step 5. Pin a version manually (optional)
  7. Step 6. Resolve the product
  8. Step 7. Validate the product
  9. Step 8. Build the production image
  10. Step 9. Deploy
  11. Step 10. Shut down production
  12. Step 11. Back to development
  13. What you learned
  14. 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 the setup command.

splent tokens:setup

This prints step-by-step instructions for obtaining the following.

  • 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:release will work. If either fails, follow the instructions from splent tokens:setup to 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 this.

  feature:contract -- splent_feature_notes
  ────────────────────────────────────────────────────

  Scanning source code...

  Inferred contract:

  [tool.splent.contract.provides]
  routes     = ["/notes/", "/notes/<int:note_id>/delete", "/notes/create"]
  models     = ["Notes"]
  hooks      = ["layout.sidebar.authenticated.items"]
  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:release automatically 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, do it now.

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, run the release command.

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.

Notice the gate at the top. Nothing is bumped, committed, tagged or uploaded until both channels have confirmed they would accept this exact version, so a bad token costs you a message rather than a burned version number. PyPI is then published before the tag is pushed, because a PyPI version can never be taken back while an unpushed tag costs nothing. If something still fails halfway, finish it with release:resume, which never bumps the version.

  Releasing splent_io/splent_feature_notes v1.0.0

  Release gate (channels)
  github   ready, splent-io/splent_feature_notes accepts push, tag and release as you
  pypi     ready, this upload would create splent_feature_notes and PyPI accepted
           the project name and the credentials

  contract updating from source code...
  contract written to pyproject.toml
  version  1.0.0 written to pyproject.toml
  commit   changes committed
  pypi     building package...
  pypi     upload complete
  tag      v1.0.0 created
  tag      v1.0.0 pushed to origin
  github   release created: https://github.com/...
  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 to pin the release in pyproject.toml (replacing the editable reference) and reinstall it in the web container automatically.

You can also pass the version explicitly to skip the wizard, e.g. 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.

Pin a released version manually with feature:attach.

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 this output.

  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_notes is not published on PyPI. The production build installs features from PyPI, so it will fail on splent_feature_notes. Remove it from your product before building with 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

Heavy step. This can take several minutes. product:build runs a multi-stage production Docker build. It installs the pinned features from PyPI and compiles the frontend assets with Webpack. The first build is slow; later builds reuse cached layers.

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.example. A template of the production environment variables (database credentials, Redis URL, etc.) with placeholder <SET> values. product:deploy uses it to create the real .env.deploy in the next step, prompting you for any missing values.
  • 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 creates .env.deploy from the template and prompts for any required production credentials that are still unset (database name, user, password, root password). 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, shut it down.

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

Continue with Tutorial 6, Extend with refinement, to override a service, extend a model, and add new routes without forking the base feature.


Back to top

splent. Distributed by an LGPL license v3. Contact us: drorganvidez@us.es