This package is a collection of (very) opinionated Poe the Poet Python tasks for common Python development workflows.
You can add common-python-tasks to a new project by using the handy automated installation script.
curl -sSL https://api.github.com/repos/ci-sourcerer/common-python-tasks/contents/scripts/add-common-python-tasks.sh | jq -r '.content' | base64 -d | TAGS_TO_INCLUDE="format lint test" shThis will complete the following steps.
- Add the latest version of
common-python-tasksto yourpyproject.tomldependencies - Configure Poe the Poet to include only the tasks with the specified tags
- Install the package using Poetry
Always review scripts before running them! Even though I believe I write good software, it's best practice to verify any script you download from the Internet.
-
Add
common-python-tasksto yourpyproject.tomland configure Poe the Poet to include the desired tasks[project] name = "my-awesome-project" version = "0.0.2" dependencies = [ "common-python-tasks==0.0.2", # Always pin to a specific version ] [tool.poe] include_script = "common_python_tasks:tasks(include_tags=['format', 'lint', 'test'])" # Include or exclude tasks by tags
-
Install the package
poetry install
-
Run tasks
poe format # Format your code poe lint # Check code quality poe test # Run tests with coverage
Internal tasks are used by other tasks and are not meant to be run directly.
| Task | Description | Tags |
|---|---|---|
build |
Build the project and its containers (when containers tag is included) |
packaging, containers |
build-image |
Build the container image for this project using the Dockerfile template (and configured extensions) | containers, build |
build-package |
Build the package (wheel and sdist) | packaging, build |
bump-version |
Bump the project version, defaulting to an inferred semantic bump from git history | packaging |
changelog |
Print the changelog for the current version based on git history | packaging, release |
clean |
Clean up temporary files and directories | clean |
container-shell |
Run the debug image with an interactive shell | containers, debug |
db-shell |
Open a psql shell to the database container | web, containers, database |
format |
Format code with autoflake, black, and isort | format |
lint |
Lint Python code with autoflake, black, isort, and flake8 | lint |
publish-package |
Publish the package to the PyPI server | packaging |
publish-github-release |
Publish or update a GitHub Release and attach built distribution assets | packaging, release |
push-image |
Push the Docker image to the container registry | containers, packaging, release |
release |
Run package release flow and publish containers when containers tag is included. Supports optional RELEASE_PRE_SCRIPT and RELEASE_POST_SCRIPT hooks. |
packaging, release |
reset-db |
Reset the database by deleting the database volume | web, containers, database |
run-container |
Run the Docker image as a container | containers |
run-db-migrations |
Run database migrations | web, containers, database |
stack-down |
Bring down the development stack for the application | web, containers |
stack-up |
Bring up the development stack for the application | web, containers |
test |
Run the test suite with coverage | test |
Some tasks of certain tags provide Docker Compose-based development stacks for running your application with supporting services (databases, caches, etc.). Currently supports FastAPI applications with PostgreSQL.
Specifies the type of application stack. Currently supported:
fastapi- FastAPI application with optional database, Alembic migrations
Set via environment variable:
[tool.poe.env]
COMPOSE_TYPE = "fastapi"Colon-separated list of additional services to include. Available addons:
db- PostgreSQL database with Alembic migration support and Adminer web UI
Example:
[tool.poe.env]
COMPOSE_ADDONS = "db"For multiple addons (future): COMPOSE_ADDONS = "db:redis:cache"
The compose setup follows this precedence.
- Environment override -
COMPOSE_FILEenvironment variable with colon-separated paths - Auto-loaded files - Based on
COMPOSE_TYPEandCOMPOSE_ADDONS:compose-base.yml- Core application servicecompose-{addon}.yml- For each addon (e.g.,compose-db.yml)compose-debug.yml- When--debugflag is usedcompose-{addon}-debug.yml- Debug overlays for addons
- Additional overlays -
COMPOSE_OVERLAY_FILESwith colon-separated paths
You can provide local compose files or let the tasks use bundled templates.
The fastapi stack includes a service for your FastAPI application. It uses the standard Dockerfile included with this package.
API_PORT- Port for the API server (default:8080)SECRET_KEY- Application secret key (auto-generated and stored in.envif not set)ENVIRONMENT- Environment name likedevelopmentorproduction(default:production)DEBUG_PORT- Port for the Python debugger when using--debug(default:5678)DB_PORT- PostgreSQL port (default:5432)DB_USER- Database user (default: package name)DB_BASE- Database name (default: package name)DB_PASS- Database password (auto-generated and stored in.envif not set)POSTGRES_VERSION- PostgreSQL Docker image version (default:17)ADMINER_PORT- Adminer web UI port (default:8081)
Your project must meet the following requirements.
- Use Poetry for dependency management
- Have a
pyproject.tomlfile at the root - Have a package name (automatically inferred from
project.nameinpyproject.tomlor set viaPACKAGE_NAMEenvironment variable)
Tasks that need configuration files (pytest, coverage, flake8, isort) follow this order of precedence.
pyproject.tomlsections -[tool.pytest],[tool.coverage],[tool.isort]take priority- Environment variables - Override config paths (see Environment Variables)
- Local config files -
pytest.ini,.coveragerc,.flake8,.isort.cfgin project root - Bundled defaults - Sensible defaults included with this package, found in the
src/common_python_tasks/datadirectory
You can start with zero configuration and customize as needed.
The following environment variables configure the paths to configuration files.
PYTEST_CONFIGspecifies the path to the pytest configuration fileCOVERAGE_RCFILEspecifies the path to the coverage configuration fileFLAKE8_CONFIGspecifies the path to the flake8 configuration fileISORT_CONFIGspecifies the path to the isort configuration file
The following environment variables configure package and container behavior.
PACKAGE_NAMEoverrides the package name (default is frompyproject.toml)POETRY_VERSIONoverrides the Poetry version for container buildsDOCKERHUB_USERNAMEspecifies the Docker Hub username for image tagging (default is current local user)CONTAINER_REGISTRY_URLspecifies the registry URL (default isdocker.io/{username})CONTAINER_BUILD_ARGSprovides additional Docker build arguments inKEY=VALUE:OTHER=VALUEformatCONTAINER_EXTENSION_FILESspecifies colon-delimited local extension Dockerfile paths to include in the rendered build.CONTAINER_EXTENSIONSspecifies colon-delimited extension bundle names or parameterized bundle values to include in the rendered build.CONTAINER_ENVprovides colon-delimitedKEY=VALUEdeclarations to inject into the builder stage of the rendered Dockerfile..containerenvcan also supply the same declarations from a file in the project root. It is the fallback source when neithercontainer_envfilenorCONTAINER_ENVis provided.CONTAINER_PRUNE_KEEPcontrols image pruning after builds (-1keep all,0keep latest only,Nkeep latest +Nprevious)CUSTOM_IMAGE_ENTRYPOINTspecifies a custom entrypoint script name for containersCONTAINER_DEPS_CONTENTsupplies inline Dockerfile instructions for a dependency image that installs artifacts into/tmp/depsCONTAINER_DEPS_FILEpoints to one or more explicit Dockerfiles to build the dependency image. It may be a colon-delimited list of file paths and is used only whenCONTAINER_DEPS_CONTENTis unset.CONTAINER_DEPS_MAPPINGSmaps copied dependency names from/tmp/depsinto destination paths, as whitespace-separatedname:/target/pathentries. This is only used whenCONTAINER_DEPS_MOVE_SCRIPTorCONTAINER_DEPS_MOVE_SCRIPT_PATHis not set.CONTAINER_DEPS_MOVE_SCRIPTsupplies a raw executable script to run after/tmp/depsis copied into the image. The script is written to/tmp/container-deps-move-scriptand executed with its own shebang.CONTAINER_DEPS_MOVE_SCRIPT_PATHsupplies a host path to a script file to run after/tmp/depsis copied into the image. This path takes precedence overCONTAINER_DEPS_MOVE_SCRIPTwhen both are set.GITHUB_RELEASE_ASSETScolon-separated list of file paths or glob patterns to attach to the GitHub Release (default:dist/*)SKIP_GITHUB_RELEASEtruthy value to skip GitHub Release publication.GITHUB_TOKENorGH_TOKENGitHub authentication token used to publish releases and upload assets.GITHUB_REPOSITORYoptional override for the repository slug used when publishing a GitHub Release.GITHUB_API_URLandGITHUB_SERVER_URLconfigure the GitHub API host for GitHub Enterprise environments.GITHUB_RELEASE_TAGoptional tag name to publish for the GitHub Release.GITHUB_RELEASE_NAMEoptional release title to use for the GitHub Release.GITHUB_RELEASE_BODYoptional release body text to use for the GitHub Release.RELEASE_PRE_SCRIPToptional shell command to run before the release steps.RELEASE_POST_SCRIPToptional shell command to run after the release completes.- Hook commands receive the following env vars:
RELEASE_TAG,RELEASE_VERSION,RELEASE_STAGE,RELEASE_COMPONENT, andRELEASE_DRY_RUN.
The following environment variables configure Docker Compose stacks (when using the web tag).
COMPOSE_TYPEspecifies the type of application stack (e.g.,fastapi)COMPOSE_ADDONScolon-separated list of services to include (e.g.,dbfor database)COMPOSE_FILEoverrides all compose files with colon-separated pathsCOMPOSE_OVERLAY_FILESadditional compose files to merge (colon-separated paths)API_PORTport for the API server (default:8080)SECRET_KEYapplication secret key (auto-generated if not set)ENVIRONMENTenvironment name (default:production)DEBUG_PORTport for Python debugger in debug mode (default:5678)DB_PORTPostgreSQL port (default:5432)DB_USERdatabase user (default: package name)DB_BASEdatabase name (default: package name)DB_PASSdatabase password (auto-generated if not set)POSTGRES_VERSIONPostgreSQL version (default:17)ADMINER_PORTAdminer web UI port (default:8081)
The following environment variable enables debugging output.
COMMON_PYTHON_TASKS_LOG_LEVELshould be set toDEBUGto see detailed configuration resolution
You can include or exclude tasks by tags in your pyproject.toml
[project]
name = "simple-cli-tool"
version = "0.0.1"
dependencies = ["common-python-tasks==0.0.1"]
[tool.poe]
include_script = "common_python_tasks:tasks(include_tags=['format', 'lint'])"Available tasks: format, lint.
[project]
name = "containerized-app"
version = "0.0.1"
dependencies = ["common-python-tasks==0.0.1"]
[tool.poe]
include_script = "common_python_tasks:tasks(include_tags=['format', 'lint', 'test', 'containers'])"
[tool.poe.env]
DOCKERHUB_USERNAME = "myusername"
PACKAGE_NAME = "containerized-app"Available tasks: All tasks including build-image and push-image.
[project]
name = "custom-test-setup"
dependencies = ["common-python-tasks==0.0.1"]
dynamic = ["version"]
[tool.poe]
include_script = "common_python_tasks:tasks(include_tags=['test'])"
[tool.pytest.ini_options]
testpaths = ["tests", "integration"]
addopts = "-ra"The test task will automatically use your [tool.pytest.ini_options] configuration.
Check your [tool.poe] configuration in pyproject.toml. Make sure you're using include_script, not includes.
# Correct
[tool.poe]
include_script = "common_python_tasks:tasks(exclude_tags=['internal'])"
# Incorrect
[tool.poe]
includes = "common_python_tasks:tasks"This is expected behavior. The bump-version task requires commits between the last tag and HEAD. You can resolve this in one of the following ways.
- Make changes and commit them first
- Delete the old tag (for example,
git tag -d v0.0.1). This is not recommended. Versions should be immutable, and if you need to fix something, you should create a new patch version instead. Rarely do you want to pass off new code as an old version
Check the configuration precedence (see How it works). Use debug logging to see which config is selected.
If your release task does not attach assets, confirm dist/ contains the built wheels and sdists. You can override the default asset selection using GITHUB_RELEASE_ASSETS, for example:
GITHUB_RELEASE_ASSETS="dist/*.whl:dist/*.tar.gz" poe publish-github-releaseCOMMON_PYTHON_TASKS_LOG_LEVEL=DEBUG poe testMake sure your pyproject.toml contains the following.
- A correct package name in
[project] - A package location defined with this configuration:
[tool.poetry] packages = [{ include = "your_package", from = "src" }]
If stack-up builds successfully but services can't connect:
- Check that required environment variables are set (
COMPOSE_TYPEat minimum) - Verify ports aren't already in use (defaults: 8080 for API, 5432 for database, 8081 for Adminer)
- Check Docker daemon is running:
docker info - View service logs:
docker-compose logsin your project directory
If run-db-migrations fails:
- Ensure the
dbaddon is included:COMPOSE_ADDONS=db - Check that your project has Alembic configured with migrations in the expected location
- Verify database credentials in
.envmatch your Alembic configuration - Manually inspect the database:
poe db-shell
If SECRET_KEY or DB_PASS aren't auto-generated:
- Ensure
.envfile is writable in your project root - Check file permissions:
ls -la .env - Generate manually:
python -c "import secrets; print(secrets.token_hex(32))"
Dockerfile (see src/common_python_tasks/data/Dockerfile)
The standard Python Dockerfile incorporates several intentional design choices.
- Multi-stage build: The build stage installs Poetry and builds a wheel while the runtime stage installs only the wheel to keep the final image slim and reproducible
- Pip and Poetry cache mounts speed up iterative builds without bloating the final image
- Explicit inputs through build args (
PYTHON_VERSION,POETRY_VERSION,PACKAGE_NAME,AUTHORS,GIT_COMMIT,CUSTOM_ENTRYPOINT) make image metadata and behavior predictable and auditable - Optional debug stage exports and installs the
debugdependency group only when present without failing otherwise and is not part of the default final image - Stable package path creates symlinks to the installed package so entrypoints and consumers have a consistent
/pkgand/_$PACKAGE_NAMEpath regardless of wheel layout, which ensures that the package can be reliably imported and executed from a known location, and allows for the less common use case of reading files directly from the package path - Safe entrypoint selection means the default entrypoint resolves the console script matching the package name while
CUSTOM_ENTRYPOINTallows overriding at build time while keeping runtime behavior predictable - Minimal final image uses the slim Python base by default, cleans wheel artifacts and caches, and sets
runtimeas the explicit final target so the debug stage is opt-in
- This project dogfoods itself - it uses
common-python-tasksfor its own development RELEASE_PRE_SCRIPTandRELEASE_POST_SCRIPThooks may not be necessary for most users, as this package promotes the use ofpoetry-dynamic-versioningandgit-cliffto automate versioning and changelog generation based on git history. However, one advanced use case for theRELEASE_PRE_SCRIPThook is to edit a file before release, such as a README that references the current stable version number. This allows you to keep the README up to date with the latest version without hardcoding it.- Contributions welcome! Open an issue/discussion to discuss changes before submitting a PR. I do not claim to have all the answers, and you can help determine the future of low-code solutions for Python. I am very interested in your feedback as I don't want to work in a vacuum
- Alpha status: Expect breaking changes between minor versions until 1.0.0