Skip to content

Add Create a backend add-on section in the Developer guide with GenericSetup#1757

Open
stevepiercy wants to merge 28 commits into
6.0from
dev-guide-create-a-backend-add-on
Open

Add Create a backend add-on section in the Developer guide with GenericSetup#1757
stevepiercy wants to merge 28 commits into
6.0from
dev-guide-create-a-backend-add-on

Conversation

@stevepiercy
Copy link
Copy Markdown
Contributor

@stevepiercy stevepiercy commented Nov 6, 2024

See #1748 (comment)

Closes #1754.

Ping @petschki to take it from here.


📚 Documentation preview 📚: https://plone6--1757.org.readthedocs.build/

Ping @petschki
- Enhance the Development Guide index
- Update the term Buildout in the Glossary to point to dusty old, yet useful, docs in Plone 4.

(cherry picked from commit 8142b96)
- Add term reference for Buildout
Comment thread docs/developer-guide/create-a-backend-add-on.md Outdated
@stevepiercy stevepiercy changed the title Add Create a backend add-on section in the Developer guide. Add Create a backend add-on section in the Developer guide with GenericSetup Nov 24, 2024
@stevepiercy
Copy link
Copy Markdown
Contributor Author

I'm ready to close this issue as won't fix. Does anyone on @plone/classicui-team care about the topic of how to add or create a backend add-on section in the Developer guide with GenericSetup? If so, speak up, and let's get this finished.

@petschki
Copy link
Copy Markdown
Member

@stevepiercy will have a look at this.

@petschki
Copy link
Copy Markdown
Member

I've created a basic "addon creation documentation" and moved the old docs for GenericSetup here. I've also removed several outdated parts/link and tried to update the XML examples. Not finished yet though.

I would recommend to place these docs all inside "Backend" and make a link from "Developer guide / Develop backend add-ons" -> "Backend / Add-ons" (like its done for Volto add-ons)

Further we can add references for several plonecli installation guides (like Classic-UI / theming / create theme addon) to the new "Backend / Add-on" doc ...

What do you think?

@petschki
Copy link
Copy Markdown
Member

petschki commented Nov 25, 2025

what's also missing is something like "Installing backend Add-on" ... at least I didn't find it... would also make sense to add it to "Backend / Add-ons" chapter.

EDIT: I mean adding a pypi package or source checkout to cookieplone/buildout project ...

@stevepiercy
Copy link
Copy Markdown
Contributor Author

what's also missing is something like "Installing backend Add-on" ... at least I didn't find it... would also make sense to add it to "Backend / Add-ons" chapter.

EDIT: I mean adding a pypi package or source checkout to cookieplone/buildout project ...

Was this what you couldn't find?

https://6.docs.plone.org/admin-guide/add-ons.html

The plan is to merge the Admin section into Develop section so that people don't have to think of what role they're in versus the task they want to perform. See #1997.

Co-authored-by: Mikel Larreategi <mlarreategi@codesyntax.com>
@petschki
Copy link
Copy Markdown
Member

petschki commented Dec 9, 2025

@petschki @erral are there any other technical issues to resolve before I dive in for a general editorial review? Please let me know. Thank you!

I still need to go through the genericsetup doc technically. There are some outdated bits which have to be removed.

@stevepiercy
Copy link
Copy Markdown
Contributor Author

I still need to go through the genericsetup doc technically. There are some outdated bits which have to be removed.

@petschki I don't see a commit to remove the outdated bits. Did you forget to push it? Please let me know. Thank you!

@MrTango
Copy link
Copy Markdown
Contributor

MrTango commented Jan 15, 2026

I would use plonecli for all, after @erral did the final release in the comming days.
It will then work as it was working for Plone 5 development, with the difference that now the create addon part is using cookieplone backend_addon under the hood. This is exactly why we have plonecli, to hide stuff under it and give the developers a clean cli.

example:

uv tool install plonecli
plonecli create addon collective.myaddon

cd collective.myaddon
plonecli add content_type
...

make add, would be an abstraction, which calls an abstraction (plonecli), which calls a bobtemplate.
I don't mind that the make command exist, for who want to do it this way, but i would not put this out as the default, otherwise we can take out plonecli from the mix and do it purely in Make.

We could even create a full cookieplone project with plonecli.

plonecli create volto-project

or

