These guidelines apply to GFW's Python repositories created from this template,
pipelines and services alike.
They describe the tooling the template ships (Makefile, Docker, uv),
so they live here, next to it, and each repository links to them from its README.
They aim to maintain code quality, clearly communicate the state of the codebase,
and standardize the development workflow.
Use your best judgment, and feel free to propose changes by submitting a pull request.
- Preparing the development environment
- Updating dependencies
- Git Workflow
- How to work on a feature branch
- How to deploy
This is the common setup shared across repositories created from this template. It assumes you already cloned the repository, as its README explains, and that you run every command from the root of the cloned repository. A given repository's README may add repository-specific system dependencies on top of this (e.g. GDAL) -- check there first if something doesn't build.
Install Docker Engine using the docker official instructions (avoid snap packages) and the docker compose plugin.
- Make sure you can build the docker image:
make docker-build- In order to be able to connect to Google Cloud (e.g. BigQuery), authenticate and configure the project:
make docker-gcpNote
If you'd rather do all development inside a Docker container instead of a local
virtual environment, run make docker-shell to get a shell with everything already
installed, skip steps 3-6 below, and go directly to step 7 -- make test works the
same way once you're inside that shell as it does in a local venv.
- Install uv (faster installs than plain pip):
make uv- Create a virtual environment and activate it:
make venv
. .venv/bin/activateThe activation script must be sourced (. or source), not executed:
running ./.venv/bin/activate would only affect a subshell.
- Install all dependencies and the package itself in editable mode:
make install- (Optional) Install the pre-commit hooks:
make hooksThis is strongly recommended to maintain code quality and prevent technical debt,
particularly regarding documentation, PEP8 compliance, spelling, and type-hinting.
If this feels too rigid for your current workflow, at a minimum,
make regular use of the Makefile's format, lint, codespell, and typecheck targets.
PEP8 checks are enforced in the GitHub CI.
- Make sure you can run the unit tests:
make testrequirements.txt contains all transitive dependencies pinned to specific versions. It
is automatically generated with uv pip compile, based on the dependencies specified in
pyproject.toml. This process ensures reproducibility, allowing the application to run
consistently across different environments.
requirements-test.txt does the same for the test dependencies, declared in the test
dependency group of pyproject.toml. It contains only the test dependencies, kept compatible
with the pins in requirements.txt, so the test image can install just them on top of the
production image and test the package exactly as it is deployed.
It doesn't repeat the production pins: those are compiled for the Docker image's Python version,
and the CI job on the next Python version installs the package with its own resolution.
Use pyproject.toml to define high-level dependencies with flexible version constraints
(e.g., ~=1.2, >=1.0, <2.0, ...).
make reqs-upgrade upgrades within those constraints;
a new major version (e.g. ~=8.3 to ~=9.0) is a deliberate change in pyproject.toml.
Every repository's Makefile is self-documenting -- run make help (or bare make) to
see its exact current targets and descriptions. The typical names are:
make reqs # re-compile requirements.txt and requirements-test.txt
make reqs-upgrade # upgrade all dependencies to their latest compatible versionsImportant
Do not modify requirements.txt or requirements-test.txt manually.
Note
After changing requirements.txt, rebuild the docker image (make docker-build)
to use it locally.
If the project uses Apache Beam, keep the Beam SDK image in the Dockerfile
at the same version as the apache-beam pin in requirements.txt.
There are two main Git workflows commonly used in software development: Git Flow and
GitHub Flow. We encourage using GitHub Flow whenever possible, as it's a
lightweight workflow that supports rapid iteration, continuous delivery, and simpler
branching. It works especially well for teams that deploy frequently and want to keep
main always production-ready. However, if a project involves a long release cycle and
a long release process, or needs an unstable shared branch like develop for ongoing
integration, then Git Flow may be more appropriate. You can read a summary of both
workflows in GITHUB-FLOW.md and GIT-FLOW.md.
To work on a new feature or fix, create a branch from main (or develop, depending
on the workflow the repository follows).
- Branch names should be descriptive and concise, all lowercase and with words separated
by hyphens. Optionally, prefix with the JIRA ticket, e.g.
PIPELINE-2020-name-of-the-branch. - Write clear commit messages. See How to Write a Git Commit Message.
- Maintain a clean commit history in your feature branch. Use interactive rebase
(
git rebase -i) to squash, reorder, or edit commits.1 - If you're not using pre-commit hooks, run the
Makefile's quality-check targets (format,lint,codespell,typecheck) as much as possible before pushing. - Add unit tests for each piece of code: avoid connecting to external services during tests (mock as needed), and keep tests fast.
- When opening a pull request:
- Target the correct base branch, depending on the repository's Git Workflow.
- Keep it focused and limited in scope to simplify review.
- Link the related JIRA ticket in the description.
A Google Cloud Build that publishes a Docker image is triggered in either of these cases:
- A commit is merged into
main(ordevelop, for repositories using Git Flow). - A new tag is created.
The triggers are defined with Terraform in the repository's cloudbuild/ directory.
Footnotes
-
When performing a rebase, it's acceptable to
push --forceto your own feature branch, but never on shared branches. Protectmainand/ordevelopfrom force pushes via GitHub settings. ↩