bsp-registry-tools can upload Yocto/Isar build artifacts produced by
bsp build to Azure Blob Storage (default) or AWS S3 after the build
completes. It also supports uploading and restoring Yocto build caches
(DL_DIR and SSTATE_DIR) so team members and CI agents can seed their local
caches without running a full build.
- Overview
- Quick Start
- Installation
- Registry Configuration
- Public access without anonymous blob access
- Authentication
- CLI Reference
- Dry-run mode
- Artifact manifest
- Partial failures
- Python API
- CI/CD integration
After a Yocto build, images and SDKs land under:
<build_path>/tmp/deploy/images/
<build_path>/tmp/deploy/sdk/
bsp deploy finds all files that match the configured glob patterns in those
directories and uploads them to your cloud storage provider. An optional JSON
manifest (with artifact names, sizes, and SHA-256 checksums) is uploaded
alongside the artifacts.
When yocto_cache is enabled, the tool also packs the Yocto download cache
(DL_DIR) and/or the shared-state cache (SSTATE_DIR) as tar.gz archives
and uploads them under a cache/ sub-directory of the same prefix.
bsp gather downloads previously deployed artifacts. With --gather-cache it
also downloads and extracts the cache archives back to the configured local
directories — no full build needed to warm up the cache on a fresh machine.
Config can live either in the registry YAML (checked in, shared by the team) or be overridden entirely from the command line.
# 1. Install cloud SDK extras (one-time)
pip install "bsp-registry-tools[azure]" # Azure
pip install "bsp-registry-tools[aws]" # AWS
pip install "bsp-registry-tools[deploy]" # both
# 2. Authenticate (one-time)
az login # Azure (interactive)
aws configure # AWS (interactive)
# 3. Build and deploy in one step
bsp build poky-qemuarm64-scarthgap --deploy --deploy-container bsp-artifacts
# — or — deploy separately after a successful build
bsp deploy poky-qemuarm64-scarthgap --container bsp-artifacts
# Also upload Yocto caches (DL_DIR / SSTATE_DIR) after a build
bsp deploy poky-qemuarm64-scarthgap --container bsp-artifacts --deploy-cache
# Download artifacts from cloud storage
bsp gather poky-qemuarm64-scarthgap --dest-dir ./downloads
# Download artifacts AND restore Yocto caches
bsp gather poky-qemuarm64-scarthgap \
--dest-dir ./downloads \
--gather-cache \
--cache-downloads-dir /mnt/yocto/downloads \
--cache-sstate-dir /mnt/yocto/sstate
# Preview what would be uploaded (no credentials required)
bsp deploy poky-qemuarm64-scarthgap --dry-runCloud SDK dependencies are optional to avoid forcing them on users who do not need deployment.
# Azure Blob Storage support
pip install "bsp-registry-tools[azure]"
# installs: azure-storage-blob>=12.0, azure-identity>=1.0
# AWS S3 support
pip install "bsp-registry-tools[aws]"
# installs: boto3>=1.20
# Both providers
pip install "bsp-registry-tools[deploy]"--dry-run mode works without any cloud SDK installed.
Add a top-level deploy: block to your registry YAML. It applies to every
build by default.
specification:
version: "2.0"
deploy:
provider: azure
account_url: $ENV{AZURE_STORAGE_ACCOUNT_URL} # supports $ENV{} expansion
container: bsp-artifacts
prefix: "{vendor}/{device}/{release}/{date}"
patterns:
- "**/*.wic.gz"
- "**/*.wic.bz2"
- "**/*.tar.bz2"
- "**/*.ext4"
- "**/*.sdimg"
artifact_dirs:
- tmp/deploy/images
- tmp/deploy/sdk
include_manifest: true
include_build_manifest: true
# Optional: bundle all artifacts into a single archive before uploading
archive:
name: "firmware-{device}-{release}-{date}"
format: tar.gz
registry:
# ...AWS variant:
deploy:
provider: aws
bucket: my-s3-bucket
region: eu-west-1
prefix: "{device}/{release}/{date}"
patterns:
- "**/*.wic.gz"
artifact_dirs:
- tmp/deploy/imagesAn individual BspPreset entry can include its own deploy: block. Only the
fields that differ from the DeployConfig defaults override the global config;
all other fields keep their global values.
Merge order (later entries win):
- Global
deploy:— baseline for every build - Preset
deploy:— overrides only fields that differ from their defaults - CLI flags (
--provider,--container, …) — highest priority
deploy: # global: Azure, shared container
provider: azure
account_url: $ENV{AZURE_STORAGE_ACCOUNT_URL}
container: bsp-artifacts
prefix: "{vendor}/{device}/{release}/{date}"
registry:
bsp:
# Uses global settings unchanged.
- name: qemuarm64-scarthgap
device: qemuarm64
release: scarthgap
features: []
# Overrides only container and prefix; provider and account_url come from global.
- name: imx8mp-adv-scarthgap-release
description: "Advantech i.MX8MP Scarthgap – release artefacts"
device: imx8mp-adv
release: scarthgap
features: []
deploy:
container: imx8mp-release-artifacts # ← override
prefix: "release/{device}/{release}/{date}" # ← override
patterns: # ← override
- "**/*.wic.gz"
# Switches to AWS entirely for this preset only.
- name: aws-build-scarthgap
device: qemuarm64
release: scarthgap
features: []
deploy:
provider: aws # ← override: switch provider
container: my-s3-bucket # ← override: bucket name| Field | Type | Default | Description |
|---|---|---|---|
provider |
string | "azure" |
Cloud provider: "azure" or "aws" |
container |
string (opt.) | — | Azure Blob container name |
bucket |
string (opt.) | — | AWS S3 bucket name |
account_url |
string (opt.) | — | Azure account URL; supports $ENV{VAR} expansion. Falls back to the AZURE_STORAGE_ACCOUNT_URL env var. |
prefix |
string (opt.) | "{vendor}/{device}/{release}/{date}" |
Remote path prefix template (see placeholders) |
patterns |
list[str] | ["**/*.wic*", "**/*.tar.gz", "**/*.ext4", "**/*.sdimg"] |
Glob patterns for artifact files |
artifact_dirs |
list[str] | ["tmp/deploy/images", "tmp/deploy/sdk"] |
Subdirectories under the build path to scan |
include_manifest |
bool | true |
Upload a JSON manifest alongside artifacts |
include_build_manifest |
bool | true |
Upload the build-manifest.json written by bsp build |
archive |
object (opt.) | — | Bundle all artifacts into a single archive before uploading. See Archive bundling. |
region |
string (opt.) | — | AWS region (optional; boto3 default otherwise) |
profile |
string (opt.) | — | AWS credentials profile (optional) |
yocto_cache |
object (opt.) | — | Upload / restore Yocto DL_DIR / SSTATE_DIR caches. See Yocto cache upload. |
index |
object (opt.) | — | Generate a browsable index.html of the uploaded artifacts. See HTML index generation. |
The prefix field is a Python format string. The following variables are
available at deploy time:
| Placeholder | Example value | Description |
|---|---|---|
{device} |
qemuarm64 |
Device slug |
{release} |
scarthgap |
Release slug |
{distro} |
poky |
Effective distro slug |
{vendor} |
qemu |
Device vendor slug |
{date} |
2025-03-15 |
Build date (UTC, YYYY-MM-DD) |
{datetime} |
20250315-143022 |
Build date + time (UTC, YYYYMMDD-HHMMSS) |
Example prefixes:
{vendor}/{device}/{release}/{date}
→ qemu/qemuarm64/scarthgap/2025-03-15/
builds/{device}/{date}
→ builds/qemuarm64/2025-03-15/
release/{release}/{device}
→ release/scarthgap/qemuarm64/
By default every matching artifact file is uploaded individually. Set the
archive: sub-object inside deploy: to collect all artifacts into a single
compressed archive before uploading. Only the archive (plus the manifest
when include_manifest: true) is uploaded.
deploy:
provider: azure
container: bsp-artifacts
archive:
name: "firmware-{device}-{release}-{date}"
format: tar.gz| Field | Type | Default | Description |
|---|---|---|---|
name |
string | "artifacts-{device}-{date}" |
Archive filename template (without extension). Supports the same placeholders as prefix: {device}, {release}, {distro}, {vendor}, {date}, {datetime}. |
format |
string | "tar.gz" |
Compression format: tar.gz, tar.bz2, tar.xz, or zip. |
The appropriate file extension is appended automatically (e.g. .tar.gz for
tar.gz).
CLI equivalents:
# bsp deploy
bsp deploy my-preset \
--archive-name "firmware-{device}-{release}-{date}" \
--archive-format tar.gz
# bsp build --deploy
bsp build my-preset --deploy \
--deploy-archive-name "firmware-{device}-{release}-{date}" \
--deploy-archive-format tar.gzEnable yocto_cache in your deploy: block to also upload the Yocto build
caches (DL_DIR downloads cache and SSTATE_DIR shared-state cache) to the
same cloud prefix. Cache upload is opt-in (disabled by default) and has
no impact on the regular artifact upload pipeline.
deploy:
provider: azure
container: bsp-artifacts
prefix: "{vendor}/{device}/{release}/{date}"
yocto_cache:
enabled: true # master switch — must be true to upload anything
downloads: true # upload DL_DIR (default: true)
sstate: true # upload SSTATE_DIR (default: true)
# Optional: hard-code paths instead of using DL_DIR / SSTATE_DIR env vars
# downloads_path: /mnt/yocto/downloads
# sstate_path: /mnt/yocto/sstate-
Deploy – the tool reads
DL_DIR/SSTATE_DIRfrom the environment (or fromdownloads_path/sstate_pathif set in the registry). Each enabled cache directory is packed into atar.gzarchive and uploaded under{prefix}/cache/:{prefix}/cache/downloads.tar.gz {prefix}/cache/sstate.tar.gzCache metadata (remote URL, size, SHA-256) is recorded in the
yocto_cachesection ofmanifest.json. -
Gather – run
bsp gather --gather-cacheto download and extract the cache archives back to the local filesystem. A missing cache archive is treated as a soft warning — the rest of the gather succeeds normally.
| Field | Type | Default | Description |
|---|---|---|---|
enabled |
bool | false |
Master switch. Must be true to upload any caches. |
downloads |
bool | true |
Include DL_DIR in the upload / restore. |
sstate |
bool | true |
Include SSTATE_DIR in the upload / restore. |
downloads_path |
string (opt.) | — | Override the local DL_DIR path. Falls back to DL_DIR, then <topdir>/downloads (TOPDIR inferred from artifact_dirs). |
sstate_path |
string (opt.) | — | Override the local SSTATE_DIR path. Falls back to SSTATE_DIR, then <topdir>/sstate-cache (TOPDIR inferred from artifact_dirs). |
Upload caches with bsp deploy:
# Upload artifacts + DL_DIR + SSTATE_DIR
bsp deploy my-preset --deploy-cache
# Upload artifacts + DL_DIR only (skip sstate)
bsp deploy my-preset --deploy-cache --no-deploy-cache-sstate
# Upload artifacts + SSTATE_DIR only (skip downloads)
bsp deploy my-preset --deploy-cache --no-deploy-cache-downloads
# Same flags work with bsp build --deploy
bsp build my-preset --deploy --deploy-cacheRestore caches with bsp gather:
# Download artifacts + restore both caches to default dirs
bsp gather my-preset --gather-cache
# Specify exact restore paths
bsp gather my-preset \
--gather-cache \
--cache-downloads-dir /mnt/yocto/downloads \
--cache-sstate-dir /mnt/yocto/sstate
# Dry run – see what would be downloaded/restored
bsp gather my-preset --dry-run --gather-cacheNote:
bsp gather --gather-cacheuses theDL_DIRandSSTATE_DIRenvironment variables as default restore destinations (same as the deploy side). Explicit--cache-downloads-dir/--cache-sstate-dirflags take priority. If neither is set, archives are extracted into<topdir>/downloadsand<topdir>/sstate-cache, where TOPDIR is the Yocto build directory inferred from theartifact_dirsconfiguration (e.g.build/tmp/deploy/images→ TOPDIR =<dest-dir>/build/).
When caches are uploaded the manifest.json gains a yocto_cache section:
{
"schema_version": "1",
"artifacts": [ ... ],
"yocto_cache": {
"downloads": {
"name": "downloads.tar.gz",
"remote_url": "https://<account>.blob.core.windows.net/bsp-artifacts/acme/myboard/scarthgap/2025-01-15/cache/downloads.tar.gz",
"size_bytes": 2147483648,
"sha256": "..."
},
"sstate": {
"name": "sstate.tar.gz",
"remote_url": "https://...",
"size_bytes": 5368709120,
"sha256": "..."
}
}
}bsp gather --gather-cache uses this section to locate the correct archive
URLs. Old manifests that lack the yocto_cache section are handled
gracefully — the gatherer falls back to the heuristic
{prefix}/cache/{type}.tar.gz path.
Add an index: block to deploy: to publish a browsable index.html next to
the uploaded artifacts:
deploy:
provider: azure
container: bsp-artifacts
index:
enabled: true
title: "{vendor} {device} — {release}"
sign_urls: true
sas_expiry: "2038-01-19T03:14:06Z"
tree: true
collapse_depth: 1
search: true
show_dates: true
facets: [preset, machine, release, date]
theme: auto
exclude:
- "cache/*"| Field | Type | Default | Description |
|---|---|---|---|
enabled |
bool | false |
Master switch. Index generation is opt-in. |
title |
string | "{vendor} {device} — {release}" |
Page title template. Supports the same placeholders as prefix. |
sign_urls |
bool | true |
Link artifacts through read-only signed URLs (Azure SAS / S3 presigned). Set to false when a CDN, Front Door or custom domain fronts the container — relative links are emitted instead. |
sas_expiry |
string | "2038-01-19T03:14:06Z" |
Expiry timestamp (ISO-8601) for generated signed URLs. The default is the 32-bit time_t limit. |
root_index |
bool | true |
Deprecated and ignored. A single index.html is always written at the container root; no index.html is generated inside artifact folders. |
tree |
bool | true |
Render a collapsible tree that preserves the remote directory structure below the indexed prefix. Set to false for the legacy flat table. |
collapse_depth |
int | 1 |
Directory depth expanded by default in the tree view (1 expands only the top level). |
search |
bool | true |
Show the search / filter box. Plain substrings and simple * / ? globs are matched against the full relative path. |
exclude |
list | [] |
Glob patterns (matched against the path relative to the indexed prefix, or against the bare file name) omitted from the index. |
show_dates |
bool | true |
Show last-modified timestamps when the storage backend provides them. |
facets |
list | [preset, machine, release, date] |
Facet groups shown in the filter bar. Supported names: preset, machine, release, distro, vendor, date. An empty list disables faceted filtering. |
theme |
string | "auto" |
Colour scheme: auto (follows prefers-color-scheme), light or dark. |
accent |
string | "" |
CSS colour used as the page accent (e.g. "#0366d6"). Empty keeps the built-in accent. |
The page is self-contained (no external assets, no CDN JavaScript, no server),
so it loads from a private container through a single signed URL. It ships a
design-token stylesheet with automatic dark mode, a sticky header holding the
title, a clickable prefix breadcrumb and the filter bar, per-type artifact
icons, click-to-copy SHA-256 values, keyboard-navigable tree rows and a live
"N files · M total" summary. It lists
every artifact with its human-readable size, last-modified timestamp and short
SHA-256, links to manifest.json, and carries no-cache <meta> tags so
browsers never show stale, expired links.
Above the tree a faceted filter bar offers multi-select chips for the BSP
preset, machine, Yocto release and upload date (with Today / Last 7 days /
Last 30 days / Older buckets and a From–To date range). Values are
ANDed across groups and ORed within a group, chip counts update live, and every
active facet is encoded in the URL fragment so a filtered view can be
bookmarked or shared. Facet values are recorded at deploy time in an
index-meta.json sidecar next to manifest.json, so bsp deploy index
rebuilds and the container-root index keep them; when the sidecar is missing
they are recovered by inverting the configured prefix template. The
container-root index lists one row per build prefix with its facets, newest
first, and is filtered by the same bar.
In the default tree view the remote directory structure of the container is
preserved, so nested artifacts (images/…, sdk/…, cache archives)
keep their folders and identically named files in different directories stay
distinct. The inlined vanilla-JavaScript renderer provides:
- fold / unfold of directories, with per-directory file counts and aggregated sizes, plus Expand all / Collapse all buttons;
- search by substring or simple glob against the full relative path, auto-expanding the ancestors of every match;
- sorting by name, size or last-modified within each directory level;
- shareable state — the active query, type filter and expanded folders are mirrored into the URL hash.
A <noscript> fallback renders the same artifacts as the plain flat table, and
--flat (or tree: false) selects that table unconditionally. Only
index.html pages are skipped, so genuine HTML build artifacts such as reports
remain listed. Every interpolated value is HTML-escaped and the embedded JSON
data island is escaped so a hostile blob name cannot break out of its
<script> element.
The index is fully regenerated on every run from the current artifact set
(or, for bsp deploy index, from the live container listing) — it is never appended
to, so links are always fresh.
Storage accounts with allowBlobPublicAccess=false (and no $web static
website endpoint) cannot serve blobs anonymously. The generated index solves
this without weakening that posture: each artifact link is a read-only signed
URL, and index.html itself is fetched through a signed URL.
On Azure the backend picks the strongest available option:
- Account-key SAS — used when
AZURE_STORAGE_CONNECTION_STRING(or an explicit connection string) is available. Supports arbitrary expiry, so the 2038 sentinel works as-is. - User-delegation SAS — used when authenticated via
DefaultAzureCredential(az login, managed identity, service principal). Azure caps delegation keys at 7 days, so longer expiries are clamped automatically with a warning.
On AWS get_signed_url() returns an S3 presigned URL (capped at 7 days).
Trade-offs to be aware of:
- Anyone holding a link can download that blob until the SAS expires — treat the links as bearer tokens.
- User-delegation links expire after at most 7 days; schedule
bsp deploy index <container>(for example from a nightly job) to re-sign them. - Signed links are not written to logs, and the account key / connection string is never logged or embedded in the page.
- Uploads set
Content-Type: application/octet-stream(with noContent-Encoding) for artifacts so browsers do not transparently decompress*.wic.gzimages and corrupt them;index.htmlis stored astext/htmlso it renders instead of downloading.
Credentials are resolved in the following order:
AZURE_STORAGE_CONNECTION_STRINGenvironment variable — if set, the connection string is used directly (noaccount_urlneeded).deploy.account_url(orAZURE_STORAGE_ACCOUNT_URLenv var) +DefaultAzureCredential— supports any of the methods below transparently:
| Method | Required setup |
|---|---|
| Azure CLI | az login |
| Service principal | AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_TENANT_ID env vars |
| Managed Identity | Automatic on Azure VMs / AKS / App Service |
| Workload Identity | Automatic in AKS with OIDC |
Minimal local setup:
export AZURE_STORAGE_ACCOUNT_URL=https://myaccount.blob.core.windows.net
az login
bsp deploy my-preset --container bsp-artifactsService principal (CI):
export AZURE_CLIENT_ID=...
export AZURE_CLIENT_SECRET=...
export AZURE_TENANT_ID=...
export AZURE_STORAGE_ACCOUNT_URL=https://myaccount.blob.core.windows.net
bsp deploy my-preset --container bsp-artifactsCredentials are resolved using the standard boto3 credential chain:
- Environment variables:
AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY/AWS_SESSION_TOKEN - Shared credentials file:
~/.aws/credentials(set up withaws configure) - AWS config file:
~/.aws/config - IAM role (EC2 instance profile, ECS task role, Lambda execution role)
Minimal local setup:
aws configure # interactive prompts for key, secret, region
bsp deploy my-preset --provider aws --bucket my-s3-bucketEnvironment variables (CI):
export AWS_ACCESS_KEY_ID=...
export AWS_SECRET_ACCESS_KEY=...
export AWS_DEFAULT_REGION=eu-west-1
bsp deploy my-preset --provider aws --bucket my-s3-bucketUpload artifacts from a previous build to cloud storage.
bsp deploy <bsp_name> [OPTIONS]
bsp deploy --device <d> --release <r> [--feature <f>] [OPTIONS]
| Option | Description |
|---|---|
--provider PROVIDER |
Override provider: azure or aws |
--container CONTAINER / --bucket CONTAINER |
Override Azure container or AWS bucket name |
--prefix PREFIX |
Override remote path prefix template |
--pattern PATTERN |
Override glob patterns (repeatable; replaces registry config) |
--archive-name NAME |
Bundle artifacts into a single archive with this name (supports {device}, {release}, {distro}, {vendor}, {date}, {datetime}) |
--archive-format FORMAT |
Archive format: tar.gz (default), tar.bz2, tar.xz, zip |
--deploy-cache |
Also upload Yocto DL_DIR / SSTATE_DIR caches |
--no-deploy-cache-downloads |
Skip uploading the DL_DIR downloads cache (use with --deploy-cache) |
--no-deploy-cache-sstate |
Skip uploading the SSTATE_DIR sstate cache (use with --deploy-cache) |
--update-index |
Regenerate and upload the browsable container-root index.html after a successful deploy |
--no-update-index |
Never generate an index, even when enabled in the registry |
--dry-run |
List what would be uploaded without uploading (no credentials needed) |
Examples:
# Deploy using registry settings
bsp deploy poky-qemuarm64-scarthgap
# Dry run – see what would be uploaded
bsp deploy poky-qemuarm64-scarthgap --dry-run
# Override container at runtime
bsp deploy poky-qemuarm64-scarthgap --container my-other-container
# Deploy to AWS with a custom prefix
bsp deploy poky-qemuarm64-scarthgap \
--provider aws \
--bucket my-s3-bucket \
--prefix "builds/{device}/{release}/{date}"
# Upload only *.wic.gz files
bsp deploy poky-qemuarm64-scarthgap --pattern "**/*.wic.gz"
# Deploy by components (no preset required)
bsp deploy --device qemuarm64 --release scarthgap --container bsp-artifacts
# Deploy artifacts + Yocto caches
bsp deploy poky-qemuarm64-scarthgap --deploy-cache
# Deploy and publish a browsable, SAS-signed index.html
bsp deploy poky-qemuarm64-scarthgap --update-indexDeploy artifacts automatically after a successful build. All --deploy-*
flags mirror the bsp deploy options.
bsp build <bsp_name> --deploy [--deploy-provider PROVIDER]
[--deploy-container CONTAINER] [--deploy-prefix PREFIX]
| Option | Description |
|---|---|
--deploy |
Deploy artifacts after a successful build |
--deploy-provider PROVIDER |
Override storage provider |
--deploy-container CONTAINER |
Override container or bucket name |
--deploy-prefix PREFIX |
Override path prefix template |
--deploy-archive-name NAME |
Bundle artifacts into a single archive with this name (supports {device}, {release}, {distro}, {vendor}, {date}, {datetime}) |
--deploy-archive-format FORMAT |
Archive format: tar.gz (default), tar.bz2, tar.xz, zip |
--deploy-cache |
Also upload Yocto DL_DIR / SSTATE_DIR caches after a successful build |
--no-deploy-cache-downloads |
Skip uploading the DL_DIR downloads cache |
--no-deploy-cache-sstate |
Skip uploading the SSTATE_DIR sstate cache |
Examples:
# Build and deploy in one step
bsp build poky-qemuarm64-scarthgap --deploy
# Build and deploy to a specific AWS bucket
bsp build poky-qemuarm64-scarthgap \
--deploy \
--deploy-provider aws \
--deploy-container my-s3-bucket
# Build, deploy artifacts and caches in one step
bsp build poky-qemuarm64-scarthgap --deploy --deploy-cacheRebuild the browsable HTML index straight from the live container listing — no build required. This is the command to schedule when signed URLs expire.
A container has exactly one index page: index.html at its root, listing
every artifact of every prefix as a navigable tree. No index.html is written
inside artifact folders. Deploying with --update-index refreshes the same
page, so bsp deploy index and bsp build --deploy --update-index leave the
container in the same state.
bsp deploy index [container] [OPTIONS]
The provider, container/bucket, Azure account URL, AWS region/profile and the
index: options are taken from the root-level deploy: block of the registry
configuration (bsp-registry.yaml); any option given on the command line wins:
deploy:
provider: azure # "azure" (default) or "aws"
account_url: "https://modularbsp.blob.core.windows.net" # Azure only; supports $ENV{} expansion
container: bsp-registry-artifacts # Azure container nameWith such a registry, bsp deploy index needs no further arguments.
| Option | Description |
|---|---|
--prefix PREFIX |
Deprecated and ignored: the root index.html always covers the whole container |
--root |
Deprecated and ignored: the root index.html is always generated |
--provider PROVIDER |
Provider: azure or aws (default: deploy.provider, else azure) |
--account-url URL |
Azure storage account URL (default: deploy.account_url) |
--no-sign-urls |
Emit relative links instead of signed URLs (CDN / custom domain) |
--sas-expiry ISO8601 |
Expiry for generated signed URLs (default 2038-01-19T03:14:06Z) |
--tree / --flat |
Render the collapsible directory tree (default) or the legacy flat table |
--collapse-depth N |
Directory depth expanded by default in the tree view (default 1) |
--exclude PATTERN |
Glob pattern of paths to omit from the index (repeatable) |
--no-search |
Omit the interactive search box |
--dry-run |
Show what would be generated without uploading (no credentials needed) |
bsp deploy index # container from bsp-registry.yaml
bsp deploy index bsp-artifacts
bsp deploy index bsp-artifacts --dry-run
bsp deploy index bsp-artifacts --collapse-depth 2
bsp deploy index bsp-artifacts --exclude 'cache/*' --exclude '*.sig'
bsp deploy index bsp-artifacts --flat --no-searchDownload previously deployed artifacts from cloud storage.
bsp gather <bsp_name> [OPTIONS]
bsp gather --device <d> --release <r> [--feature <f>] [OPTIONS]
| Option | Description |
|---|---|
--provider PROVIDER |
Override provider: azure or aws |
--container CONTAINER / --bucket CONTAINER |
Override Azure container or AWS bucket name |
--prefix PREFIX |
Override remote path prefix template |
--dest-dir PATH |
Local directory to write artifacts into |
--date DATE |
Date override for {date} placeholder (YYYY-MM-DD) |
--gather-cache |
Also restore Yocto caches from cloud storage if available |
--cache-downloads-dir PATH |
Local directory to restore the DL_DIR cache into |
--cache-sstate-dir PATH |
Local directory to restore the SSTATE_DIR cache into |
--dry-run |
List what would be downloaded without downloading (no credentials needed) |
Examples:
# Download latest artifacts for a preset
bsp gather poky-qemuarm64-scarthgap --dest-dir ./artifacts
# Download artifacts from a specific date
bsp gather poky-qemuarm64-scarthgap --dest-dir ./artifacts --date 2025-01-15
# Download artifacts + restore Yocto caches
bsp gather poky-qemuarm64-scarthgap \
--dest-dir ./artifacts \
--gather-cache
# Restore caches to explicit directories
bsp gather poky-qemuarm64-scarthgap \
--gather-cache \
--cache-downloads-dir /mnt/yocto/downloads \
--cache-sstate-dir /mnt/yocto/sstate
# Dry run
bsp gather poky-qemuarm64-scarthgap --dry-run --gather-cache--dry-run lists all artifacts that would be uploaded and where they would go,
without performing any uploads and without requiring cloud credentials or
installed cloud SDKs.
bsp deploy poky-qemuarm64-scarthgap --dry-run
bsp deploy poky-qemuarm64-scarthgap --dry-run --deploy-cacheExample output:
[dry-run] Would upload 3 artifact(s):
core-image-minimal-qemuarm64.rootfs.wic.gz → dry-run:qemu/qemuarm64/scarthgap/2025-03-15/core-image-minimal-qemuarm64.rootfs.wic.gz
core-image-minimal-qemuarm64.rootfs.tar.bz2 → dry-run:qemu/qemuarm64/scarthgap/2025-03-15/core-image-minimal-qemuarm64.rootfs.tar.bz2
manifest.json → dry-run:qemu/qemuarm64/scarthgap/2025-03-15/manifest.json
[dry-run] Would upload 2 Yocto cache archive(s):
downloads: downloads.tar.gz → dry-run:qemu/qemuarm64/scarthgap/2025-03-15/cache/downloads.tar.gz
sstate: sstate.tar.gz → dry-run:qemu/qemuarm64/scarthgap/2025-03-15/cache/sstate.tar.gz
When include_build_manifest: true (default), the build-manifest.json
written by bsp build into the build path is uploaded as
<prefix>/build-manifest.json, so the deployed artifacts stay traceable to
the exact registry, device, release and feature set they were built from.
It is looked up at <build_path>/build-manifest.json and, failing that, at
<build_path>/build/build-manifest.json. When the file does not exist a
warning is logged and the deploy continues. Its remote URL is also recorded
under the build_manifest key of manifest.json.
Pass --no-build-manifest to bsp deploy (or to bsp build --deploy) to
skip the upload for a single run.
The build manifest (schema_version: "2") never contains absolute host paths.
Every path is emitted relative to one of the anchors described in its roots
section:
| Anchor | Meaning |
|---|---|
roots.registry |
The directory containing the registry YAML (always .) |
roots.build |
The build directory, relative to the registry root |
Paths that fall outside both anchors are replaced by placeholders:
${HOME}/<relative> when below the user's home directory, and
<external>/<name> otherwise. Free-form values that may embed paths
(inputs.local_conf lines, components.container.runtime_args,
inputs.environment_variables[].value, build options and
provenance.cli.command) are scrubbed with the same rules, using the
${registry} and ${build} tokens for anchor prefixes. provenance.cli.argv[0]
is reduced to the bare program name (e.g. bsp).
When include_manifest: true (default), a manifest.json file is uploaded
alongside the artifacts. It contains:
{
"schema_version": "1",
"generated_at": "2025-03-15T14:30:22+00:00",
"provider": "azure",
"build": {
"device": "qemuarm64",
"release": "scarthgap",
"distro": "poky",
"vendor": "qemu"
},
"artifacts": [
{
"name": "core-image-minimal-qemuarm64.rootfs.wic.gz",
"remote_url": "https://myaccount.blob.core.windows.net/bsp-artifacts/qemu/qemuarm64/scarthgap/2025-03-15/core-image-minimal-qemuarm64.rootfs.wic.gz",
"size_bytes": 35651584,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
],
"total_size_bytes": 35651584,
"yocto_cache": {
"downloads": {
"name": "downloads.tar.gz",
"remote_url": "https://myaccount.blob.core.windows.net/bsp-artifacts/qemu/qemuarm64/scarthgap/2025-03-15/cache/downloads.tar.gz",
"size_bytes": 2147483648,
"sha256": "..."
},
"sstate": {
"name": "sstate.tar.gz",
"remote_url": "https://myaccount.blob.core.windows.net/bsp-artifacts/qemu/qemuarm64/scarthgap/2025-03-15/cache/sstate.tar.gz",
"size_bytes": 5368709120,
"sha256": "..."
}
}
}The yocto_cache section is only present when caches were uploaded.
If an individual file upload fails, the tool continues uploading the remaining files and reports a summary at the end:
Uploaded 2 artifact(s):
core-image-minimal-qemuarm64.rootfs.tar.bz2 → https://...
manifest.json → https://...
WARNING: 1 artifact(s) failed to upload:
core-image-minimal-qemuarm64.rootfs.wic.gz: [Errno 32] Broken pipe
The process exits with code 0 when at least one file succeeded, or 1 when all uploads fail.
from bsp import BspManager
manager = BspManager("bsp-registry.yaml")
manager.initialize()
# Dry-run deploy for a preset
result = manager.deploy_bsp("poky-qemuarm64-scarthgap", dry_run=True)
print(f"Would upload {result.success_count} artifact(s)")
# Deploy with runtime overrides
result = manager.deploy_bsp(
"poky-qemuarm64-scarthgap",
deploy_overrides={
"provider": "aws",
"container": "my-s3-bucket",
"prefix": "builds/{device}/{release}/{date}",
},
)
for artifact in result.artifacts:
print(f" {artifact.local_path.name} → {artifact.remote_url}")
print(f" sha256: {artifact.sha256}")
# Gather artifacts
result = manager.gather_bsp(
"poky-qemuarm64-scarthgap",
dest_dir="./artifacts",
)
print(f"Downloaded {result.total_count} artifact(s)")
# Gather artifacts + restore Yocto caches
result = manager.gather_bsp(
"poky-qemuarm64-scarthgap",
dest_dir="./artifacts",
gather_cache=True,
cache_downloads_dest="/mnt/yocto/downloads",
cache_sstate_dest="/mnt/yocto/sstate",
)
print(f"Restored {len(result.cache_artifacts)} cache(s)")
# Use the lower-level deployer + storage backend directly
from bsp.storage import create_backend
from bsp.deployer import ArtifactDeployer
from bsp.models import ArchiveConfig, DeployConfig, YoctoCacheConfig
config = DeployConfig(
provider="azure",
container="bsp-artifacts",
prefix="{device}/{release}/{date}",
patterns=["**/*.wic.gz"],
artifact_dirs=["tmp/deploy/images"],
archive=ArchiveConfig(
name="firmware-{device}-{release}-{date}",
format="tar.gz",
),
yocto_cache=YoctoCacheConfig(
enabled=True,
downloads=True,
sstate=True,
),
)
backend = create_backend("azure", container_name="bsp-artifacts")
deployer = ArtifactDeployer(config, backend)
result = deployer.deploy(
build_path="build/poky-qemuarm64-scarthgap",
device="qemuarm64",
release="scarthgap",
distro="poky",
vendor="qemu",
downloads_path="/mnt/yocto/downloads",
sstate_path="/mnt/yocto/sstate",
)
print(deployer.generate_manifest(result, device="qemuarm64", release="scarthgap"))name: Build and Deploy BSP
on:
push:
branches: [main]
jobs:
build-deploy:
runs-on: ubuntu-latest
permissions:
id-token: write # required for OIDC / Workload Identity federation
contents: read
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install bsp-registry-tools with Azure support
run: pip install "bsp-registry-tools[azure]"
- name: Azure Login (OIDC)
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Build BSP
run: bsp build poky-qemuarm64-scarthgap
- name: Deploy artifacts (+ caches)
env:
AZURE_STORAGE_ACCOUNT_URL: ${{ secrets.AZURE_STORAGE_ACCOUNT_URL }}
run: |
bsp deploy poky-qemuarm64-scarthgap \
--container bsp-artifacts \
--prefix "ci/{device}/{release}/${{ github.sha }}" \
--deploy-cachename: Build and Deploy BSP (AWS)
on:
push:
branches: [main]
jobs:
build-deploy:
runs-on: ubuntu-latest
permissions:
id-token: write # required for OIDC
contents: read
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install bsp-registry-tools with AWS support
run: pip install "bsp-registry-tools[aws]"
- name: Configure AWS Credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: eu-west-1
- name: Build BSP
run: bsp build poky-qemuarm64-scarthgap
- name: Deploy artifacts (+ caches)
run: |
bsp deploy poky-qemuarm64-scarthgap \
--provider aws \
--bucket my-bsp-artifacts \
--prefix "ci/{device}/{release}/${{ github.sha }}" \
--deploy-cache- Overview
- Quick Start
- Installation
- Registry Configuration
- Authentication
- CLI Reference
- Dry-run mode
- Artifact manifest
- Partial failures
- Python API
- CI/CD integration
After a Yocto build, images and SDKs land under:
<build_path>/tmp/deploy/images/
<build_path>/tmp/deploy/sdk/
bsp deploy finds all files that match the configured glob patterns in those
directories and uploads them to your cloud storage provider. An optional JSON
manifest (with artifact names, sizes, and SHA-256 checksums) is uploaded
alongside the artifacts.
Config can live either in the registry YAML (checked in, shared by the team) or be overridden entirely from the command line.
# 1. Install cloud SDK extras (one-time)
pip install "bsp-registry-tools[azure]" # Azure
pip install "bsp-registry-tools[aws]" # AWS
pip install "bsp-registry-tools[deploy]" # both
# 2. Authenticate (one-time)
az login # Azure (interactive)
aws configure # AWS (interactive)
# 3. Build and deploy in one step
bsp build poky-qemuarm64-scarthgap --deploy --deploy-container bsp-artifacts
# — or — deploy separately after a successful build
bsp deploy poky-qemuarm64-scarthgap --container bsp-artifacts
# Preview what would be uploaded (no credentials required)
bsp deploy poky-qemuarm64-scarthgap --dry-runCloud SDK dependencies are optional to avoid forcing them on users who do not need deployment.
# Azure Blob Storage support
pip install "bsp-registry-tools[azure]"
# installs: azure-storage-blob>=12.0, azure-identity>=1.0
# AWS S3 support
pip install "bsp-registry-tools[aws]"
# installs: boto3>=1.20
# Both providers
pip install "bsp-registry-tools[deploy]"--dry-run mode works without any cloud SDK installed.
Add a top-level deploy: block to your registry YAML. It applies to every
build by default.
specification:
version: "2.0"
deploy:
provider: azure
account_url: $ENV{AZURE_STORAGE_ACCOUNT_URL} # supports $ENV{} expansion
container: bsp-artifacts
prefix: "{vendor}/{device}/{release}/{date}"
patterns:
- "**/*.wic.gz"
- "**/*.wic.bz2"
- "**/*.tar.bz2"
- "**/*.ext4"
- "**/*.sdimg"
artifact_dirs:
- tmp/deploy/images
- tmp/deploy/sdk
include_manifest: true
include_build_manifest: true
# Optional: bundle all artifacts into a single archive before uploading
archive:
name: "firmware-{device}-{release}-{date}"
format: tar.gz
registry:
# ...AWS variant:
deploy:
provider: aws
bucket: my-s3-bucket
region: eu-west-1
prefix: "{device}/{release}/{date}"
patterns:
- "**/*.wic.gz"
artifact_dirs:
- tmp/deploy/imagesAn individual BspPreset entry can include its own deploy: block. Only the
fields that differ from the DeployConfig defaults override the global config;
all other fields keep their global values.
Merge order (later entries win):
- Global
deploy:— baseline for every build - Preset
deploy:— overrides only fields that differ from their defaults - CLI flags (
--provider,--container, …) — highest priority
deploy: # global: Azure, shared container
provider: azure
account_url: $ENV{AZURE_STORAGE_ACCOUNT_URL}
container: bsp-artifacts
prefix: "{vendor}/{device}/{release}/{date}"
registry:
bsp:
# Uses global settings unchanged.
- name: qemuarm64-scarthgap
device: qemuarm64
release: scarthgap
features: []
# Overrides only container and prefix; provider and account_url come from global.
- name: imx8mp-adv-scarthgap-release
description: "Advantech i.MX8MP Scarthgap – release artefacts"
device: imx8mp-adv
release: scarthgap
features: []
deploy:
container: imx8mp-release-artifacts # ← override
prefix: "release/{device}/{release}/{date}" # ← override
patterns: # ← override
- "**/*.wic.gz"
# Switches to AWS entirely for this preset only.
- name: aws-build-scarthgap
device: qemuarm64
release: scarthgap
features: []
deploy:
provider: aws # ← override: switch provider
container: my-s3-bucket # ← override: bucket name| Field | Type | Default | Description |
|---|---|---|---|
provider |
string | "azure" |
Cloud provider: "azure" or "aws" |
container |
string (opt.) | — | Azure Blob container name |
bucket |
string (opt.) | — | AWS S3 bucket name |
account_url |
string (opt.) | — | Azure account URL; supports $ENV{VAR} expansion. Falls back to the AZURE_STORAGE_ACCOUNT_URL env var. |
prefix |
string (opt.) | "{vendor}/{device}/{release}/{date}" |
Remote path prefix template (see placeholders) |
patterns |
list[str] | ["**/*.wic*", "**/*.tar.gz", "**/*.ext4", "**/*.sdimg"] |
Glob patterns for artifact files |
artifact_dirs |
list[str] | ["tmp/deploy/images", "tmp/deploy/sdk"] |
Subdirectories under the build path to scan |
include_manifest |
bool | true |
Upload a JSON manifest alongside artifacts |
include_build_manifest |
bool | true |
Upload the build-manifest.json written by bsp build |
archive |
object (opt.) | — | Bundle all artifacts into a single archive before uploading. See Archive bundling. |
region |
string (opt.) | — | AWS region (optional; boto3 default otherwise) |
profile |
string (opt.) | — | AWS credentials profile (optional) |
The prefix field is a Python format string. The following variables are
available at deploy time:
| Placeholder | Example value | Description |
|---|---|---|
{device} |
qemuarm64 |
Device slug |
{release} |
scarthgap |
Release slug |
{distro} |
poky |
Effective distro slug |
{vendor} |
qemu |
Device vendor slug |
{date} |
2025-03-15 |
Build date (UTC, YYYY-MM-DD) |
{datetime} |
20250315-143022 |
Build date + time (UTC, YYYYMMDD-HHMMSS) |
Example prefixes:
{vendor}/{device}/{release}/{date}
→ qemu/qemuarm64/scarthgap/2025-03-15/
builds/{device}/{date}
→ builds/qemuarm64/2025-03-15/
release/{release}/{device}
→ release/scarthgap/qemuarm64/
By default every matching artifact file is uploaded individually. Set the
archive: sub-object inside deploy: to collect all artifacts into a single
compressed archive before uploading. Only the archive (plus the manifest
when include_manifest: true) is uploaded.
deploy:
provider: azure
container: bsp-artifacts
archive:
name: "firmware-{device}-{release}-{date}"
format: tar.gz| Field | Type | Default | Description |
|---|---|---|---|
name |
string | "artifacts-{device}-{date}" |
Archive filename template (without extension). Supports the same placeholders as prefix: {device}, {release}, {distro}, {vendor}, {date}, {datetime}. |
format |
string | "tar.gz" |
Compression format: tar.gz, tar.bz2, tar.xz, or zip. |
The appropriate file extension is appended automatically (e.g. .tar.gz for
tar.gz).
CLI equivalents:
# bsp deploy
bsp deploy my-preset \
--archive-name "firmware-{device}-{release}-{date}" \
--archive-format tar.gz
# bsp build --deploy
bsp build my-preset --deploy \
--deploy-archive-name "firmware-{device}-{release}-{date}" \
--deploy-archive-format tar.gzAdd an index: block to deploy: to publish a browsable index.html next to
the uploaded artifacts:
deploy:
provider: azure
container: bsp-artifacts
index:
enabled: true
title: "{vendor} {device} — {release}"
sign_urls: true
sas_expiry: "2038-01-19T03:14:06Z"
tree: true
collapse_depth: 1
search: true
show_dates: true
facets: [preset, machine, release, date]
theme: auto
exclude:
- "cache/*"| Field | Type | Default | Description |
|---|---|---|---|
enabled |
bool | false |
Master switch. Index generation is opt-in. |
title |
string | "{vendor} {device} — {release}" |
Page title template. Supports the same placeholders as prefix. |
sign_urls |
bool | true |
Link artifacts through read-only signed URLs (Azure SAS / S3 presigned). Set to false when a CDN, Front Door or custom domain fronts the container — relative links are emitted instead. |
sas_expiry |
string | "2038-01-19T03:14:06Z" |
Expiry timestamp (ISO-8601) for generated signed URLs. The default is the 32-bit time_t limit. |
root_index |
bool | true |
Deprecated and ignored. A single index.html is always written at the container root; no index.html is generated inside artifact folders. |
tree |
bool | true |
Render a collapsible tree that preserves the remote directory structure below the indexed prefix. Set to false for the legacy flat table. |
collapse_depth |
int | 1 |
Directory depth expanded by default in the tree view (1 expands only the top level). |
search |
bool | true |
Show the search / filter box. Plain substrings and simple * / ? globs are matched against the full relative path. |
exclude |
list | [] |
Glob patterns (matched against the path relative to the indexed prefix, or against the bare file name) omitted from the index. |
show_dates |
bool | true |
Show last-modified timestamps when the storage backend provides them. |
facets |
list | [preset, machine, release, date] |
Facet groups shown in the filter bar. Supported names: preset, machine, release, distro, vendor, date. An empty list disables faceted filtering. |
theme |
string | "auto" |
Colour scheme: auto (follows prefers-color-scheme), light or dark. |
accent |
string | "" |
CSS colour used as the page accent (e.g. "#0366d6"). Empty keeps the built-in accent. |
The page is self-contained (no external assets, no CDN JavaScript, no server),
so it loads from a private container through a single signed URL. It ships a
design-token stylesheet with automatic dark mode, a sticky header holding the
title, a clickable prefix breadcrumb and the filter bar, per-type artifact
icons, click-to-copy SHA-256 values, keyboard-navigable tree rows and a live
"N files · M total" summary. It lists
every artifact with its human-readable size, last-modified timestamp and short
SHA-256, links to manifest.json, and carries no-cache <meta> tags so
browsers never show stale, expired links.
Above the tree a faceted filter bar offers multi-select chips for the BSP
preset, machine, Yocto release and upload date (with Today / Last 7 days /
Last 30 days / Older buckets and a From–To date range). Values are
ANDed across groups and ORed within a group, chip counts update live, and every
active facet is encoded in the URL fragment so a filtered view can be
bookmarked or shared. Facet values are recorded at deploy time in an
index-meta.json sidecar next to manifest.json, so bsp deploy index
rebuilds and the container-root index keep them; when the sidecar is missing
they are recovered by inverting the configured prefix template. The
container-root index lists one row per build prefix with its facets, newest
first, and is filtered by the same bar.
In the default tree view the remote directory structure of the container is
preserved, so nested artifacts (images/…, sdk/…, cache archives)
keep their folders and identically named files in different directories stay
distinct. The inlined vanilla-JavaScript renderer provides:
- fold / unfold of directories, with per-directory file counts and aggregated sizes, plus Expand all / Collapse all buttons;
- search by substring or simple glob against the full relative path, auto-expanding the ancestors of every match;
- sorting by name, size or last-modified within each directory level;
- shareable state — the active query, type filter and expanded folders are mirrored into the URL hash.
A <noscript> fallback renders the same artifacts as the plain flat table, and
--flat (or tree: false) selects that table unconditionally. Only
index.html pages are skipped, so genuine HTML build artifacts such as reports
remain listed. Every interpolated value is HTML-escaped and the embedded JSON
data island is escaped so a hostile blob name cannot break out of its
<script> element.
The index is fully regenerated on every run from the current artifact set
(or, for bsp deploy index, from the live container listing) — it is never appended
to, so links are always fresh.
Storage accounts with allowBlobPublicAccess=false (and no $web static
website endpoint) cannot serve blobs anonymously. The generated index solves
this without weakening that posture: each artifact link is a read-only signed
URL, and index.html itself is fetched through a signed URL.
On Azure the backend picks the strongest available option:
- Account-key SAS — used when
AZURE_STORAGE_CONNECTION_STRING(or an explicit connection string) is available. Supports arbitrary expiry, so the 2038 sentinel works as-is. - User-delegation SAS — used when authenticated via
DefaultAzureCredential(az login, managed identity, service principal). Azure caps delegation keys at 7 days, so longer expiries are clamped automatically with a warning.
On AWS get_signed_url() returns an S3 presigned URL (capped at 7 days).
Trade-offs to be aware of:
- Anyone holding a link can download that blob until the SAS expires — treat the links as bearer tokens.
- User-delegation links expire after at most 7 days; schedule
bsp deploy index <container>(for example from a nightly job) to re-sign them. - Signed links are not written to logs, and the account key / connection string is never logged or embedded in the page.
- Uploads set
Content-Type: application/octet-stream(with noContent-Encoding) for artifacts so browsers do not transparently decompress*.wic.gzimages and corrupt them;index.htmlis stored astext/htmlso it renders instead of downloading.
Credentials are resolved in the following order:
AZURE_STORAGE_CONNECTION_STRINGenvironment variable — if set, the connection string is used directly (noaccount_urlneeded).deploy.account_url(orAZURE_STORAGE_ACCOUNT_URLenv var) +DefaultAzureCredential— supports any of the methods below transparently:
| Method | Required setup |
|---|---|
| Azure CLI | az login |
| Service principal | AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_TENANT_ID env vars |
| Managed Identity | Automatic on Azure VMs / AKS / App Service |
| Workload Identity | Automatic in AKS with OIDC |
Minimal local setup:
export AZURE_STORAGE_ACCOUNT_URL=https://myaccount.blob.core.windows.net
az login
bsp deploy my-preset --container bsp-artifactsService principal (CI):
export AZURE_CLIENT_ID=...
export AZURE_CLIENT_SECRET=...
export AZURE_TENANT_ID=...
export AZURE_STORAGE_ACCOUNT_URL=https://myaccount.blob.core.windows.net
bsp deploy my-preset --container bsp-artifactsCredentials are resolved using the standard boto3 credential chain:
- Environment variables:
AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY/AWS_SESSION_TOKEN - Shared credentials file:
~/.aws/credentials(set up withaws configure) - AWS config file:
~/.aws/config - IAM role (EC2 instance profile, ECS task role, Lambda execution role)
Minimal local setup:
aws configure # interactive prompts for key, secret, region
bsp deploy my-preset --provider aws --bucket my-s3-bucketEnvironment variables (CI):
export AWS_ACCESS_KEY_ID=...
export AWS_SECRET_ACCESS_KEY=...
export AWS_DEFAULT_REGION=eu-west-1
bsp deploy my-preset --provider aws --bucket my-s3-bucketUpload artifacts from a previous build to cloud storage.
bsp deploy <bsp_name> [OPTIONS]
bsp deploy --device <d> --release <r> [--feature <f>] [OPTIONS]
| Option | Description |
|---|---|
--provider PROVIDER |
Override provider: azure or aws |
--container CONTAINER / --bucket CONTAINER |
Override Azure container or AWS bucket name |
--prefix PREFIX |
Override remote path prefix template |
--pattern PATTERN |
Override glob patterns (repeatable; replaces registry config) |
--archive-name NAME |
Bundle artifacts into a single archive with this name (supports {device}, {release}, {distro}, {vendor}, {date}, {datetime}) |
--archive-format FORMAT |
Archive format: tar.gz (default), tar.bz2, tar.xz, zip |
--update-index |
Regenerate and upload the browsable container-root index.html after a successful deploy |
--no-update-index |
Never generate an index, even when enabled in the registry |
--dry-run |
List what would be uploaded without uploading (no credentials needed) |
Examples:
# Deploy using registry settings
bsp deploy poky-qemuarm64-scarthgap
# Dry run – see what would be uploaded
bsp deploy poky-qemuarm64-scarthgap --dry-run
# Override container at runtime
bsp deploy poky-qemuarm64-scarthgap --container my-other-container
# Deploy to AWS with a custom prefix
bsp deploy poky-qemuarm64-scarthgap \
--provider aws \
--bucket my-s3-bucket \
--prefix "builds/{device}/{release}/{date}"
# Upload only *.wic.gz files
bsp deploy poky-qemuarm64-scarthgap --pattern "**/*.wic.gz"
# Deploy by components (no preset required)
bsp deploy --device qemuarm64 --release scarthgap --container bsp-artifactsDeploy artifacts automatically after a successful build. All --deploy-*
flags mirror the bsp deploy options.
bsp build <bsp_name> --deploy [--deploy-provider PROVIDER]
[--deploy-container CONTAINER] [--deploy-prefix PREFIX]
| Option | Description |
|---|---|
--deploy |
Deploy artifacts after a successful build |
--deploy-provider PROVIDER |
Override storage provider |
--deploy-container CONTAINER |
Override container or bucket name |
--deploy-prefix PREFIX |
Override path prefix template |
--deploy-archive-name NAME |
Bundle artifacts into a single archive with this name (supports {device}, {release}, {distro}, {vendor}, {date}, {datetime}) |
--deploy-archive-format FORMAT |
Archive format: tar.gz (default), tar.bz2, tar.xz, zip |
Examples:
# Build and deploy in one step
bsp build poky-qemuarm64-scarthgap --deploy
# Build and deploy to a specific AWS bucket
bsp build poky-qemuarm64-scarthgap \
--deploy \
--deploy-provider aws \
--deploy-container my-s3-bucket--dry-run lists all artifacts that would be uploaded and where they would go,
without performing any uploads and without requiring cloud credentials or
installed cloud SDKs.
bsp deploy poky-qemuarm64-scarthgap --dry-runExample output:
[dry-run] Would upload 3 artifact(s):
core-image-minimal-qemuarm64.rootfs.wic.gz → dry-run:qemu/qemuarm64/scarthgap/2025-03-15/core-image-minimal-qemuarm64.rootfs.wic.gz
core-image-minimal-qemuarm64.rootfs.tar.bz2 → dry-run:qemu/qemuarm64/scarthgap/2025-03-15/core-image-minimal-qemuarm64.rootfs.tar.bz2
manifest.json → dry-run:qemu/qemuarm64/scarthgap/2025-03-15/manifest.json
When include_build_manifest: true (default), the build-manifest.json
written by bsp build into the build path is uploaded as
<prefix>/build-manifest.json, so the deployed artifacts stay traceable to
the exact registry, device, release and feature set they were built from.
It is looked up at <build_path>/build-manifest.json and, failing that, at
<build_path>/build/build-manifest.json. When the file does not exist a
warning is logged and the deploy continues. Its remote URL is also recorded
under the build_manifest key of manifest.json.
Pass --no-build-manifest to bsp deploy (or to bsp build --deploy) to
skip the upload for a single run.
The build manifest (schema_version: "2") never contains absolute host paths.
Every path is emitted relative to one of the anchors described in its roots
section:
| Anchor | Meaning |
|---|---|
roots.registry |
The directory containing the registry YAML (always .) |
roots.build |
The build directory, relative to the registry root |
Paths that fall outside both anchors are replaced by placeholders:
${HOME}/<relative> when below the user's home directory, and
<external>/<name> otherwise. Free-form values that may embed paths
(inputs.local_conf lines, components.container.runtime_args,
inputs.environment_variables[].value, build options and
provenance.cli.command) are scrubbed with the same rules, using the
${registry} and ${build} tokens for anchor prefixes. provenance.cli.argv[0]
is reduced to the bare program name (e.g. bsp).
When include_manifest: true (default), a manifest.json file is uploaded
alongside the artifacts. It contains:
{
"schema_version": "1",
"generated_at": "2025-03-15T14:30:22+00:00",
"provider": "azure",
"build": {
"device": "qemuarm64",
"release": "scarthgap",
"distro": "poky",
"vendor": "qemu"
},
"artifacts": [
{
"name": "core-image-minimal-qemuarm64.rootfs.wic.gz",
"remote_url": "https://myaccount.blob.core.windows.net/bsp-artifacts/qemu/qemuarm64/scarthgap/2025-03-15/core-image-minimal-qemuarm64.rootfs.wic.gz",
"size_bytes": 35651584,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
],
"total_size_bytes": 35651584
}If an individual file upload fails, the tool continues uploading the remaining files and reports a summary at the end:
Uploaded 2 artifact(s):
core-image-minimal-qemuarm64.rootfs.tar.bz2 → https://...
manifest.json → https://...
WARNING: 1 artifact(s) failed to upload:
core-image-minimal-qemuarm64.rootfs.wic.gz: [Errno 32] Broken pipe
The process exits with code 0 when at least one file succeeded, or 1 when all uploads fail.
from bsp import BspManager
manager = BspManager("bsp-registry.yaml")
manager.initialize()
# Dry-run deploy for a preset
result = manager.deploy_bsp("poky-qemuarm64-scarthgap", dry_run=True)
print(f"Would upload {result.success_count} artifact(s)")
# Deploy with runtime overrides
result = manager.deploy_bsp(
"poky-qemuarm64-scarthgap",
deploy_overrides={
"provider": "aws",
"container": "my-s3-bucket",
"prefix": "builds/{device}/{release}/{date}",
},
)
for artifact in result.artifacts:
print(f" {artifact.local_path.name} → {artifact.remote_url}")
print(f" sha256: {artifact.sha256}")
# Deploy by components
result = manager.deploy_by_components(
device_slug="qemuarm64",
release_slug="scarthgap",
deploy_overrides={"container": "bsp-artifacts"},
)
# Use the lower-level deployer + storage backend directly
from bsp.storage import create_backend
from bsp.deployer import ArtifactDeployer
from bsp.models import ArchiveConfig, DeployConfig
config = DeployConfig(
provider="azure",
container="bsp-artifacts",
prefix="{device}/{release}/{date}",
patterns=["**/*.wic.gz"],
artifact_dirs=["tmp/deploy/images"],
archive=ArchiveConfig(
name="firmware-{device}-{release}-{date}",
format="tar.gz",
),
)
backend = create_backend("azure", container_name="bsp-artifacts")
deployer = ArtifactDeployer(config, backend)
result = deployer.deploy(
build_path="build/poky-qemuarm64-scarthgap",
device="qemuarm64",
release="scarthgap",
distro="poky",
vendor="qemu",
)
print(deployer.generate_manifest(result, device="qemuarm64", release="scarthgap"))name: Build and Deploy BSP
on:
push:
branches: [main]
jobs:
build-deploy:
runs-on: ubuntu-latest
permissions:
id-token: write # required for OIDC / Workload Identity federation
contents: read
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install bsp-registry-tools with Azure support
run: pip install "bsp-registry-tools[azure]"
- name: Azure Login (OIDC)
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Build BSP
run: bsp build poky-qemuarm64-scarthgap
- name: Deploy artifacts
env:
AZURE_STORAGE_ACCOUNT_URL: ${{ secrets.AZURE_STORAGE_ACCOUNT_URL }}
run: |
bsp deploy poky-qemuarm64-scarthgap \
--container bsp-artifacts \
--prefix "ci/{device}/{release}/${{ github.sha }}"name: Build and Deploy BSP (AWS)
on:
push:
branches: [main]
jobs:
build-deploy:
runs-on: ubuntu-latest
permissions:
id-token: write # required for OIDC
contents: read
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install bsp-registry-tools with AWS support
run: pip install "bsp-registry-tools[aws]"
- name: Configure AWS Credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: eu-west-1
- name: Build BSP
run: bsp build poky-qemuarm64-scarthgap
- name: Deploy artifacts
run: |
bsp deploy poky-qemuarm64-scarthgap \
--provider aws \
--bucket my-bsp-artifacts \
--prefix "ci/{device}/{release}/${{ github.sha }}"