cal-publish-python
CLI tool to publish Python packages and documentation with external configuration.
Overview
cal-publish-python is designed to keep sensitive configuration (tokens, registry URLs, SSH keys) outside of build jobs. The project being published defines the "what" (name, version), while a named profile in the publishing configuration defines the "where" (registry, documentation platform).
Installation
pip install cal-publish-python
Or with uv:
uv pip install cal-publish-python
Usage
# Publish everything in output/ (wheel + docs zip)
cal-publish-python --set-latest output/
# Publish only the PyPI package
cal-publish-python --pypi-only output/
# Publish only the documentation
cal-publish-python --docs-only --set-latest output/
# Specify a profile explicitly
cal-publish-python --profile internal --set-latest output/
# Specify individual files
cal-publish-python output/my-package-1.0.0-py3-none-any.whl output/my-package-1.0.0-docs.zip
CLI Arguments
| Argument | Short | Description |
|---|---|---|
ARTIFACT |
Directory or file(s) to publish (.whl and/or docs .zip) | |
--config |
-c |
Path to JSON configuration file |
--profile |
Profile to use from the configuration file | |
--pypi-only |
Only publish to PyPI (skip documentation) | |
--docs-only |
Only publish documentation (skip PyPI) | |
--set-latest |
-l |
Also set this version as 'latest' (GitLab Pages mode only) |
--force |
-f |
Overwrite existing documentation version if it exists |
--verbose |
-v |
Verbose output |
Configuration
Profiles
Configuration uses named profiles. Each profile contains the full set of publishing settings (PyPI credentials, documentation targets, etc.). This allows the same config file to serve both public and internal publishing.
Create a JSON config file at ~/.config/cal-publish-python/config.json:
{
"profiles": {
"public": {
"pypi": {
"token": "pypi-xxxxxxxxxxxx",
"username": "__token__",
"repository_url": "https://upload.pypi.org/legacy/"
},
"docs": {
"mode": "gitlab-pages",
"gitlab_pages": {
"token": "glpat-xxxxxxxxxxxx",
"url": "https://gitlab.com",
"group": "mygroup/public/docs"
}
}
},
"internal": {
"pypi": {
"token": "nexus-xxxxxxxxxxxx",
"username": "deploy-user",
"repository_url": "https://nexus.internal/repository/pypi/"
},
"docs": {
"mode": "ssh",
"ssh": {
"host": "docs.internal",
"base_path": "/var/www/docs"
}
}
}
}
}
Profile Selection
A profile is required when a config file is present. The profile is resolved in this order:
--profile NAMECLI argumentCAL_PUBLISH_PROFILEenvironment variable[tool.cal-publish-python]inpyproject.toml:
[tool.cal-publish-python]
profile = "public"
This means each project can declare which profile it uses, and make publish just works without any extra arguments.
Config File Location
The config file is resolved in this order:
--config FILECLI argumentCAL_PUBLISH_CONFIGenvironment variable~/.config/cal-publish-python/config.json(default)
If no config file exists at the default path, the tool runs in environment-variable-only mode (useful for CI).
Environment Variables
All configuration values can be overridden by environment variables, regardless of what the config file/profile contains:
| Variable | Description |
|---|---|
CAL_PUBLISH_CONFIG |
Path to JSON config file |
CAL_PUBLISH_PROFILE |
Profile name to use from config file |
CAL_PUBLISH_PYPI_TOKEN |
PyPI API token or password |
CAL_PUBLISH_PYPI_USERNAME |
PyPI username (default: __token__) |
CAL_PUBLISH_PYPI_REPOSITORY_URL |
PyPI repository URL |
CAL_PUBLISH_DOCS_MODE |
Documentation mode (gitlab-pages, ssh, or cal-docs-server) |
CAL_PUBLISH_GITLAB_TOKEN |
GitLab personal access token |
CAL_PUBLISH_GITLAB_URL |
GitLab instance URL |
CAL_PUBLISH_GITLAB_GROUP |
GitLab group for docs |
CAL_PUBLISH_SSH_HOST |
SSH host |
CAL_PUBLISH_SSH_BASE_PATH |
Base path on remote server |
CAL_PUBLISH_SSH_KEY |
Path to SSH private key |
CAL_PUBLISH_SSH_USER |
SSH user |
CAL_PUBLISH_SSH_PORT |
SSH port |
CAL_PUBLISH_DOCS_SERVER_URL |
cal-docs-server URL |
CAL_PUBLISH_DOCS_SERVER_TOKEN |
API token for cal-docs-server |
Integration with Makefile
Add a publish target to your project's Makefile:
.PHONY: publish
publish: check-dependencies
uv run cal-publish-python --set-latest output/
And declare the profile in pyproject.toml:
[tool.cal-publish-python]
profile = "public"
Documentation Zip Filename
The docs zip filename is used to determine the project name and version:
project-name-VERSION-docs.zip
Examples: myproject-1.0.0-docs.zip, my-lib-2.3.4.post5+gabcdef-docs.zip
Development
# Clone the repository
git clone <repo-url>
cd cal-publish-python
# Set up development environment
make dev
# Run checks
make check
# Build
make build
Licence
MIT License - Copyright (c) 2026 Cyber Assessment Labs
Metadata
Release files for cal-publish-python 1.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cal_publish_python-1.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Release files / cal_publish_python-1.1.1-py3-none-any.whl
| Download URL | cal_publish_python-1.1.1-py3-none-any.whl |
|---|---|
| Size | 31.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7e98076c067948d45db90dee31a6d27266ac31c01f2afa89fb50dd9dafb5d34c
|
|
BLAKE2b-256 checksum How to use checksums |
77b0b93de5beae576e96d206d147f0dff972365f4980a6bddd35da1e10d49f75
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.2
|