Contributing#
Everything in this page is run from a checkout. If you only want to use the package, How to install the backend is the page you want instead.
If you are a coding agent, read
AGENTS.md
in the repository root. It covers the same ground more tersely, and adds the
traps that are only discoverable by hitting them.
The repository holds both halves#
Directory |
Holds |
|---|---|
|
The Plone add-on |
|
The Volto add-on |
|
These pages, and the screenshot harness that illustrates them. |
They are one repository on purpose. A change to a REST API payload and the change to the component that reads it are one commit, and a reference page cannot drift from the source it documents because both are in the same checkout.
The two packages are released separately, under different licences:
pas.plugins.identity is GPL-2.0-only on PyPI, @plone-collective/volto-identity
is MIT on npm.
Set up a checkout#
What you need#
Install#
git clone git@github.com:collective/pas-plugins-identity.git
cd pas-plugins-identity
make install
That installs both halves. make backend-install and make frontend-install
do one each.
Run it#
make backend-create-site # first run only
make backend-start # http://localhost:8080/
Then, in a second shell:
make frontend-start # http://localhost:3000/
To see the whole package working at once instead, run the two-site federation in Docker: Federate two Plone sites.
What a change has to satisfy#
Run these before opening a pull request. CI runs the same ones.
Command |
Checks |
|---|---|
|
|
|
|
|
That core never imports the |
|
The documentation, with warnings as errors |
|
Prose style. Errors must be zero; warnings are advisory |
Formatting and linting#
Half |
Tool |
Does |
Configured in |
|---|---|---|---|
backend |
Ruff |
Formats Python, sorts imports |
|
backend |
|
Formats XML and ZCML |
— |
backend |
Pyroma |
Checks package metadata |
— |
backend |
check-python-versions |
Checks the declared Python versions |
— |
frontend |
ESLint |
Lints JavaScript and TypeScript |
|
frontend |
prettier |
Formats JavaScript and TypeScript |
|
frontend |
Stylelint |
Formats CSS, Less and Sass |
|
Each runs from backend/ or frontend/ on its own as well.
Important
Run make format before staging. A formatter that rewrites a file after
you have staged it leaves the staged and working copies disagreeing, and the
commit carries whichever half you were not looking at.
The layer boundary is a contract#
make check-imports runs import-linter
contracts asserting that core never imports the optional server layer. It is
not part of make lint, so run it yourself when you touch either layer.
A soft import counts: import-linter reads function bodies, so moving an import inside a function does not get past it. That is deliberate—see About the two layers.
Conventions#
Changelog#
Every change carries a towncrier news fragment. There are three scopes, and a change adds one to each it touches:
Scope |
Folder |
|---|---|
Repository and documentation |
|
Backend |
|
Frontend |
|
Name it <issue>.<type> when an issue exists, +<slug>.<type> when none does.
The types are breaking, feature, bugfix, documentation, internal and
tests. Write it in the past tense, for somebody reading the changelog rather
than the diff, and end with your GitHub handle.
Tests#
A bugfix carries a test that fails without it. Verify that by removing the fix and watching the test go red—a regression test nobody has seen fail is a regression test that may be asserting nothing.
The backend suite uses pytest-plone. Tests needing Docker are marked
docker and skipped without it.
Documentation#
docs/STYLE.md is the house style, and it is short. The parts that catch
people out:
The code is the source of truth. Cite the file a fact came from in an HTML comment under the heading it supports.
No "should" language. Either it does, or you have not run it—and then say that.
Reference pages are tables. Rationale belongs in
concepts/.Diagrams are Mermaid, never images. Screenshots are captured by the harness in
docs/screenshots/, never by hand.Every page ends with Related or Next steps.
Translations#
make i18n
Generates the translation files for both halves.
Releasing#
Both packages are released by one command, from a clean checkout of main
with every gate above green.
uvx repoplone release a
The argument is the next version, either spelled out or as a segment—a,
b, rc, major, minor, patch, or release for the final one.
uvx repoplone versions next prints what each of them resolves to for all
three components, and --dry-run walks the pipeline without publishing.
repoplone reads repository.toml and runs eight steps: it shows the
changelog it is about to write, settles the version, updates version.txt and
the root changelog, releases the backend, releases the frontend, commits, tags,
and opens a GitHub release. It asks before it starts.
Half |
What the release step does |
|---|---|
Backend |
Compiles the |
Frontend |
Drives |
Two things follow from that, and both bite if forgotten.
You have to be authenticated to PyPI and to npm before you start. There is no step that stops halfway and asks.
The frontend must be published by
pnpm, nevernpm. Its peer dependencies are written asworkspace:*andcatalog:, which onlypnpmrewrites into real version ranges. Annpm publishuploads those literally, and the result cannot be installed.
towncrier removes each fragment it has consumed, so the news folders come back empty and the changelogs carry the entries.
Reporting a security vulnerability#
Not through the issue tracker. Follow SECURITY.md.