plonecli create cookieplone-project

or

plonecli create plone-project

it doesn't matter and we are flexible to replace part under it, with the next cool tool, someone creates, without destroing exiting docs and api's.

my 50cent...

@petschki
Copy link
Copy Markdown
Member

petschki commented Jan 15, 2026

@MrTango I had documente the plonecli part initially and removed it as @erral's request ... can we find a consent here?

I think, due to the fact that the whole plone installation and theme development documentation uses cookieplone, we should at least use it for the inital addon package creation too ... when adding further features inside the addon, we can document this with using plonecli ...

@erral
Copy link
Copy Markdown
Member

erral commented Jan 15, 2026

@petschki @MrTango sorry for the mess...

with plonecli right now we can create an add-on calling plonecli create addon which creates a backend_addon calling uvx cookieplone backend_addon.

I haven't checked to do the same for volto addon, project, etc, but could be done, and it is not that hard.

If we do that we can document everything to use plonecli indeed, and cookieplone would be just an "implementation detail".

But we also have documentation to create Volto projects using uvx cookieplone so the mess is bigger...

@yurj
Copy link
Copy Markdown
Contributor

yurj commented Jan 15, 2026

cookieplone is the framework with utils to full manage a Plone project while plonecli is a client that implements the frameworks (past and present).

@stevepiercy
Copy link
Copy Markdown
Contributor Author

What are the use cases that we're trying to solve with this page of documentation? Maybe that answer will help us give a direction. Would y'all agree there are essentially two scenarios?

  1. Create a backend add-on only.
  2. Create a backend add-on along with a combination of anything else, such as a full Plone project, a Volto add-on, project documentation scaffold, and devops.

Neither Cookieplone nor plonecli are going away anytime soon. If we have two or more ways of doing the same thing, that's fine, too, provided we give sufficient information for the reader to choose which tool is best for them. For an example of that, see Choose a user interface. It's also OK if we use one tool to do one part, such as using Cookieplone to create a monorepo with all the things, and another tool to do another part, such as add new features to the backend add-on.

Does this help at all?

@petschki
Copy link
Copy Markdown
Member

@erral @MrTango I've now replaced the cookieplone part with my formerly removed plonecli part ...

erral
erral previously approved these changes Jan 16, 2026
@github-project-automation github-project-automation Bot moved this from In Progress to Approved in Plone Documentation Jan 16, 2026
Copy link
Copy Markdown
Member

@davisagli davisagli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's see if we can get this over the finish line.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just skimmed through to check that this is still accurate/relevant information. It is and we should prioritize getting this merged. It does need some editing to match current doc styles.

Comment thread docs/developer-guide/create-a-backend-add-on.md
@erral
Copy link
Copy Markdown
Member

erral commented Apr 16, 2026

I have just released new versions for mr.bob, plonecli and bobtemplates removing all pkg_resources style namespaces. I tested the three of them together and it works as expected. I will review this PR tomorrow to check that everything is Ok.

Copy link
Copy Markdown
Member

@erral erral left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have checked the output of uvx plonecli and it is a bit different than the previous version.

Comment on lines +39 to +87

--> Package description [An add-on for Plone]:

--> Plone version [6.0.0]:

--> Python version for virtualenv [python3]:

--> Do you want me to activate VS Code support? (y/n) [y]:



isort-apply: successful:
isort-apply: install_deps> python -I -m pip install isort -c constraints.txt
isort-apply: commands[0]> isort /Users/<username>/Development/collective.addon/src
/Users/<username>/Development/collective.addon/setup.py
Fixing /Users/<username>/Development/collective.addon/src/collective/addon/testing.py
Fixing /Users/<username>/Development/collective.addon/src/collective/addon/tests/test_setup.py
isort-apply: OK (2.57=setup[1.94]+cmd[0.63] seconds)
congratulations :) (2.59 seconds)


Identified `/` as project root containing a file system root.
Sources to be formatted: "Users/<username>/Development/collective.addon/src",
"Users/<username>/Development/collective.addon/setup.py"
src/collective/__init__.py wasn't modified on disk since last run.
src/collective/addon/browser/__init__.py wasn't modified on disk since last run.
src/collective/addon/locales/__init__.py wasn't modified on disk since last run.
src/collective/addon/tests/__init__.py wasn't modified on disk since last run.
src/collective/addon/interfaces.py already well formatted, good job.
reformatted src/collective/addon/__init__.py
reformatted src/collective/addon/setuphandlers.py
reformatted src/collective/addon/testing.py
reformatted setup.py
reformatted src/collective/addon/locales/update.py
reformatted src/collective/addon/tests/test_setup.py

