[{"data":1,"prerenderedAt":30},["ShallowReactive",2],{"doc:en:plugins\u002Fpublishing":3},{"title":4,"lede":5,"html":6,"headings":7},"Publishing on the store","A plugin is published on store.escouade.dev from its public GitHub repository and a git tag. This page says how to submit a version, what the store checks, and how to fix a rejection.","\u003Ch2 id=\"submitting-a-version\">Submitting a version\u003C\u002Fh2>\n\u003Col>\n\u003Cli>Sign in on \u003Ca href=\"https:\u002F\u002Fstore.escouade.dev\u002Fen\u002Fsubmit\" rel=\"noopener\">store.escouade.dev\u003C\u002Fa> with GitHub, and install the “Escouade Store” App on the plugin’s repository. It reads the code and the tags, never writing. A private repository is refused: the code of a plugin of the store is read by all.\u003C\u002Fli>\n\u003Cli>Choose the repository, then the tag of the version (\u003Ccode class=\"key\">v1.2.0\u003C\u002Fcode> or \u003Ccode class=\"key\">1.2.0\u003C\u002Fcode>). The store resolves it into its commit once, at submission, and never reads it again for this version.\u003C\u002Fli>\n\u003Cli>The ten checks run on the page, one line each. The store downloads the commit’s archive once, keeps it as it came and computes its SHA-256 fingerprint; nothing of the plugin runs on the server.\u003C\u002Fli>\n\u003Cli>If everything passes, publish: a first version asks for one to three categories and keywords. The version appears \u003Cstrong>unverified\u003C\u002Fstrong>; the “Ask for the review” box (checked) puts it in the queue of the “Verified by Escouade” review, which holds for this version only.\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>Each published version has its \u003Ccode class=\"key\">version.json\u003C\u002Fcode> file: the id, the number, the repository, the commit, the fingerprint of the archive, of the manifest and of each binary. Its fingerprint is the version’s, which the app checks and the review signs. A published version never changes, and its number is not used again.\u003C\u002Fp>\n\u003Ch2 id=\"the-ten-checks\">The ten checks\u003C\u002Fh2>\n\u003Cdiv class=\"table\">\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Check\u003C\u002Fth>\n\u003Cth>What it asks\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\u003Ctr>\n\u003Ctd>Manifest\u003C\u002Ftd>\n\u003Ctd>\u003Ccode class=\"key\">escouade-plugin.json\u003C\u002Fcode> at the root, valid against the schema: the same rules as in the app (\u003Ca href=\"\u002Fen\u002Fdocs\u002Fplugins\u002Fmanifest\u002F\">the manifest\u003C\u002Fa>).\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Version\u003C\u002Ftd>\n\u003Ctd>\u003Ccode class=\"key\">version\u003C\u002Fcode> equal to the tag (the \u003Ccode class=\"key\">v\u003C\u002Fcode> is optional), greater than the last one published.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Identifier\u003C\u002Ftd>\n\u003Ctd>Free or already yours, published from this repository; \u003Ccode class=\"key\">dev.escouade.*\u003C\u002Fcode> is reserved. A name too close to another plugin’s goes through a manual review first.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Repository\u003C\u002Ftd>\n\u003Ctd>\u003Ccode class=\"key\">repository\u003C\u002Fcode> equal to the repository submitted, \u003Ccode>https:\u002F\u002Fgithub.com\u002F&lt;owner&gt;\u002F&lt;repository&gt;\u003C\u002Fcode>.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>License\u003C\u002Ftd>\n\u003Ctd>A free license (approved by the OSI or the FSF) as an SPDX id, the same one GitHub recognizes in the \u003Ccode class=\"key\">LICENSE\u003C\u002Fcode> or \u003Ccode class=\"key\">COPYING\u003C\u002Fcode> file.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>README\u003C\u002Ftd>\n\u003Ctd>\u003Ccode class=\"key\">README.md\u003C\u002Fcode> (or \u003Ccode class=\"key\">README.fr.md\u003C\u002Fcode> and \u003Ccode class=\"key\">README.en.md\u003C\u002Fcode>) at the root, not empty: it is the plugin’s page.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Screenshots\u003C\u002Ftd>\n\u003Ctd>Those of \u003Ccode class=\"key\">store.screenshots\u003C\u002Fcode>: 6 at most, in PNG, JPEG or WebP, 1600 px wide and 1 MB each at most. The store re-encodes them, without their metadata.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Archive\u003C\u002Ftd>\n\u003Ctd>50 MB at most, no symbolic link, no absolute path nor \u003Ccode class=\"key\">..\u003C\u002Fcode>, no submodule nor unresolved Git LFS file.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Permissions\u003C\u002Ftd>\n\u003Ctd>Consistent with the plugin: with no program and no webhook, it asks for no network, no events, no secrets; a secret it asks for is declared in \u003Ccode class=\"key\">contributes.secrets\u003C\u002Fcode>; a host is written alone (\u003Ccode class=\"key\">hooks.slack.com\u003C\u002Fcode>).\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Binaries\u003C\u002Ftd>\n\u003Ctd>The program of \u003Ccode class=\"key\">run\u003C\u002Fcode> is in the repository, or in the tag’s release for each system (see below).\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\u003C\u002Ftable>\u003C\u002Fdiv>\n\u003Ch2 id=\"the-plugin-s-page\">The plugin’s page\u003C\u002Fh2>\n\u003Cp>Once published, the version has its page on the store: its name, its author, “Not verified” or “In review” until it is signed, what it will be able to do in words (the strongest permission first), its repository, commit and fingerprint, its versions, and “Open in Escouade”, which opens its page in the app.\u003C\u002Fp>\n\u003Cp>The README is rendered there once, at submission, in the visitor’s language (\u003Ccode class=\"key\">README.en.md\u003C\u002Fcode> or \u003Ccode class=\"key\">README.fr.md\u003C\u002Fcode>, else \u003Ccode class=\"key\">README.md\u003C\u002Fcode>); the markdown pages of the \u003Ccode class=\"key\">store.docs\u003C\u002Fcode> folder make the “Documentation” tab. The store keeps the markdown and a short list of HTML tags (headings, lists, links, code, tables, \u003Ccode class=\"key\">details\u003C\u002Fcode>); it removes scripts, styles, \u003Ccode class=\"key\">style\u003C\u002Fcode> and \u003Ccode class=\"key\">class\u003C\u002Fcode> attributes, frames and forms. The repository’s images (PNG, JPEG or WebP, 5 MB at most, by a relative path or by their address on GitHub) are copied to the store and re-encoded; images from elsewhere, such as badges, are removed: a visitor loads nothing from a third party. A link to a file of the repository leads to GitHub, at the published commit.\u003C\u002Fp>\n\u003Cp>Anyone can report a plugin from its page, without an account, with an e-mail address: a person reads every report. You see that a report is open, never who sent it.\u003C\u002Fp>\n\u003Ch2 id=\"the-binaries\">The binaries\u003C\u002Fh2>\n\u003Cp>A plugin that runs a compiled program attaches to the tag’s release one archive per system, whose name says the system and the architecture: \u003Ccode class=\"key\">…-windows-x64.zip\u003C\u002Fcode>, \u003Ccode class=\"key\">…-macos-arm64.tar.gz\u003C\u002Fcode>, \u003Ccode class=\"key\">…-linux-x64.tar.gz\u003C\u002Fcode> (50 MB at most each). Each system that runs a program missing from the repository must have its own. The store downloads them once, keeps them and computes their fingerprint.\u003C\u002Fp>\n\u003Cp>Build them in a public GitHub Actions workflow with \u003Ccode>actions\u002Fattest-build-provenance\u003C\u002Fcode>: the provenance attestation ties each file to the tag’s commit. A binary without an attestation is published unverified and cannot be verified; a binary attested from another commit is refused.\u003C\u002Fp>\n\u003Ch2 id=\"fixing-a-rejection\">Fixing a rejection\u003C\u002Fh2>\n\u003Cp>Each check that fails says what is wrong, where (the field or the file, such as \u003Ccode>permissions.network[0]\u003C\u002Fcode>), and how to fix it. If you gave an e-mail address in your account, the same text goes there.\u003C\u002Fp>\n\u003Cp>To fix it: commit, then push a \u003Cstrong>new tag\u003C\u002Fstrong>. The store sees it through the App and runs the checks again by itself. If the failure came from the store or from GitHub, the page offers “Run the checks again”.\u003C\u002Fp>\n\u003Ch2 id=\"new-tags\">New tags\u003C\u002Fh2>\n\u003Cp>Once the plugin is published, each new tag of the repository becomes a version checked by itself, which waits for your click to appear. The “Publish new tags by themselves” box publishes it without waiting, if every check passes.\u003C\u002Fp>\n\u003Cp>A tag moved after publication changes nothing to the published version: the store keeps the commit and the archive it froze, marks it “tag moved on the repository” and tells you. To publish this new code, push a new tag.\u003C\u002Fp>\n\u003Ch2 id=\"managing-your-plugins\">Managing your plugins\u003C\u002Fh2>\n\u003Cp>Your account, at \u003Ca href=\"https:\u002F\u002Fstore.escouade.dev\u002Fen\u002Faccount\" rel=\"noopener\">store.escouade.dev\u002Fen\u002Faccount\u003C\u002Fa>, shows your plugins, their state, their latest version and their installs (those of the users who accepted Escouade’s statistics only). A version found on a new tag waits at the top, with the permissions it asks more: “Publish” or “Dismiss”. A plugin’s “…” menu opens its versions, its installs week by week and what you can do with it:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>Take a version back\u003C\u002Fstrong>: it is no longer offered to install nor as an update; those who have it keep it, marked “taken back by its author”. Its number does not come back.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Take off the list\u003C\u002Fstrong>: the plugin leaves the search and the app’s catalog; its page stays readable, installations keep their versions, without updates. One click puts it back.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Transfer\u003C\u002Fstrong>: to another account of the store, which accepts it from its own. If the repository changes owners on GitHub, the new owner, once the App is installed on their side, can take the plugin over. Either way, its publications freeze for 7 days, the time for a report, and its page tells the transfer.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Delete\u003C\u002Fstrong>, by typing its id: it leaves the catalog at once; its page and archives go after 30 days, during which you can undo it. Its id stays your account’s, and its version numbers are not used again.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Each action goes to the journal and, if you gave an address, out by e-mail: an action you did not do shows. An account that still has plugins cannot be deleted: transfer or delete them first.\u003C\u002Fp>\n",[8,12,15,18,21,24,27],{"id":9,"text":10,"depth":11},"submitting-a-version","Submitting a version",2,{"id":13,"text":14,"depth":11},"the-ten-checks","The ten checks",{"id":16,"text":17,"depth":11},"the-plugin-s-page","The plugin’s page",{"id":19,"text":20,"depth":11},"the-binaries","The binaries",{"id":22,"text":23,"depth":11},"fixing-a-rejection","Fixing a rejection",{"id":25,"text":26,"depth":11},"new-tags","New tags",{"id":28,"text":29,"depth":11},"managing-your-plugins","Managing your plugins",1793278987152]