Publish a plugin
A plugin is a Python package with a manifest, built and released by your own repository. This registry never holds your code: it holds a plugin's id, who owns it, and a pointer to the release artifact you published, together with a sha256 this server computed itself from the bytes it downloaded.
Publishing is a step in your release pipeline, not a pull request against anybody's repository. The whole path is below, in order.
Start from the template
The plugin template
(prokopto-dev/nparse-plugin-template) is a working plugin with the manifest, the
layout and a release workflow already in it. Use it as a GitHub template repository and you
start from something that builds.
Writing one from scratch is fine too. What this registry needs from a build is one artifact at
a public https URL and a version string your plugin declares.
The path, in order
- Start from the template. Build your plugin, and make your release produce one artifact — normally a zip attached to a GitHub Release.
-
Claim your plugin id.
An id is first come, permanent, and never recycled: it names your plugin in every
installed copy on every user's machine, so it is never released or reassigned, not even after a
delisting. Claiming it registers the id to your account and makes you its owner. It does not
list anything.
Sign in with GitHub, then claim the id with the Claim a plugin id form on your account page. GitHub is the only identity this registry accepts, because the artifacts live on GitHub Releases and the ownership records it was migrated from are GitHub handles.
A token cannot do this step, however scoped, and that is deliberate. Claiming is session-only —
POST /api/v1/pluginsfor a machine, the account page's form for you — because a deployment credential that could register new ids would grow its own reach every time it was used. Claim the id before you mint the token: a token is pinned to an id, and publishing to an id nobody has claimed is answered404, whatever the token carries.Do not ask somebody else to claim it for you. The account whose session sends the request becomes the owner — the claim takes the caller's own id and there is no on-behalf-of field — and an id is permanent, so that is not something anybody can correct afterwards. It has to be handed over instead: they add you as an owner, you mint your own token, and only then do they remove themselves. That needs you to have signed in here at least once anyway, so signing in and claiming it yourself is strictly less work.
-
Mint a scoped token.
On your account, mint a personal access token with
plugin:publishand nothing else, pinned to that one plugin id. The pin is the half that contains a leak: a token stolen from one repository's CI can publish to one plugin.The secret is shown exactly once and is never recoverable — what is stored is a keyed hash of it. If you lose it, revoke it and mint another.
-
Store it as a repository secret.
In your plugin repository, Settings → Secrets and variables → Actions, as
REGSERVE_TOKEN. It goes to the reusable workflow throughsecrets:, never through an input and never into arun:block. - Tag a release. Your workflow builds the artifact, uploads it to the GitHub Release, and then calls the registry's reusable workflow with the plugin id, the version your plugin declares, the artifact URL and its sha256.
-
The reusable workflow publishes.
One job in your pipeline. It sends an idempotency key derived from your repository, id and
version, so a re-run replays the original answer instead of burning a version that can only be
used once.
publish: needs: build uses: prokopto-dev/nparse-plugin-regserve/.github/workflows/publish-plugin.yml@5b0d87f7666f20c0c188b7208ad2738bd55c10d7 # v0.3.0 with: plugin-id: your-plugin-id version: ${{ needs.build.outputs.version }} artifact-url: https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/your_plugin.zip artifact-sha256: ${{ needs.build.outputs.sha256 }} secrets: registry-token: ${{ secrets.REGSERVE_TOKEN }}
Your first release waits for a human, and that is not a failure
A new plugin id always goes to human review, whatever your trust level. The first appearance of an id is where impersonation is caught, and nothing bypasses it — not a trusted account, not a second attempt.
So the first run of your publish job normally reports state: pending and
succeeds with a warning. The release is durably recorded and its version is
claimed; it is simply not in the index yet. An author who expects a green tick to mean "live"
reads that as a broken publish, and it is not one. Later version bumps of an
already-approved plugin can publish without a human, which is what trust levels govern.
There is a third outcome worth knowing before you meet it: verified: false, meaning
the registry could not download your artifact at all. It waits for review and is honest about
having checked nothing. The usual cause is ordering — a GitHub Release asset does not exist for an
anonymous caller until the release is published, so a draft release gives the registry a 404.
Pin the workflow to a commit, not to a tag and not to a branch
The uses: line above ends in a 40-character commit SHA, with the release it belongs
to written beside it. That is not fussiness: a reusable workflow runs inside your job, with
your publish token, so the ref you copy from us decides what gets to run next to that secret.
@main would mean your pipeline changes whenever this repository does.
And @v0.3.0 is not a pin either — a tag is a movable label, so one
git tag -f and a force-push here would change what every pipeline that copied it runs
on its next release, with no diff to review and nothing to notice. A commit SHA cannot be moved,
and that is the whole property worth buying.
The comment is what makes it readable: # v0.3.0 says which release you are on when
forty hex characters do not, and a pin you cannot place is a pin you will never update. This
repository pins every action it uses the same way, and it is a fair rule to hold us to.
The rest of it
- Publishing from CI — every input, what happens to the hash you send, how re-runs behave, and how to make a release that lands in review fail your pipeline if that is what you want.
- The plugin template — a plugin that builds, to start from.
- The index — the public JSON document every nParse+ install reads. Your plugin appears in it once a release of it has been approved.