All done! ✨ 🍰 ✨
6 files reformatted, 5 files left unchanged.

black-enforce: successful:
black-enforce: install_deps> python -I -m pip install black -c constraints.txt
black-enforce: commands[0]> black -v src setup.py
black-enforce: OK (2.60=setup[2.12]+cmd[0.48] seconds)
congratulations :) (2.61 seconds)


git init is disabled!
Generated file structure at /Users/<username>/Development/collective.addon/collective.addon
```
Copy link
Copy Markdown
Member

@erral erral May 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
--> Package description [An add-on for Plone]:
--> Plone version [6.0.0]:
--> Python version for virtualenv [python3]:
--> Do you want me to activate VS Code support? (y/n) [y]:
isort-apply: successful:
isort-apply: install_deps> python -I -m pip install isort -c constraints.txt
isort-apply: commands[0]> isort /Users/<username>/Development/collective.addon/src
/Users/<username>/Development/collective.addon/setup.py
Fixing /Users/<username>/Development/collective.addon/src/collective/addon/testing.py
Fixing /Users/<username>/Development/collective.addon/src/collective/addon/tests/test_setup.py
isort-apply: OK (2.57=setup[1.94]+cmd[0.63] seconds)
congratulations :) (2.59 seconds)
Identified `/` as project root containing a file system root.
Sources to be formatted: "Users/<username>/Development/collective.addon/src",
"Users/<username>/Development/collective.addon/setup.py"
src/collective/__init__.py wasn't modified on disk since last run.
src/collective/addon/browser/__init__.py wasn't modified on disk since last run.
src/collective/addon/locales/__init__.py wasn't modified on disk since last run.
src/collective/addon/tests/__init__.py wasn't modified on disk since last run.
src/collective/addon/interfaces.py already well formatted, good job.
reformatted src/collective/addon/__init__.py
reformatted src/collective/addon/setuphandlers.py
reformatted src/collective/addon/testing.py
reformatted setup.py
reformatted src/collective/addon/locales/update.py
reformatted src/collective/addon/tests/test_setup.py
All done! ✨ 🍰 ✨
6 files reformatted, 5 files left unchanged.
black-enforce: successful:
black-enforce: install_deps> python -I -m pip install black -c constraints.txt
black-enforce: commands[0]> black -v src setup.py
black-enforce: OK (2.60=setup[2.12]+cmd[0.48] seconds)
congratulations :) (2.61 seconds)
git init is disabled!
Generated file structure at /Users/<username>/Development/collective.addon/collective.addon
```
RUN: bobtemplates.plone:addon -O collective.addon
Welcome to mr.bob interactive mode. Before we generate directory structure, some questions need to be answered.
Answer with a question mark to display help.
Values in square brackets at the end of the questions show the default value if there is no answer.
--> Author's name [Mikel Larreategi]:
--> Author's email [mlarreategi@codesyntax.com]:
--> Author's GitHub username:
--> Package description [An add-on for Plone]:
--> Do you want me to initialize a GIT repository in your new package? (y/n) [y]:
--> Plone version [6.0.0]:
--> Python version for virtualenv [python3]:
--> Do you want me to activate VS Code support? (y/n) [y]:
╭──────────────────────────────── cookieplone ─────────────────────────────────╮
│ │
*******
***************
*** ***
*** *** ***
*** ***** ***
*** *** ***
*** *** ***
*** ***** ***
*** *** *** ***
*** ***** ***
*** *** ***
*** ***
***************
*******
│ │
╰──────────────────────────────────────────────────────────────────────────────╯
╭──────────────────────────────── Plone Addon ─────────────────────────────────╮
│ │
│ Creating a new Plone Addon │
│ │
│ Sanity check results: │
│ │
│ - Cookieplone: ✓ │
│ - uv: ✓ │
│ - git: ✓ │
│ │
╰──────────────────────────────────────────────────────────────────────────────╯
-> Remove files used in classic UI setup
-> Create namespace packages
-> Format code
-> Initialize Git repository
-> Generate documentation scaffold
-> Setup VSCode configuration
-> Setup GitHub CI
╭────────────────────────── New addon was generated ───────────────────────────╮
│ │
│ collective.addon │
│ │
│ Now, enter the repository, start coding, and push to your organization. │
│ │
│ Sorry for the convenience, │
│ The Plone Community. │
│ │
https://plone.org/
╰──────────────────────────────────────────────────────────────────────────────╯
Done
Generated file structure at /tmp/collective.addon

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @erral! The terminal output is very wide, though. The code block area when rendered in the docs starts to wrap at the 90th character, requiring users to horizontal scroll back and forth to view the output. Would you please narrow your terminal session window and run it again?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, let's try again

