zephyr provider

A zephyr stage manages a West workspace (Zephyr RTOS).

[my-zephyr-stage]
provider = "zephyr"

(provider:/description:/disabled:/ci:/depends-on:/scripts:/env:/env-prepend:/env-append: are generic keys every stage has — see “Generic stage keys” in Configuration. Everything below is specific to zephyr.)

Requires

west must be installed wherever this stage runs — denver never installs it. In practice an earlier uv stage provides it, by listing west in its requirements:.

Key reference

  • topdir — WEST_TOPDIR (a zephyr concept, not a denver built-in), set explicitly. Wins outright over everything else, and is exported as WEST_TOPDIR (overwriting an already-exported value) so every ${WEST_TOPDIR} substitution elsewhere in this env agrees with it. Without it, WEST_TOPDIR falls back to an already-exported WEST_TOPDIR env var (e.g. set by the user, or by an outer denver run before re-invoking inside docker), else discovery: walking up from the env dir for the nearest enclosing .west, or failing that, the outermost enclosing .git.

  • exe (default: "west") — the west executable, resolved on PATH.

  • west-yml (default: <WEST_TOPDIR>/west.yml) — the manifest.

  • base (default: ${WEST_TOPDIR}/zephyr-rtos) — ZEPHYR_BASE. The zephyr.base west config entry itself is set separately, after west update and project patches are applied, from west list zephyr -f {path} — i.e. wherever the manifest’s own zephyr project actually landed. Left untouched if the manifest has no project named zephyr.

  • west-config — extra/overriding west config key/value pairs, e.g. {zephyr.base-prefer: env}.

  • blobs-cache — a path to an auto-generated list of west blobs to pre-cache.

  • blobs-fetch-args (default ["--auto-accept"]) — extra west blobs fetch args.

  • skip-blobs-fetch (default false) — skip west blobs fetch entirely.

  • blobs-fetch-allow-failure (default false) — don’t fail the stage if west blobs fetch exits non-zero (e.g. a flaky blob host). Ignored if skip-blobs-fetch: is set.

  • patch-committer-name/patch-committer-email/patch-committer-date — the identity used when applying project patches via west patches (GIT_COMMITTER_NAME/_EMAIL/_DATE), defaulting to denver/denver@denver/2000-01-01T00:00:00 (a fixed value, so applying the same patches twice never produces a different commit).

  • update-args — extra west update args.

  • skip-update (default false) — skip west update entirely (patch application, zephyr.base, and blobs fetch/cache still run).

  • skip-patch-apply (default false) — skip applying projects’ patches.yml (see patches-yml-path:) via west patches.

  • patches-yml-path (default ["zephyr/patches.yml"]) — a list of paths, each relative to a west project’s own directory, checked for a patches.yml to apply via west patch --patch-yml. Every listed path that exists in a given project is applied (all of them, not just the first match), in the order listed.

WEST_CONFIG_SYSTEM (west’s own base-config env var, e.g. the remotes/defaults denver ships) is not a denver.yml key — set it directly via env:/hooks.env like any other real environment variable; west reads it itself, no provider-specific handling needed.

Design notes

  • west packages pip is a separate concern. Installing the Python packages a workspace’s own modules declare (west packages pip) isn’t this provider’s job — give a separate uv stage a requirements: [$(west packages pip)] entry instead (see uv.md), with its own overrides:/freeze-to: for pinning them. It has to run after this stage, since until the workspace is updated there’s no way to know what those packages even are.

  • Broken update.auto-cache entries are removed before west update. With west’s update.auto-cache set, an interrupted git clone --mirror (Ctrl+C, a cancelled CI job, a dropped network) leaves an empty bare repo with no refs in the cache. west never repairs an existing entry, so every later west update fails with upload-pack: not our ref. Right before each west update, this stage therefore removes every <auto-cache>/<project>/<hash>/ entry that has no refs at all (one info line each), and west recreates it. A mirror that is merely out of date is kept. Nothing happens if update.auto-cache is unset or the dir does not exist, or when west update itself doesn’t run (skip-update:, --fast, or nothing changed since the last run).

  • --ci always adds a fixed shallow-clone strategy (--narrow -o=--depth=1) to west update, on top of whatever update-args: already configures.

  • west update checks out with parallel workers. It runs with git’s checkout.workers=0 (git >= 2.32, one worker per CPU), passed via GIT_CONFIG_COUNT/GIT_CONFIG_KEY_<n>/GIT_CONFIG_VALUE_<n> (appended to any the environment already has), so no git config file is changed.

  • Patches of different modules are applied in parallel. west patch applies one patch after the other and has no jobs option of its own. So for a patches.yml whose patches name a module:, there is one west patch --dst-module <module> apply per destination module, run in parallel through xargs -P <CPUs>; the patches of one module stay in order. A patches.yml without any module: is applied in one go, as before.

  • --fast only checks the workspace is already configured — dies with a clear message if it isn’t, rather than running west update.

  • --force always reruns west update (even if this stage’s own drift check found nothing new) and recreates the workspace setup steps’ own on-disk state unconditionally.

  • --dry-run prints the west commands instead of running them, and writes neither .west/config, the drift fingerprint, nor blobs-cache:, nor removes any broken auto-cache entry (it is reported as rm -r instead). The read-only queries this stage branches on (west config -l, west config update.auto-cache, west manifest --resolve, west list, git rev-parse, git for-each-ref) do still run: they are what decide which west config keys differ, whether west update would be skipped as unchanged, which projects carry a patches.yml, and which auto-cache entries are broken.