kippo is intended to be a light-weight project tracker.
-
Install python 3.13.X
-
clone project from github https://github.com/monkut/kippo.git
-
Create the virtualenv and install the requirements:
Note: This will use the 'pipenv' created and added in the virtual environment
$ pipenv install
Prerequisites:
- docker
- pgcli (for local db creation)
- python 3.13
- pipenv
-
Install development requirements:
pipenv install --dev # enter environment pipenv shell -
Setup
pre-commithooks (black, isort):# assumes pre-commit is installed on system via: `pip install pre-commit` pre-commit install -
Configure environment variables:
kippo/kippo/settings.pyreads all deployment-specific values from the environment. For local development, onlyDEBUGand the postgres connection vars need to be set; everything else falls back to sensible defaults defined insettings.py.export DEBUG=True export DB_NAME=kippo export DB_USER=postgres export DB_PASSWORD=mysecretpassword export DB_HOST=127.0.0.1 export DB_PORT=5432
Optional variables that affect serving:
URL_PREFIX— prefix prepended toSTATIC_URL,LOGIN_REDIRECT_URL, etc. Required when the app is mounted under a stage path such as/prod/(e.g. on AWS API Gateway).STATIC_URLis derived asf"{URL_PREFIX}/static/"— do not set it directly.ALLOWED_HOSTS— comma-separated list; defaults to*.
-
Setup database:
# From the repository root run the following docker run --name postgres -e POSTGRES_PASSWORD=mysecretpassword -e POSTGRES_USER=postgres -p 5432:5432 -d postgres # create the database in the container pgcli -h localhost -p 5432 -U postgres -W # Create the database (make sure it matches the name defined in your kippo.settings.local configuration) > CREATE DATABASE kippo; > \q # Make migrations and migrate (create tables in the database) cd kippo python manage.py makemigrations python manage.py migrate # Load initial fixtures python manage.py loaddata `default_columnset` python manage.py loaddata default_labelset # Create management users python manage.py loaddata required_bot_users # load countries to database # - loads countries from accounts/fixtures/countries.csv python manage.py loadcountries # create bucket `python manage.py `create_required_buckets``
The following fixtures are prepared for local testing and development of the admin:
python manage.py loaddata testdata`creates the following:
Organizations:
- org1
- org2
User:
- admin (org1, org2)
- org1-user1 (org1)
- org2-user1 (org2)
- dualorg-user3 (org1, org2)
- unassigned-org1 (auto-created for org)
- unassigned-org2 (auto-created for org)
Required Bot Users:
- cli-manager
- github-manager
Where user passwords are set to: 5up3r-53cr3t-p@$$w0rd
The Django backend serves the monkut/kippo-ui
React SPA under /ui/. The SPA bundle is not vendored in this repo — it is downloaded
from the latest monkut/kippo-ui GitHub release at deploy time by the update_ui
management command.
uv run poe update-uiThis poe task is a sequence that runs:
uv run python manage.py update_ui
uv run python manage.py collectstatic --noinputupdate_ui downloads kippo-ui-build-prod.tar.gz from the latest release of
monkut/kippo-ui and extracts the client/ directory into static/ui/
(at the repo root). collectstatic then copies it into STATIC_ROOT
(kippo/staticfiles/) where whitenoise can serve it.
collectstaticis mandatory afterupdate_ui. Without it,/ui/*returns 404 from Django even though the API still works — theupdate_uistep alone writes to the source directory only, not toSTATIC_ROOT.
The kippo-ui Vite bundle hard-codes its asset base URL at build time. To serve
the SPA from a non-prod API Gateway stage (e.g. the dev or stg stage where
assets live under /dev/static/ui/... or /stg/static/ui/...), select the
stage-matched tarball with --base-prefix:
# Dev stage (Zappa dev stage, URL_PREFIX=dev)
uv run python manage.py update_ui --base-prefix=/dev
uv run python manage.py collectstatic --noinput
# Staging stage (Zappa stg stage, URL_PREFIX=stg)
uv run python manage.py update_ui --base-prefix=/stg
uv run python manage.py collectstatic --noinput
# Or via env var (no poe-task changes needed)
KIPPO_UI_BASE_PREFIX=/dev uv run poe update-uiResolution order, highest precedence first:
--tarball-name <name>— explicit override (existing escape hatch from #256)--base-prefix <prefix>— mapped via theTARBALL_BY_PREFIXtableKIPPO_UI_BASE_PREFIXenv var — same mapping as--base-prefix- Default
--base-prefix=/prod→kippo-ui-build-prod.tar.gz(no production regression)
Known prefixes and their tarballs:
--base-prefix |
Release asset |
|---|---|
(unset, defaults to /prod) |
kippo-ui-build-prod.tar.gz |
/prod |
kippo-ui-build-prod.tar.gz |
/stg |
kippo-ui-build-stg.tar.gz |
/dev |
kippo-ui-build-dev.tar.gz |
An unknown --base-prefix (e.g. /staging) fails fast with a CommandError
that lists the known prefixes — use --tarball-name=<name> to override
explicitly when needed.
Note: the
/devand/stgpaths require the matchingmonkut/kippo-uiCI change that publishes per-stage tarballs alongside the existing prod tarball to be merged and a release cut. Until then,--base-prefix=/devor--base-prefix=/stgwill fail at the asset-lookup step with "tarball not found in release".
update_ui calls the unauthenticated GitHub API. If you hit a 403 rate-limit,
set a personal access token (any token with public_repo scope) and rerun:
export GITHUB_TOKEN=<your-pat>
uv run poe update-uiThe retry path in update_ui.py currently handles only 5xx and 429 — 403s are
not retried automatically.
The Django URL conf (kippo/kippo/urls.py) catches every path under /ui/ with a
SPAView that returns the SPA's index.html so React Router can take over
client-side. SPA assets (JS/CSS) are served from /static/ui/assets/ by whitenoise.
If SPAView raises Http404("UI not installed. Run 'uv run poe update-ui' to install."),
either update_ui was never run or collectstatic was skipped after running it.
kippo uses whitenoise middleware to serve static
files in production. Relevant settings (in kippo/kippo/settings.py):
| Setting | Value | Notes |
|---|---|---|
STATIC_URL |
f"{URL_PREFIX}/static/" |
Derived from URL_PREFIX env var |
STATIC_ROOT |
<repo>/kippo/staticfiles/ |
Target of collectstatic |
STATICFILES_DIRS |
[("ui", "<repo>/static/ui/")] if present |
Populated by update_ui |
WHITENOISE_STATIC_PREFIX |
/static/ |
See whitenoise issue #164 |
STATICFILES_STORAGE |
whitenoise.storage.CompressedManifestStaticFilesStorage |
See note below |
Note on
STATICFILES_STORAGEand Django 5.2. The setting was deprecated in Django 4.2 and removed in 5.1, so on the current Django 5.2 dependency it is silently ignored — manifest hashing and.gzprecompression are not active. Whitenoise still serves/static/*fromSTATIC_ROOTvia middleware, so files load correctly, just without cache-busting hashes. Migration to the Django 5.1+STORAGESdict is tracked in #258. Any such migration must exclude thestatic/ui/bundle from manifest re-hashing because Vite already pre-hashes those filenames and the SPA'sindex.htmlhard-codes them.
Optionally, the environment variable, PROJECTID_MAPPING_JSON_S3URI may be defined to periodically write the Active
ProjectIds to Project names in the following json format:
{
"last_updated": "2020-10-01T01:10:00+9:00",
"{KippoProject.id (uuid)}": "{KippoProject.name}"
}NOTE: appropriate permissions need to be applied to the related kippo execution role
To enable this feature the envar must be defined and related Cloudwatch event set to fire the following handler periodically (daily expected):
projects.handlers.functions.handle_write_projectid_mapping_event