Publishing to the official store
The official add-on store lives at github.com/apexinfosysindia/addons.
The Supervisor ships with this repository built in (repository slug core);
every device syncs it, so a merged add-on is visible to the whole fleet on the
next store refresh.
Repository layout
Each add-on is a top-level directory named after its slug, containing the full add-on anatomy described in getting started:
addons/
├── .github/workflows/publish-addons.yml
├── samba/
│ ├── config.yaml
│ ├── build.yaml
│ ├── Dockerfile
│ ├── rootfs/
│ ├── DOCS.md
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── icon.png
│ ├── logo.png
│ └── translations/en.yaml
├── ssh/
├── zigbee2mqtt/
└── ... (18 add-ons)
PR flow
- Fork/branch, add or modify your add-on directory.
- Bump
version:inconfig.yaml— the version is the image tag, so a content change without a version bump will not reach devices. - Update
CHANGELOG.mdfor the new version. - Open a pull request against
main. Thepublish-addonsworkflow runs on every PR and builds every add-on for every supported architecture — a PR must prove the builds, but nothing is pushed from a PR. - After merge to
main, the same workflow rebuilds and pushes the prebuilt images to the registry.
Two writing rules for PRs in this repository:
- Wording in configs, docs, commits, and code must be ApexOS-native — no upstream project names (the platform validates only native keys anyway; see the legacy-alias note).
- User-facing text follows the store conventions in presentation.
The publish workflow
.github/workflows/publish-addons.yml builds prebuilt images for all
locally-buildable add-ons and pushes them to the ApexOS registry as:
registry.apexinfosys.in/apexos/{arch}-addon-<slug>:<version>
Triggers and gating
- Runs on
pushtomain, onpull_requesttargetingmain, and manually (workflow_dispatch). - Concurrency is grouped per ref with cancel-in-progress, so a superseded run is stopped.
- All jobs skip for
dependabot[bot]. - The push step runs only on
main(github.event_name != 'pull_request') and fails hard if theREGISTRY_PASSWORDsecret is missing.
Job 1: mirror-bases
Third-party base images that add-ons build FROM are mirrored into the org
registry first, so CI and devices never depend on external registries at build
or ship time. Currently that is the matter_server base:
ghcr.io/matter-js/matterjs-server:1.2.6 is copied (with skopeo copy --all,
preserving the multi-arch manifest) to
registry.apexinfosys.in/apexos/matterjs-server:1.2.6. The copy is idempotent.
Job 2: build (the matrix)
A full cross-product of:
- Architectures:
amd64onubuntu-latest,aarch64onubuntu-24.04-arm— native-arch runners, no emulation. - Add-ons: all 18 store add-ons (aircast, apex-cloud-link, autossh, cec_scan, configurator, matter_server, mosquitto, openthread_border_router, openwakeword, piper, plex, samba, speech_to_phrase, spotify, ssh, vscode, whisper, zigbee2mqtt).
Each leg:
-
Reads metadata from the add-on's
config.yaml(orconfig.json) and itsbuild.yaml— falling back to a repo-rootbuild.yamlif the add-on has none. It extractsslug,version, the per-archbuild_from, andargs. -
Auto-skips when the add-on does not list the architecture in
arch:or has nobuild_fromfor it — an unsupported leg is a clean skip, not a failure. -
Builds with the same argument contract the Supervisor uses for local builds:
docker buildx build --load "$ADDON" \--build-arg "BUILD_FROM=$BUILD_FROM" \--build-arg "BUILD_ARCH=$ARCH" \--build-arg "BUILD_VERSION=$VERSION" \${extra_args_from_build_yaml} \--label "io.apexos.arch=$ARCH" \-t "registry.apexinfosys.in/apexos/$ARCH-addon-$SLUG:$VERSION"Because CI and the on-device builder pass identical
BUILD_FROM/BUILD_ARCH/BUILD_VERSIONargs, a Dockerfile that builds locally also builds in the pipeline. -
Pushes the tag on
mainafterdocker loginwith theREGISTRY_USER/REGISTRY_PASSWORDorg secrets (a robot credential).
A final addons-published job asserts the whole matrix succeeded, giving one
required status to gate on.
One deliberate difference from the product pipelines
The add-on pipeline runs no denylist image-gate: add-on payloads are third-party upstream software (mosquitto, media servers, ...), not pipeline output, so the gate's zero-violation contract applies only to ApexOS base and product images.
The image: key convention
Prebuilt add-ons point the Supervisor at the published image via config.yaml:
image: registry.apexinfosys.in/apexos/{arch}-addon-samba
{arch}is substituted with the device architecture at install time.- The pulled tag is always the
versionfromconfig.yaml— which is why the workflow tags images with the config version, and why every content change needs a version bump. - Presence of
image:means "pull"; absence means "build on device". Devices then pull prebuilds from the ApexOS registry instead of building locally. Keep theimage:slug identical to the add-onslugso it matches what the workflow pushes:{arch}-addon-<slug>.
Store metadata checklist
Before opening the PR, make sure the directory carries the presentation assets the store renders (details in presentation):
icon.png— square icon, fixed filename.logo.png— wide logo, fixed filename.README.md— becomes the add-on's long description in the store.DOCS.md— rendered as the Documentation tab; written for end users (install steps, options, examples).CHANGELOG.md— rendered when an update is offered; add a## <version>section per release.translations/en.yaml— names and descriptions for every option inschema:, so the configuration UI shows labels instead of bare keys.