@github-project-automation github-project-automation Bot moved this from Approved to In Progress in Plone Documentation May 3, 2026
Copy link
Copy Markdown
Contributor Author

@stevepiercy stevepiercy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I started a review, and I'm not sure what remains in here about archetypes. Anyone know?

I'm also slowly going through this to pass editorial review, which will be in subsequent commits.

Comment on lines +18 to +21
```{todo}
remove archetypes example code everywhere
```

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either remove this todo or remove the code to which it refers.

Comment on lines +39 to +87

--> Package description [An add-on for Plone]:

--> Plone version [6.0.0]:

--> Python version for virtualenv [python3]:

--> Do you want me to activate VS Code support? (y/n) [y]:



isort-apply: successful:
isort-apply: install_deps> python -I -m pip install isort -c constraints.txt
isort-apply: commands[0]> isort /Users/<username>/Development/collective.addon/src
/Users/<username>/Development/collective.addon/setup.py
Fixing /Users/<username>/Development/collective.addon/src/collective/addon/testing.py
Fixing /Users/<username>/Development/collective.addon/src/collective/addon/tests/test_setup.py
isort-apply: OK (2.57=setup[1.94]+cmd[0.63] seconds)
congratulations :) (2.59 seconds)


Identified `/` as project root containing a file system root.
Sources to be formatted: "Users/<username>/Development/collective.addon/src",
"Users/<username>/Development/collective.addon/setup.py"
src/collective/__init__.py wasn't modified on disk since last run.
src/collective/addon/browser/__init__.py wasn't modified on disk since last run.
src/collective/addon/locales/__init__.py wasn't modified on disk since last run.
src/collective/addon/tests/__init__.py wasn't modified on disk since last run.
src/collective/addon/interfaces.py already well formatted, good job.
reformatted src/collective/addon/__init__.py
reformatted src/collective/addon/setuphandlers.py
reformatted src/collective/addon/testing.py
reformatted setup.py
reformatted src/collective/addon/locales/update.py
reformatted src/collective/addon/tests/test_setup.py

All done! ✨ 🍰 ✨
6 files reformatted, 5 files left unchanged.

black-enforce: successful:
black-enforce: install_deps> python -I -m pip install black -c constraints.txt
black-enforce: commands[0]> black -v src setup.py
black-enforce: OK (2.60=setup[2.12]+cmd[0.48] seconds)
congratulations :) (2.61 seconds)


