Development¶
Required developer dependencies¶
Install all of these tools before running the project setup:
- Git
- Bash
- ripgrep (
rg) - Python 3.12 or newer (CI tests Python 3.12 and 3.14)
- uv
- Node.js 22.13 or newer with npm (Node.js 23 is not supported)
- just
- git-cliff, required by
just changelogand the release-preparation workflow - curl, used by the Docker deployment smoke tests
- OpenSSL, used to generate secrets for Docker Compose deployments
- Docker Engine
- A current Docker Compose release, available as either
docker composeordocker-compose
Verify the container tooling before running deployment tests:
Set up¶
Setup verifies Git, Node.js, npm, and uv, installs every locked Python
dependency group, installs the locked frontend dependencies, ensures the
Playwright Chromium browser is available for end-to-end tests, and
records successful initialization in .init/setup. It is safe to rerun;
an existing setup rechecks the browser installation. Use the following
commands when a completely fresh environment is needed:
just clean removes generated caches (including the uv cache), reports,
and build outputs while preserving installed environments. just reset
additionally removes .venv, frontend node_modules, and the setup
marker.
Start the backend and frontend development servers together:
The recipe generates an ephemeral render-token secret unless
QR_RENDER_TOKEN_SECRET is already set, waits for the backend to become
healthy, and then starts Vite. Press Ctrl+C to stop both servers.
The frontend development server proxies /api and /health to the
backend on port 8080.
Local and custom container images¶
Build the production image from the current checkout with:
This creates the local qrcode:local image. The repository's tracked
Compose files build the checkout by default and use the same tag. Set
QR_IMAGE to apply a custom local tag while building:
The optional local Nginx reference configuration builds the same checkout, removes the application host port, and exposes the proxy on loopback port 8081:
These source-tree Compose workflows are for development and deployment testing. Deploy the published application image by following the getting-started guide.
Quality checks¶
just test is the normal pre-commit host test suite and runs the backend
and frontend unit/integration tests. just check adds formatting,
linting, type checking, a strict documentation build, and dependency
license validation. Browser and Docker deployment suites remain separate
because they require additional host services or installed browser
binaries.
For vCard changes, supplement automated QR decoding with manual imports
on current iOS and Android devices. Test both manually entered and
.vcf-imported contacts in Apple Contacts and Google Contacts. Verify
the structured name, phone labels, email, company and title, work
address, and website, and record the device, OS, scanner, and contact
application versions in the pull request.
Run the complete on-host deployment gate with:
This builds the production image and runs both the application and proxy
deployment suites against the host Docker Engine. The scripts accept
either the docker compose plugin form or the standalone
docker-compose command. The image installs pinned uv tooling and
syncs its production environment from uv.lock with Python 3.14.
Individual suites remain available with:
The application suite verifies health, a real preview/download cycle, the packaged frontend, loopback binding, OCI metadata, resource limits, the non-root runtime user, read-only root filesystem, and graceful shutdown. The proxy suite verifies private application networking plus loopback binding and the documented request-size and rate limits.
Changelog and releases¶
Pull-request titles use Conventional Commits because squash merges make
the title the commit subject on main. CI validates the title. The
supported user-facing types are:
| Type | Changelog section |
|---|---|
feat |
Added |
change |
Changed |
deprecate |
Deprecated |
remove |
Removed |
fix |
Fixed |
security or fix(security) |
Security |
perf |
Performance |
deploy |
Deployment & Operations |
docs |
Documentation |
build(deps) or build(deps-dev) |
Dependencies |
revert |
Reverted |
Use an optional scope when it adds useful context, for example
fix(a11y): label the preview or
deploy(compose): document the proxy network. Mark a breaking change
with !, as in deploy!: rename the render secret, and explain the
migration in the commit body's BREAKING CHANGE: footer. Routine
build, chore, ci, refactor, style, and test commits do not
appear in the changelog unless they are breaking.
Preview changelog-visible commits since the latest release without changing files:
Prepare a release from a clean release-preparation branch with an explicit bare semantic version:
just bump 0.2.0
just check
git add CHANGELOG.md changelogs pyproject.toml uv.lock frontend/package.json frontend/package-lock.json
git commit -m "chore(release): prepare for 0.2.0"
The bump command updates both application versions and lockfiles,
generates the release section, and restores the original files if a
step fails. It is safe to rerun the same untagged version after adding
release changes. The initial 0.1.0 preparation promotes the curated
Unreleased baseline. Later patch and prerelease entries remain in the
active X.Y line; starting a new minor or major line moves older
entries to files under
the changelog archive.
After the release-preparation change is merged, update local main and
create the release tag:
Tagging requires a clean main that exactly matches origin/main,
synchronized Python and frontend versions, committed release notes, and
a tag that does not already exist. It creates and pushes one annotated
vX.Y.Z tag. The tag workflow reruns every release gate, publishes the
Docker images, and creates the GitHub Release from the committed
changelog section. Prerelease versions such as 0.2.0-rc.1 create
GitHub prereleases and do not update the Docker latest tag.
Documentation¶
Documentation sources are Markdown files in docs/. Preview the site at
http://localhost:8000 while editing:
Build the same strict static site used by GitHub Pages:
The generated site/ directory is build output and must not be
committed.
Continuous integration¶
Pull requests and pushes to main run formatting, linting, type checks,
backend and frontend tests, strict documentation builds,
direct-dependency license policy checks, and the complete on-host Docker
deployment gate. Pull requests, pushes to main, manual quality runs,
and release tags also run desktop/mobile browser tests. Failed browser
runs retain their Playwright report and traces as workflow artifacts for
14 days. Release tags rerun every gate against the exact release
candidate before publishing images.
The security workflow runs independent Python and npm dependency
audits, CodeQL analysis, repository scanning, and the license inventory
check for Dependabot pull requests, relevant pushes to main, and its
weekly schedule. Every job writes a result table to the workflow
summary, while the scanner step logs contain the detailed reports. The
jobs run in parallel, so one finding does not prevent the other audits
from finishing. The workflow creates no issue, artifact, dependency
commit, or pull request.
To receive one email when a scheduled audit fails, open the GitHub notification settings and select Actions, Email, and Only notify for failed workflows. Scheduled-run notifications go to the user who created the workflow schedule. If necessary, the intended recipient can take ownership by disabling and re-enabling the workflow or by updating its cron expression. Repository administrators should also enable the dependency graph and Dependabot Alerts under the repository's Advanced Security settings. Leave automatic Dependabot security updates disabled when remediation pull requests should remain maintainer-controlled.
Dependabot proposes routine version updates only for direct dependencies declared in the uv, npm, Docker, Docker Compose, and GitHub Actions manifests. Dependabot Alerts and the scheduled audits continue to cover the complete resolved dependency graph, including transitive dependencies. Review alerts in the repository's Security view and use the failed workflow's summary and logs for the corresponding audit details. For uv and npm, Dependabot leaves a declared range unchanged when it already permits the target release. It edits the manifest only when admitting the release requires a range change. Minor and patch uv and npm updates are grouped by ecosystem, while major updates receive individual pull requests. Docker, Docker Compose, and GitHub Actions updates also remain individual.
Treat Dependabot pull requests as the routine direct-dependency update workflow. Review the release notes and the manifest and lockfile diff, including any resolver-required transitive changes. Major updates need explicit compatibility review and relevant focused tests in addition to the required checks. Minor and patch updates from Dependabot are eligible for squash auto-merge after the application quality, browser, container, and Dependabot security gates pass. The security gate requires the repository scan, Python and JavaScript audits, license policy, and CodeQL analysis to succeed.
GitHub branch protection must require branches to be up to date before
merging. Repository administrators must enable squash merging and
auto-merge, allow GitHub Actions read and write workflow permissions,
then require the Application quality, Browser tests,
Production container, and Dependabot security gate checks on
main. Dependabot uses its default automatic rebase behavior to refresh
open pull requests on scheduled update checks and after conflicts with
the target branch. Maintainers can request an immediate refresh by
commenting @dependabot rebase on the pull request.
Major updates, updates without recognized semantic-version metadata, and pull requests with maintainer changes remain manual. If an eligible update does not enable auto-merge, verify the repository settings and request a rebase so the synchronize event reevaluates the pull request.
Dependabot classifies a SHA-pinned GitHub Action update from its
associated release tag, so minor and patch releases remain eligible.
It does not expose Renovate's separate pinDigest update type or create
first-time digest pins. Runtime, rendering, or build dependency changes
that are not eligible for auto-merge should also run
just deployment-test and just test-e2e when applicable.
When a security audit finds a vulnerable transitive dependency, prepare
one fix(security): pull request for the compatible findings from that
audit cycle. Use the ecosystem resolver rather than editing a lockfile
by hand: target a Python package with
uv lock --upgrade-package <package> and use a non-forced npm audit fix
or targeted npm lockfile update for frontend dependencies. Do not use
npm audit fix --force or a dependency override as the normal update
path.
If the fixed transitive version satisfies its parent's declared constraints, review the resolver-generated lockfile changes and run the complete quality suite. If the parent excludes every fixed version, update or replace the parent rather than forcing an incompatible transitive version. Keep any temporary risk acceptance or mitigation explicit and documented in the security pull request.
Release tags publish multi-architecture linux/amd64 and linux/arm64
images to Docker Hub and publish matching GitHub Releases. Fork
maintainers who publish their own images configure these GitHub values:
- Repository variable
DOCKERHUB_REPOSITORY, containingnamespace/qrcode - Repository variable
DOCKERHUB_USERNAME - Repository secret
DOCKERHUB_TOKEN, containing a Docker Hub access token
Pushes that change README.md on main synchronize the Docker Hub
Repository Overview and short description with the repository. The
workflow can also be run manually after configuring Docker Hub or when
the overview needs to be refreshed.
Stable releases publish their exact semantic version, a mutable
major/minor tag, and latest. Releases at 1.0.0 and later also update
a mutable major tag; major-zero releases omit the 0 tag. Prereleases
publish only their exact version. GHCR publishing remains a later
addition and will use the same tag policy.