Problem
bakery ci matrix --image-version <X> can return an empty matrix when the product dispatcher and bakery disagree about the current version of a stream-style dev version.
Chain:
- Product CI builds
<X> (e.g. 2026.05.0-dev+130-gSHA).
- Product CI uploads artifacts to the CDN (asynchronous).
- Product CI dispatches
development.yml in the images repo with version=<X>.
- images-* workflow runs
bakery ci matrix --image-version <X>.
- Bakery's stream dev version resolution calls
get_product_artifact_by_stream(...) (posit-bakery/posit_bakery/config/image/dev_version/stream.py:54), which hits the CDN. While step 2 propagates, that call returns the previous version.
- Matrix
ImageVersion.name ≠ <X> → --image-version filter doesn't match → empty matrix → workflow fails.
Two observed failure modes:
- Dispatched version has a build number + commit SHA, bakery's rendered version has a different one.
- Dispatched version is just
YYYY.MM — matches too broadly or nothing at all.
Proposed fixes
1. Override semantics on the existing --image-version flag
When --dev-versions=only and --image-version <X> are both set, treat <X> as the authoritative version for stream-style dev versions: skip the CDN version fetch and use <X> as the ImageVersion.name. The matrix is then guaranteed to contain <X> because it was provided, not discovered.
--dev-versions |
--image-version |
Behavior |
exclude (default) |
any / unset |
unchanged — filter release versions only |
include |
any / unset |
unchanged — fetch dev versions, filter normally |
only |
unset |
unchanged — fetch dev versions from CDN |
only |
set to a full version |
override: skip CDN fetch, use --image-version as the dev version's name |
only |
set to a prefix (e.g. 2026.05) |
error — override mode requires a full version |
Why ride on the existing flag rather than add --dev-version: the two flags already co-occur in every dev-build workflow (bakery-build-native.yml, bakery-build.yml). The combination uniquely signals "this run is for a specific dev version," so the CDN fetch is redundant when both are present. No new CLI surface, no workflow changes.
Ambiguity guard
If override is set and the materialization finds more than one stream dev version in scope (e.g. an image declares both daily and preview dev versions and --dev-stream wasn't specified), error:
"--image-version with --dev-versions=only is ambiguous: image <name> has dev versions for streams [daily, preview]. Add --dev-stream to disambiguate."
URL resolution unchanged
Per-OS URL lookup still happens via _resolve_os_urls() → get_product_artifact_by_stream(...). If the CDN doesn't yet have an artifact for <X> on a given OS, the existing log-and-skip behavior applies: that OS is excluded from the build with a clear warning. This is failure-loud rather than failure-silent — much better than "the version doesn't exist anywhere in bakery."
Files touched
| File |
Change |
posit-bakery/posit_bakery/config/image/dev_version/stream.py |
add version_override field, branch in get_version() |
posit-bakery/posit_bakery/config/config.py |
add dev_version_override on BakerySettings; thread into config materialization with the ambiguity guard |
posit-bakery/posit_bakery/cli/ci.py, cli/build.py, cli/get.py |
derive override from (dev_versions == ONLY) and image_version and validate it's a full version |
posit-bakery/test/... |
unit tests for stream override + CLI plumbing |
2. Dispatchers stop hitting upstream for the version
rstudio/package-manager/.github/workflows/ci.yml: drop --from-api from scripts/release/version.py in the dispatch path. On a tag push, ${{ github.ref_name }} (minus the leading v) is the version. For pushes to main, read full_version from the same local artifact publish produced.
posit-dev/connect/.github/workflows/ci.yml: already correct — reads primary-version from packaging/build/latest-packages.json.
3. Optional --image-version-strict
Less urgent once the override semantics in (1) ship — the matrix is then guaranteed to contain the dispatched version, so silent-empty-matrix can't happen for dev builds. Still useful as a defense for release/prod paths where prefix matching can over-match.
References
posit-bakery/posit_bakery/config/image/dev_version/stream.py:43-94
posit-bakery/posit_bakery/config/config.py:252 (version_matches)
posit-bakery/posit_bakery/cli/ci.py:128
- Dispatchers:
rstudio/package-manager/.github/workflows/ci.yml:1225-1265, posit-dev/connect/.github/workflows/ci.yml:2720-2755, posit-dev/connect/tools/release/publish_release.py:274-301
Problem
bakery ci matrix --image-version <X>can return an empty matrix when the product dispatcher and bakery disagree about the current version of a stream-style dev version.Chain:
<X>(e.g.2026.05.0-dev+130-gSHA).development.ymlin the images repo withversion=<X>.bakery ci matrix --image-version <X>.get_product_artifact_by_stream(...)(posit-bakery/posit_bakery/config/image/dev_version/stream.py:54), which hits the CDN. While step 2 propagates, that call returns the previous version.ImageVersion.name≠<X>→--image-versionfilter doesn't match → empty matrix → workflow fails.Two observed failure modes:
YYYY.MM— matches too broadly or nothing at all.Proposed fixes
1. Override semantics on the existing
--image-versionflagWhen
--dev-versions=onlyand--image-version <X>are both set, treat<X>as the authoritative version for stream-style dev versions: skip the CDN version fetch and use<X>as theImageVersion.name. The matrix is then guaranteed to contain<X>because it was provided, not discovered.--dev-versions--image-versionexclude(default)includeonlyonly--image-versionas the dev version'snameonly2026.05)Why ride on the existing flag rather than add
--dev-version: the two flags already co-occur in every dev-build workflow (bakery-build-native.yml,bakery-build.yml). The combination uniquely signals "this run is for a specific dev version," so the CDN fetch is redundant when both are present. No new CLI surface, no workflow changes.Ambiguity guard
If override is set and the materialization finds more than one stream dev version in scope (e.g. an image declares both
dailyandpreviewdev versions and--dev-streamwasn't specified), error:URL resolution unchanged
Per-OS URL lookup still happens via
_resolve_os_urls()→get_product_artifact_by_stream(...). If the CDN doesn't yet have an artifact for<X>on a given OS, the existing log-and-skip behavior applies: that OS is excluded from the build with a clear warning. This is failure-loud rather than failure-silent — much better than "the version doesn't exist anywhere in bakery."Files touched
posit-bakery/posit_bakery/config/image/dev_version/stream.pyversion_overridefield, branch inget_version()posit-bakery/posit_bakery/config/config.pydev_version_overrideonBakerySettings; thread into config materialization with the ambiguity guardposit-bakery/posit_bakery/cli/ci.py,cli/build.py,cli/get.pyoverridefrom(dev_versions == ONLY) and image_versionand validate it's a full versionposit-bakery/test/...2. Dispatchers stop hitting upstream for the version
rstudio/package-manager/.github/workflows/ci.yml: drop--from-apifromscripts/release/version.pyin the dispatch path. On a tag push,${{ github.ref_name }}(minus the leadingv) is the version. For pushes to main, readfull_versionfrom the same local artifactpublishproduced.posit-dev/connect/.github/workflows/ci.yml: already correct — readsprimary-versionfrompackaging/build/latest-packages.json.3. Optional
--image-version-strictLess urgent once the override semantics in (1) ship — the matrix is then guaranteed to contain the dispatched version, so silent-empty-matrix can't happen for dev builds. Still useful as a defense for release/prod paths where prefix matching can over-match.
References
posit-bakery/posit_bakery/config/image/dev_version/stream.py:43-94posit-bakery/posit_bakery/config/config.py:252(version_matches)posit-bakery/posit_bakery/cli/ci.py:128rstudio/package-manager/.github/workflows/ci.yml:1225-1265,posit-dev/connect/.github/workflows/ci.yml:2720-2755,posit-dev/connect/tools/release/publish_release.py:274-301