git init is disabled!
Generated file structure at /Users/<username>/Development/collective.addon/collective.addon
```
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @erral! The terminal output is very wide, though. The code block area when rendered in the docs starts to wrap at the 90th character, requiring users to horizontal scroll back and forth to view the output. Would you please narrow your terminal session window and run it again?

Copy link
Copy Markdown
Contributor Author

@stevepiercy stevepiercy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After performing an editorial review and creating a separate PR against this one, then reading through these latest two sections, there is no way that this PR can be merged. It refers to obsolete practices, things that don't actually work, and it wanders without clear direction or context.

I understand it's the result of decades of copy paste and ad hoc contributions. It's going to take a lot of collaboration between those with the technical and historical knowledge and me to make it understandable for readers who don't have decades of experience with Plone.

To move this forward, please take a look at #2083 and add your comments there. Thank you!

Comment on lines +264 to +295
## Metadata version numbers

Some old add-on packages may have a `metadata.xml` without version number, but this is considered bad practice.

What should the version number in your `metadata.xml` be?

This mostly matters when you are adding upgrade steps, see also the [Upgrade steps] section.

Upgrade steps have a sort order in which they are executed. This used to be alphabetical sorting.

When you had eleven upgrade steps, marked from 1 through 11, alphabetical sorting meant this order: 1, 10, 11, 2, 3, etc.

If you are seeing this, then you are using an old version of GenericSetup.

You want numerical sorting here, which is correctly done currently. Versions with dots work fine too.

They get ordered just like they would when used for packages on PyPI.

Best practice for all versions of GenericSetup is this:

- Start with 1000.
This avoids problems with ancient GenericSetup that used alphabetical sorting.
- Simply increase the version by 1 each time you need a new metadata version.
For example: 1001, 1002, etc.
- If your add-on package version number changes, but your profile stays the same and no upgrade step is needed, you should **not** change the metadata version.
There is no need.
- If you make changes for a new major release, you should increase the metadata version significantly.
This leaves room for small metadata version increases on a maintenance branch.
Example:
You have branch master with version 1025.
You make backwards incompatible changes and you increase the version to 2000.
You create a maintenance branch where the next metadata version will be 1026.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section is too much. I think it should be rewritten to promote Python version specifiers as described in https://packaging.python.org/en/latest/specifications/version-specifiers/, and mention that if there are integer numbers or none, then use Python version specifiers.

Would that break anything? @mauritsvanrees or anyone else with historical knowledge on this topic.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stevepiercy I think this section is an accurate, if a bit informal, description of how GenericSetup profile versions are used in practice. I have never seen one that is not an integer....this claims that works, but I can't confirm that without trying it. I would be very surprised if more complex Python version specifiers (with e.g. dev/a/b/rc) worked here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davisagli thanks for the feedback. This is what I latched onto.

... Versions with dots work fine too.

They get ordered just like they would when used for packages on PyPI.

Which implies the sort order is by Python version specifiers, not alphabetic, numeric, or alphanumeric. But then a version with a dot could also be a decimal. Agh! GenericSetup's change log was no help to me either.

It would be nice to be certain of what is actually supported. If certainty is not possible, then I can simplify this section to specify integers only, while dropping mention of dots or PyPI versions for metadata versions. It might not be complete, but accuracy and clarity take precedence.

Comment on lines +299 to +371
## Custom Installer Code (`setuphandlers.py`)

Besides out-of-the-box XML steps which provide both install and uninstall,
GenericSetup provides a way to run custom Python code when your add-on package is installed and uninstalled.

In `configure.zcml`:

```
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:genericsetup="http://namespaces.zope.org/genericsetup"
i18n_domain="your.addonpackage">

<genericsetup:registerProfile
name="default"
title="Your Add-on Package"
directory="profiles/default"
description="A useful package"
provides="Products.GenericSetup.interfaces.EXTENSION"
pre_handler="your.addonpackage.setuphandlers.run_before"
post_handler="your.addonpackage.setuphandlers.run_after"
/>

</configure>
```

In `setuphandlers.py`:

```python
def run_before(context):
# This is run before running the first import step of
# the default profile. context is portal_setup.
# If you need the same context as you would get in
# an import step, like setup_various below, do this:
profile_id = 'profile-your.addonpackage:default'
good_old_context = context._getImportContext(profile_id)
...

def run_after(context):
# This is run after running the last import step of
# the default profile. context is portal_setup.
...
```

The best practice is to create a `setuphandlers.py` file which contains a function `setup_various()` which runs the required Python code
to make changes to Plone site object.

This function is registered as a custom `genericsetup:importStep` in XML.

```{note}
When you write a custom `importStep`, remember to write uninstallation code as well.
```

However, the trick is that all GenericSetup import steps, including your custom step, are run for *every* add-on package when they are installed.

If your need to run code which is **specific to your add-on install only** you need to use a marker text file which is checked by the GenericSetup context.

Also you need to register this custom import step in `configure.zcml`:

```xml
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:genericsetup="http://namespaces.zope.org/genericsetup">

<genericsetup:importStep
name="your.addonpackage"
title="your.addonpackage special import handlers"
description=""
handler="your.addonpackage.setuphandlers.setup_various"
/>

</configure>
```
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section is also confusing. First we say "do this", then later say "do this best practice." This section should be rewritten to provide a single example as the best way to do it first, then follow it with variations in subsequent examples.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Todo
Status: In Progress

Development

Successfully merging this pull request may close these issues.

Port GenericSetup docs

8 participants