Appearance
Publish and upgrade a plugin
What you're doing
Getting the .spk onto your ERP and, later, shipping a new version.
The complete sequence
bash
# 1. build the .spk (a zip of spk-assembly/)
erp plugin build spk-assembly -o office-equipment-1.0.0.spk
# 2. publish it to the current env, for a tenant
erp plugin publish office-equipment-1.0.0.spk --tenant 2Output on success:
packaged 33 files -> office-equipment-1.0.0.spk (231986 bytes)
sha256: cef54cdf...
erp plugin publish: office-equipment-1.0.0.spk -> https://erp.example.com (env "dev", tenant 2)
POST .../api/v1/authoring/plugins/upload ...
installed:
{"pluginId":"office-equipment","version":"1.0.0","state":"installed","pf4jState":"STARTED", ...}"state":"installed" and "pf4jState":"STARTED" mean it's live. The server recomputes the checksum itself — it never trusts the client digest.
What happens on install
- checksum verified server-side;
- the manifest is read (
idandversionare required;mainClassis optional — a pure-JSON plugin leaves itnull); - PF4J loads the plugin —
lib/*.jarif present, otherwise a built-in generic no-op entry point for a pure-JSON plugin; - entities are created / altered (idempotent-additive);
- providers, data views, data services, pages, menus, rules, workflows, i18n and roles are installed;
- seed data is upserted;
AFTER_CREATEregister rules fire → scheduled jobs register.
If any step fails the whole install rolls back, including the PF4J load.
Version discipline — the rules that will bite you
1. Bump the version before every publish
plugin.json version must increase. A re-publish of a version that's already installed is a silent no-op — your fix does not ship. Bump it even if you're certain the last publish already contained the change.
2. A version is immutable once stored
Once the server has stored office-equipment/1.0.0/office-equipment-1.0.0.spk, re-uploading different bytes under 1.0.0 is rejected:
Refusing to overwrite immutable artifact at "system/plugins/.../1.0.0/..." with different contentA version that genuinely shipped (reached pf4jState: STARTED on some tenant) stays immutable forever — re-publishing different bytes under it is rejected, go to the next version.
But a failed or partial install is not a real release. As of the SDK publish-hardening pass, if a 1.0.0 publish never reached STARTED (it failed later in the pipeline, or it's a metadata-only plugin that isn't PF4J-loaded), re-publishing 1.0.0 replaces the stored .spk and retries — no version bump needed just to fix a broken publish. erp plugin publish of an already-installed id automatically takes the upgrade path, so this "edit a page, re-publish the same version" loop works directly for JSON-only plugins.
3. The platform cannot load two versions of the same plugin id at once
There is an already loaded plugin ... with the same id (office-equipment) ...
Simultaneous loading of plugins with the same PluginId is not currently supported.The publish/upgrade path now reliably stops and unloads the currently-loaded same-id plugin before loading the new bundle (stop → unload → GC hint → load → start), so a normal in-place re-publish never hits this. If an earlier crash or a transient DB error during uninstall left the old code wedged — state: uninstalled in the install row but pf4jState: STARTED in the process — recover without a backend restart:
bash
erp plugin force-unload office-equipment # clears the stuck PF4J load
erp plugin publish office-equipment-1.0.1.spk --tenant 2erp plugin publish --force does the force-unload automatically before retrying, and spark publish / erp plugin publish also auto-retry once with a force-unload if they see the "already loaded" error.
These behaviours were all hit while building the tutorial module. Treat version numbers as cheap and monotonic; use
force-unloadrather than a restart when a load gets stuck.
Roles and names must be unique across installed plugins
A plugin.json role whose name or code collides with a role from another installed plugin fails the install:
A role named "Equipment Manager" already exists
Role "..." (code "...") already exists owned by "..."Prefix your role names and codes with something plugin-specific.
Upgrading
bash
# edit files, bump plugin.json version to 1.0.1, rebuild the lib jar if Java changed
erp plugin build spk-assembly -o office-equipment-1.0.1.spk
erp plugin publish office-equipment-1.0.1.spk --tenant 2Entities gain new columns; pages, rules, seed data are re-installed (upserted). Nothing is dropped — a removed field's column stays, a removed seed row stays.
How to verify it worked
bash
erp plugin listFind your plugin: "version": "1.0.1", "state": "installed", "pf4jState": "STARTED". Then hit a page or an entity endpoint to confirm the new content is live.
Common mistakes
| Symptom | Cause | Fix |
|---|---|---|
| "fix didn't ship" | didn't bump version on a shipped version | bump; re-publish. (A failed same-version publish now retries in place.) |
Refusing to overwrite immutable artifact | retrying a version that already reached STARTED on some tenant | go to the next version |
already loaded plugin ... same id | a load wedged after a crash / DB blip | erp plugin force-unload <id> then re-publish (or publish --force) |
| role name/code clash | another plugin owns that role | prefix yours |
What to read next
- Validate and test a plugin
- Troubleshooting: publishing and install errors