Publishing to Packagist
Make your plugin installable via Composer, discoverable on Packagist, and releasable on GitHub.
Publishing a plugin is a one-time setup per package, then every release is an automatic GitHub → Packagist sync.
Naming convention
Contensio recommends prefixing all plugin package names with plugin-:
contensio/plugin-social-connect(official)acme/plugin-awesome(community)
Themes use theme-:
contensio/theme-blogger
Why: Packagist search works better, ecosystem scanners can filter by prefix, and the contensio/* namespace stays clean.
Prerequisites
- A GitHub repo for your plugin (public).
- A Packagist account at packagist.org.
- Your plugin's
composer.jsonwith:- A unique
name({vendor}/plugin-{slug}) - Correct
autoloadPSR-4 mapping extra.cms.type=plugin
- A unique
One-time setup
1. Push your plugin to GitHub
Your repo root should be the plugin root (not a subdirectory). Composer clones from the repo root.
2. Submit to Packagist
- Go to packagist.org/packages/submit
- Paste your GitHub URL
- Confirm the package name
Packagist scans your composer.json and creates a package page.
3. Set up the GitHub webhook (auto-sync)
On your GitHub repo → Settings → Webhooks → Add webhook:
- Payload URL:
https://packagist.org/api/github?username=YOUR_PACKAGIST_USERNAME - Secret: your Packagist API token (find it at packagist.org/profile/)
- Content type:
application/json - Events: Just push and create events
From now on, every push and every tag triggers Packagist to re-scan your repo.
Releasing a version
Every release is a Git tag + a GitHub release:
git tag 1.0.0
git push origin 1.0.0
Packagist picks it up within a minute. composer require acme/plugin-awesome:^1.0 now works.
GitHub releases (recommended)
For non-technical users who install via ZIP, create a GitHub release so they have a nice download:
- Go to your repo → Releases → Draft a new release
- Pick the tag you just pushed
- Fill in the release notes
- Publish
GitHub auto-generates a source ZIP. Users download it and upload in the admin's Plugins → Install Plugin.
Composer.json requirements
{
"name": "acme/plugin-awesome",
"description": "Makes things awesome.",
"type": "library",
"license": "MIT",
"require": {
"php": "^8.3",
"contensio/contensio": "^1.0"
},
"autoload": {
"psr-4": {
"Acme\\Awesome\\": "src/"
}
},
"extra": {
"cms": {
"type": "plugin",
"provider": "Acme\\Awesome\\AwesomeServiceProvider"
}
},
"minimum-stability": "stable",
"prefer-stable": true
}
The extra.cms.type is the signal: Contensio's PluginRegistry scans vendor/ for packages with this marker.
Semantic versioning
Follow semver.org:
1.0.0— first stable1.0.1— bug fix1.1.0— new features, no breaking changes2.0.0— breaking changes
Contensio users pin with ^1.0 in their own composer.json. Respecting semver keeps upgrades safe.
Pre-release tags
For RCs, betas, alphas:
1.0.0-rc.11.0.0-beta.21.0.0-alpha.1
Mark the GitHub release as a "pre-release" so casual installers see the last stable by default.
License
If you license your plugin, keep it compatible with AGPL-3.0-or-later (Contensio's license). Common choices:
- AGPL-3.0-or-later — same as Contensio
- MIT — permissive, compatible
- GPL-3.0-or-later — compatible
Questions
Can I charge for a plugin?
The plugin's license determines this, not Contensio. AGPL-compatible licenses allow commercial distribution but require source availability. If you want a closed-source paid plugin, you'd need a different arrangement with the Contensio project — open an issue to discuss.
Can I host a private plugin (not public)?
Yes. Use a private Composer repo (Satis, Private Packagist) or a GitHub repo behind auth. Contensio doesn't care where the package comes from.
How do I update my plugin's description on Packagist?
Edit your composer.json and push — Packagist resyncs automatically.