Develop#

Set up the development environment and run the site:

make dev        # Python, the packages, the git hooks, the recording of AI prompts and the database
make run        # the site on http://localhost:5000

In a second terminal, run make livehtml to serve this documentation at http://localhost:8000/. make run points documentation, changelog, error descriptions and help links to that local server unless DOCS_URL is already set in the environment or .env. Configured values take precedence.

Both commands accept DOCS_PORT if port 8000 is already in use:

make livehtml DOCS_PORT=8001
make run DOCS_PORT=8001

make dev needs uv and can be run again at any time, for example after the packages changed. make run reads the settings of .env if it exists. Use the local test configuration below to start. After loading it, missing or empty development settings get these defaults: DEBUG=1, SITE_URL=http://localhost:5000, SITE_NAME=localhost, ALLOWED_HOSTS=localhost,127.0.0.1,[::1], CSRF_TRUSTED_ORIGINS=http://localhost:5000 and DOCS_URL=http://localhost:8000/. An explicit SITE_URL supplies the default site name, allowed host and CSRF origin. Explicit values, including DEBUG=0, are preserved. The existing secret-key generation and SQLite database defaults continue to apply. GitHub credentials come from your GitHub App configuration. Without a GitHub App, the pages can be seen but signing in shows SignInNotConfigured. Another port: make run PORT=5001.

Configure the site#

Keep the local test settings in a file named .env at the root of the checkout. Start with the development example, preserving an existing file:

test -f .env || cp .env.development.example .env

The example contains the settings to fill in:

# Local test setup: copy this file to .env, then fill in the GitHub App values.
# make run and make app-manifest both load .env automatically.
# Restart make run after editing .env. Keep real credentials out of version control.

# Choose a unique app name before generating the manifest.
SITE_NAME=
# Empty values use localhost with PORT=5000 and DOCS_PORT=8000 by default.
SITE_URL=
DOCS_URL=

# Set all six values from your development GitHub App.
GITHUB_APP_ID=
GITHUB_APP_SLUG=
GITHUB_CLIENT_ID=
GITHUB_SECRET=
GITHUB_WEBHOOK_SECRET=
GITHUB_APP_PRIVATE_KEY_FILE=data/github-app.pem

# make run supplies DEBUG=1, allowed hosts and CSRF origins when unset.
# The secret key and SQLite database are created in data/ automatically.
# Start the documentation in a second terminal with make livehtml.

Set the six GITHUB_* values from your development GitHub App, and save its private key at the path named by GITHUB_APP_PRIVATE_KEY_FILE. GITHUB_SECRET is the client secret; GITHUB_WEBHOOK_SECRET is the separate webhook secret. Choose a unique SITE_NAME before creating the app. Start at Set up the GitHub App to choose the recommended submission form, manifest CLI flow or manual app creation and obtain the credentials.

Run make run after saving the file, or stop and restart it if it is already running. Both make run and make app-manifest load .env automatically; no shell export commands are needed. The file is excluded from version control. Leave SITE_URL and DOCS_URL empty to use the local defaults and any PORT / DOCS_PORT overrides. The general .env.example lists additional settings for deployments.

Generate the development GitHub App manifest with the same defaults:

make app-manifest

Its site, redirect, OAuth callback, setup and webhook URLs use http://localhost:5000 by default. Use the same PORT value for make run and make app-manifest when choosing another port. Set SITE_URL explicitly for a tunnel or another address; Test the GitHub App locally with Tailscale exposes the local site with Tailscale Funnel so that GitHub can deliver webhooks. These defaults apply only to development commands; production settings keep their existing defaults.

make test       # the tests with coverage
make lint       # ruff
make docs       # this documentation
make reuse      # the licenses
make prose      # the prose of the documentation (Vale)
make workflows  # the security of the GitHub workflows (zizmor)

make prose checks the documentation, README.md and CONTRIBUTING.md with the spelling and repetition rules of Vale (.vale.ini). Add words of the project that Vale does not know to .vale/styles/config/vocabularies/AiPycal/accept.txt, one per line. make workflows checks .github/workflows with zizmor; the actions are pinned to the commit of their release, with the version as a comment. Both need the network the first time to download the tools.

make test runs the tests on all processors with pytest-xdist (-n auto); each worker has its own test database. make test JOBS=4 limits the workers and make test JOBS=0 runs everything in one process, which is easier to debug. A single test runs without workers: uv run pytest tests/site/test_brand.py. The documentation and the browser tests stay in one process: they are not faster in parallel.

The tests never use the network: tests/fakes/github.py is a fake GitHub that answers the requests of the site. The test fixture in tests/data/agreements.yml makes both agreements mandatory to exercise signing checks. The example repository keeps the AI policy optional; a regression check verifies that this is their only configuration difference and that the agreement texts match.

The rules for the code, such as one class per module and one error class per cause, are in AGENTS.md.

Browser tests#

The optional Chromium suite exercises the three setup guides and the manifest submission form, including browser validation, popup submission and the state download. Install the browser once, then run the suite:

uv run --group browser playwright install --with-deps --only-shell chromium
make browser-test

make browser-test builds the documentation and runs tests/browser. These tests are separate from make test so the regular suite needs no browser installation. Browser and package installation need network access; the tests themselves serve built pages through intercepted requests and block external requests. Registrations sent to GitHub receive fake responses, including those opened in a new tab; no app is created. CI runs the suite in its browser job, and its Docker job builds and checks the container with make docker-test.

Run the site with a GitHub App#

Create a second GitHub App for development by following Set up the GitHub App. Use the recommended submission form or choose the CLI or manual flow, with http://localhost:5000 as address, and put its settings into .env. GitHub can not send webhooks to localhost: make the local site public as Test the GitHub App locally with Tailscale shows.

Publish the documentation#

The CI builds this documentation with make docs for every change and publishes the build of main on GitHub Pages at https://docs.ai.pycal.org/ (the jobs docs and pages of .github/workflows/tests.yml). Pull requests build it without publishing; warnings fail the build.

The repository has GitHub Pages set to GitHub Actions with the custom domain docs.ai.pycal.org. The DNS of pycal.org needs the record docs.ai.pycal.org. CNAME pycalendar.github.io.; after GitHub checked it, enable Enforce HTTPS in the Pages settings of the repository.