Publish an app
SDK 1.xWritten for core 1.53+ and gui 1.23+.
Goal: Your app listed in the app catalog, so idf.py app-install --app my-app works for everyone, with a release for every version you push.
You need: Start here done, an app made with Start from the template or from an example (Make an example your own), and gh auth login run once (the steps below create and edit GitHub repos with gh).
Run every command from the project root (~/CrossPad, Windows C:\CrossPad\firmware) with ESP-IDF loaded as in 2. Build the firmware. Used the one-line installer? Use crosspad-idf in place of idf.py. In the commands, you stands for your GitHub user name and my-app for your app’s id.
The listing rule: repos in the CrossPad org are found by the topic crosspad-app; public repos anywhere else are added to external-apps.json in CrossPad/crosspad-apps. Either way the catalog reads crosspad-app.json from the repo’s default branch on GitHub (an entry in external-apps.json may name another branch with "branch") and skips a repo without a readable one. Step 4 has one tab per route: pick the one that fits your repo.
-
Create the app with its own repo
Made your app from an example? It has its public repo already (Make an example your own, step 6): go on to step 2.
The catalog lists GitHub repos, so your app needs a public one.
--publiccreates the repo, pushes the new app to it and adds it to the project as a submodule; add--owner CrossPadfor an org repo.Terminal window idf.py app-new --app my-app --name "My App" --publicYou should see:
… file(s) generatedgit repository initialisedpushed to you/crosspad-my-app (public)added as a submodule at components/crosspad-my-appmy-app ready at components/crosspad-my-appMade the app without
--public? Its folder is already a git repo of its own; commit your work there and push it to a new public repo (CrossPad/crosspad-my-appfor an org repo):Terminal window git -C components/crosspad-my-app add -Agit -C components/crosspad-my-app commit -m "My App"gh repo create you/crosspad-my-app --public --source components/crosspad-my-app --remote origin --pushghends with✓ Pushed commits to …. The project keeps the app as a plain folder, which is fine for publishing.If not: publishing says gh is not authenticated. For any other
Error:line fromapp-new: idf.py app-new stops with an error. -
Complete the manifest
crosspad-app.jsonat the repo root describes the app to the catalog and to every client that installs it. The catalog needs five fields:id,name, a version like1.2.3,descriptionandplatforms. Fill the rest too; each field is explained under Manifest fields.{"name": "My App","id": "my-app","version": "0.1.0","description": "What it does, in one line","category": "music","icon": "my-app.png","component_path": "components/crosspad-my-app","platforms": ["esp-idf", "pc"],"requires": {"crosspad-core": ">=1.53.0","crosspad-gui": ">=1.23.0"},"changelog": ["0.1.0: first version"]}Set
requiresto at least core>=1.53.0and gui>=1.23.0, the versions the template and the examples are written against; an app that usesCP_FONT_PIXEL(as the UI kit recipe does) needs gui>=1.23.1, which declares the pixel font with C linkage, as the simulator on Windows needs. (ui_modal_confirm_dismiss, which the UI kit also uses, is older: gui 1.8.0.) Checkrequiresin your manifest: templates before SDK 1.53 wrote>=0.3.0/>=0.2.0; raise them to these floors.The icon is a 48×48 px RGBA PNG in
assets/, and its file name must be unique among all apps: the firmware build copies every app’s icons into one folder, and a second file of the same name overwrites the first (the build printsAsset collision: 'my-app.png' …). Name it after your id, as the template does (assets/my-app.png), and use the same name iniconand in the third argument ofREGISTER_APP/REGISTER_APP_PL.The catalog reads the copy on GitHub, so commit and push every change:
Terminal window git -C components/crosspad-my-app commit -am "Describe My App"git -C components/crosspad-my-app pushYou should see:
git pushend with a line such as1a2b3c4..5d6e7f8 main -> main.If not: a push rejected for lack of access: publishing says gh is not authenticated. Manifest problems: CP Tools submit says Not yet lists each one and its fix.
-
Keep library.json in step
The template also writes
library.json, the package description the Arduino build of the firmware reads ("appLibrary": truethere keeps the app’s registration in the link, asWHOLE_ARCHIVE ONdoes inCMakeLists.txt). Itsversionmust equal the manifest’sversion, or the release workflow below refuses to tag. Bump both together. An app withoutlibrary.json(the examples have none) skips this check.Terminal window grep '"version"' components/crosspad-my-app/crosspad-app.json components/crosspad-my-app/library.jsonTerminal window Select-String '"version"' components\crosspad-my-app\crosspad-app.json, components\crosspad-my-app\library.jsonYou should see: the same version on both lines, such as
"version": "0.1.0",. -
Get listed
Add the topic
crosspad-app, on the repo’s GitHub page (the gear next to About) or with the GitHub CLI. The catalog build picks it up within six hours, or at once when the repo publishes a release through the release workflow.Terminal window gh repo edit CrossPad/crosspad-my-app --add-topic crosspad-appYou should see:
✓ Edited repository CrossPad/crosspad-my-app, andcrosspad-appunder About on the repo page.If not: publishing says gh is not authenticated. If
ghrefuses with a permission error, ask an org owner to add the topic.CP Tools checks the manifest and the repo, then forks
CrossPad/crosspad-apps, adds your repo toexternal-apps.jsonand opens the pull request. Open Developer tools (key 4) → Submit an app to the catalog, pick your app and confirm.Terminal window python3 tools/app_manager.pyTerminal window python tools\app_manager.pyOne-line installer:
cptools.You should see:
This opens a pull request on CrossPad/crosspad-apps that addsyou/crosspad-my-app to the catalog. Once it is merged, the app shows upunder [2] for everyone within a few hours.([2] is the app list in CP Tools.) After you confirm:
Pull request: https://github.com/CrossPad/crosspad-apps/pull/….If not: CP Tools submit says Not yet. If it says
Couldn't: not signed in to GitHub: publishing says gh is not authenticated.Fork
CrossPad/crosspad-apps, add your entry toreposinexternal-apps.json, and open a pull request. The merge rebuilds the catalog at once.{ "repo": "you/crosspad-my-app" }You should see: after the merge, a Build App Registry run in the Actions tab of
CrossPad/crosspad-apps, andmy-appin itsregistry.json.If not: my app is not in the catalog
Check it worked
Section titled “Check it worked”Clients cache the catalog in app-registry.json in the project root for an hour, so delete the cache first:
rm -f app-registry.jsonidf.py app-listRemove-Item app-registry.json -ErrorAction SilentlyContinueidf.py app-listYour app appears under Official: (org repo) or Community: (any other repo), as [✓] my-app with its description, because this project has it installed.
If not: my app is not in the catalog
Manifest fields
Section titled “Manifest fields”Every field of crosspad-app.json, as the template writes it. The catalog copies all of them except changelog into its entry for your app.
| Field | Needed by | What it holds |
|---|---|---|
id | catalog | The id people install by (idf.py app-install --app my-app), and the catalog’s key for the app. Lowercase letters, digits and dashes, starting with a letter; the app’s folder is crosspad-<id>. |
name | catalog | The name shown in the catalog and in CP Tools. On the device the app’s label is the first argument of REGISTER_APP / REGISTER_APP_PL (MyApp), not this. |
version | catalog | Three numbers, major.minor.patch. Each new version you push is a release. |
description | catalog | One line: what the app does. |
platforms | catalog | Where it builds: esp-idf (the firmware) and pc (the simulator). CP Tools warns before installing an app on a platform it does not list. |
requires | install | The lowest crosspad-core and crosspad-gui the app builds against, as >=x.y.z. CP Tools warns, and asks, before installing an app into a project whose core or gui is older. |
icon | firmware | The icon’s file name in assets/: 48×48 px RGBA PNG, unique among all apps. |
component_path | install | Where the app lands in the firmware project: components/crosspad-<id>. |
category | CP Tools | music, audio, tools or other. |
changelog | releases | One line per version, each starting with its version ("0.2.0: …"). The release workflow uses the lines for the version it releases as the release notes. |
screenshot | CP Tools (optional) | A picture of the app, a path in the repo or a URL. CP Tools opens it from the app’s page. |
Release a new version
Section titled “Release a new version”Clients that follow Latest release (the default in CP Tools) install and update to the newest tag vX.Y.Z in your repo; an app without such tags is followed on its default branch instead. The reusable workflow in CrossPad/.github makes those tags for you: add this file to your repo as .github/workflows/app-release.yml.
name: App release
on: push: branches: [main] paths: [crosspad-app.json] workflow_dispatch:
permissions: contents: write
concurrency: group: app-release cancel-in-progress: false
jobs: release: uses: CrossPad/.github/.github/workflows/app-release.yml@v1From then on, a push to the default branch that changes version in crosspad-app.json tags the commit that introduced the version as vX.Y.Z and publishes it as a GitHub prerelease named after the app, with the matching changelog lines as its notes. It refuses, and the run fails, when:
versionis not three plain numbers, or not above your newestvX.Y.Ztag;library.jsonexists and carries another version;idhas characters other than letters, digits,.,_and-;changelogis not a list of strings.
The workflow runs only on the repo’s default branch: if yours is not main, change branches: [main] to its name.
Nothing is tagged backwards: in a repo with no vX.Y.Z tag yet, only a push that itself changes the version makes the first release. For a CrossPad org repo the release also rebuilds the catalog at once; for any other repo the catalog’s six-hourly build picks the new version up.
You should see: after the push, an App release run in your repo’s Actions tab, and the new version under Releases.
Profiles
Section titled “Profiles”A profile is a named set of apps (plus build options) that a firmware project can apply in one go: the firmware project ships full (every official app) and lite (only the Sampler) in config/profiles/. A profile names its apps by catalog id, and applying it installs each one from the catalog, so an app can be part of a profile someone else applies only once it is published.
To make a profile with your app in it, install the apps you want and save the set; then copy config/profiles/my-set.json into the other checkout (or commit it to a repo both use) and apply it there:
# in the checkout that has the appsidf.py app-install --app my-appidf.py app-profile --profile-action save --name my-set --description "Sampler and My App"
# in the other checkout, after copying config/profiles/my-set.json into itidf.py app-profile --profile-action apply --name my-setYou should see: Saved …/config/profiles/my-set.json after the save; the apply prints Profile 'my-set': … with one install line per app the checkout is missing, then installs them. --remove-extra also removes installed apps the profile leaves out. After an apply, rebuild from a clean build folder as in Start from the template, step 3.