Add positron daily channel to bakery product map#567
Conversation
Test Results1 635 tests 1 631 ✅ 6m 39s ⏱️ For more details on these failures, see this check. Results for commit 9706fbb. ♻️ This comment has been updated with latest results. |
There was a problem hiding this comment.
This creates a bit of weirdness where Positron is considered as both a dependency and a product. We'll need to make it live in one of those worlds. I would vote for keeping it a dependency and finding a way to implement dev version or prerelease behaviors with dependency matrix builds.
I would still like a --latest flag for building matrix builds to single out the latest image only. Maybe a similar concept could be implemented for --pre-release to build the an image with dependency pre-releases if defined/available?
|
Another idea, we could move it fully into the product world and make a section for |
2de6f5f to
5d99ecf
Compare
'channel' is the established term in package distribution (apt, conda, Homebrew, Chrome). ReleaseStreamEnum kept as a deprecated alias for backward compatibility during migration. PRODUCT_RELEASE_CHANNEL_SUPPORT_MAP likewise replaces PRODUCT_RELEASE_STREAM_SUPPORT_MAP, with the old name kept as a deprecated alias.
'channel' is the established term in package distribution (apt, conda, Homebrew, Chrome). The bakery.yaml 'stream' key is accepted with a deprecation warning during the migration period. - Rename stream field → channel on ImageDevelopmentVersionFromProductStream - Add model_validator to migrate 'stream' → 'channel' with a warning - Update metadata key release_stream → release_channel in base.py and the filter lookup in version.py - Update all test fixtures and test data to use channel=
Update stale ReleaseStreamEnum references left behind after the stream→channel rename landed. No behaviour change — ReleaseStreamEnum remains a deprecated alias pointing at ReleaseChannelEnum. - base.py: import and get_release_stream return type - version.py: import and matches_dev_filter parameter (dev_stream→dev_channel) - test_version.py: import, parametrize arg name, test method docstring - test_stream.py: remove unused ReleaseStreamEnum import, rename test_as_image_version_metadata_contains_release_stream, add edge-case test asserting channel wins when both stream and channel are supplied
dev_channel replaces dev_stream as the primary field. dev_stream kept as a deprecated read-only property for any existing code that reads it during the migration period.
--dev-stream is now hidden and emits a warning; --dev-channel is the primary flag. Both are accepted during the migration period.
Product repos that still pass dev-stream continue to work unchanged. The shared workflow translates both to --dev-channel for bakery. Remove dev-stream from shared workflows in Phase 0.5c after all product repos have migrated.
image_target.py:375 still read 'release_stream' from metadata after the rename to 'release_channel', causing the Stream tag context variable to always be empty for dev versions. Update both the source and the corresponding test.
Two tests added to main after branching used {"release_stream": ...}
metadata and ReleaseStreamEnum directly; both needed to use
{"release_channel": ...} and ReleaseChannelEnum after Phase 0.5a.
Also fix image_target.py release_stream property to read
"release_channel" from metadata — ceb2bd8 updated the template
context string but missed the property that drives UID generation.
Rename release_stream property → release_channel, update import from ReleaseStreamEnum → ReleaseChannelEnum, rename local variable, update docstrings and error messages. Update callers in config.py and affected tests.
BakerySettings(dev_stream=...) previously silently dropped the value since dev_stream is a property, not a field. A model_validator(before) now migrates it to dev_channel with a DeprecationWarning. When both dev_stream and dev_channel are passed, dev_channel wins and dev_stream is discarded. Two new tests cover both paths.
- Rename stream.py → channel.py; test_stream.py → test_channel.py - Rename get_release_stream() → get_release_channel() in base.py and channel.py - Rename ReleaseStreamResult → ReleaseChannelResult in main.py - Rename ReleaseStreamPath → ReleaseChannelPath in main.py - Rename get_product_artifact_by_stream → get_product_artifact_by_channel - Rename product_release_stream_url_map → product_release_channel_url_map - Rename stream_url → channel_url in ReleaseChannelPath - Rename stream parameter → channel in get_product_artifact_by_channel - Update all imports, patch paths, and call sites across test files - Fix test names, local variables, and comments in test_image_target.py and test_version.py
Finishes the rename left incomplete in the initial commits:
- Rename ImageDevelopmentVersionFromProductStream →
ImageDevelopmentVersionFromProductChannel
- Rename {{ Stream }} tag template variable → {{ Channel }}
- Rename ReleaseStreamEnum map keys → ReleaseChannelEnum in main.py
- Update field description "product stream" → "product channel"
- Rename test classes, methods, and variables accordingly
e754ad3 to
56b82eb
Compare
5af4cc6 to
8626fc7
Compare
- tag.py: kwargs.get("Stream") → kwargs.get("Channel") in the
render() guard. Since tag_template_values now uses "Channel" as the
key, the old check always returned None, silently skipping every
channel-based tag pattern and producing no channel tags.
- test_image.py: test_load_dev_versions was using the deprecated
"stream" key; updated to "channel" since this isn't a compat test.
- test_image_target.py: stale "Stream" in a comment → "Channel".
When set by a dispatch spec, pinned_version bypasses CDN discovery and is forwarded to the stream resolver as version_override. For template streams (PPM) this renders the URL offline; for manifest streams (Connect, Workbench) this asserts manifest version matches. DispatchVersionMismatchError is re-raised explicitly to escape the per-OS drop logic in _resolve_os_urls.
The dispatched version is pinned onto the matching stream dev version before load_dev_versions() runs, bypassing CDN discovery. Raises ValueError on ambiguity (multiple matching dev versions with no channel to disambiguate). - Add DevBuildSpec field to BakerySettings - Add _apply_dev_spec() module-level helper - Call _apply_dev_spec() per image when dev_spec is set, before load_dev_versions()
Accepts a JSON payload via flag or BAKERY_DEV_SPEC env var. Inert when absent; only active on workflow dispatch.
When both dev_spec.channel and --dev-channel are set and differ, the pinned version would be silently excluded by the dev-channel filter. Raise a clear ValueError instead. Also remove unused json import in build.py.
Tests for both `bakery build` and `bakery ci matrix`, covering flag and env var delivery, JSON parse errors, schema validation (extra fields forbidden), and absence (dev_spec is None). Follows the CliRunner + BakeryConfig.from_context mock pattern from test_clean.py. A shared helper handles both positional (build) and keyword (ci matrix) call_args extraction.
Product repos pass dev-version + dev-channel; the workflow assembles a JSON dev-spec via jq and passes --dev-spec to bakery ci matrix and bakery build. If dev-spec is passed directly it takes precedence. Logic is inert when dev-version is empty (scheduled and PR runs). Both bakery-build-native.yml and bakery-build.yml receive the new dev-version and dev-spec inputs, and all steps that call bakery commands (matrix, build, push) are updated.
In both bakery-build.yml and bakery-build-native.yml matrix steps, --dev-channel was appended unconditionally whenever DEV_CHANNEL was set. When DEV_SPEC also carried a channel field, the channel-conflict guard in _apply_dev_spec raised a ValueError, failing the matrix step. Restructure the ARGS/spec assembly as an if/elif/elif chain: pass --dev-spec when a spec is available (explicit or constructed from DEV_VERSION), or --dev-channel only when no spec is involved. The channel is already embedded in the constructed spec, so the separate --dev-channel flag is redundant and conflicting in that path.
Completes the terminology migration started in Phase 0.5a: - Rename get_release_stream() → get_release_channel() in base.py and channel.py - Rename ReleaseStreamResult → ReleaseChannelResult in main.py - Rename ReleaseStreamPath → ReleaseChannelPath in main.py - Rename get_product_artifact_by_stream → get_product_artifact_by_channel in main.py - Rename product_release_stream_url_map → product_release_channel_url_map in main.py - Rename stream_url → channel_url attribute in ReleaseChannelPath - Rename stream parameter → channel in get_product_artifact_by_channel - Rename stream.py → channel.py and test_stream.py → test_channel.py - Update all imports, patch paths, and call sites across test files Backward-compat shims (--dev-stream CLI flag, sourceType: "stream" YAML discriminator, and deprecation warning strings) are intentionally unchanged.
- Remove unused generalize_arch parameter from get_product_artifact_by_channel; callers control generalization via result.architecture_generalized_download_url - Remove unused ReleaseStreamEnum import from channel.py - Update docstrings in main.py, channel.py, base.py, and config.py to use "channel" terminology consistently after the stream→channel rename
_apply_dev_spec uses "BakerySettings" as a forward-reference string annotation, but BakerySettings is defined at line 298 in the same file — fully resolved before _apply_dev_spec at line 374. There is no circular import and no from __future__ import annotations in use, so the quotes serve no purpose. String annotations are only required when the referenced name is not yet defined at annotation evaluation time; that condition does not hold here.
The mode="before" validator checked "dev_channel" not in data to
decide whether to migrate dev_stream. Pydantic v2 includes explicitly
passed None values in the raw data dict, so
BakerySettings(dev_stream=DAILY, dev_channel=None) fell into the
elif branch and silently dropped dev_stream, leaving dev_channel=None.
Change the condition to data.get("dev_channel") is None so that an
explicit None is treated as unset. dev_channel still wins when it
carries a real value. Add a regression test for the explicit-None case.
- Move DispatchVersionMismatchError to posit_product/errors.py; update imports in main.py and channel.py - Fix inconsistent .value formatting in version filter error message
Replace manual model_validate_json + bare Exception catch in each command with a _parse_dev_spec callback on the option. The callback raises typer.BadParameter on pydantic.ValidationError so the error message is attributed correctly in --help output.
Removes the copy-pasted _parse_dev_spec callback from build.py and ci.py and promotes it to cli/common.py as parse_dev_spec. - Moves pydantic import to file level (was unjustified local import) - Converts Optional[str] to str | None (modern union syntax) - Splits model_validate_json into json.loads + model_validate so JSON parse errors produce "not valid JSON: <msg>" rather than a verbose pydantic ValidationError dump - Adds an explanatory comment on the # type: ignore[arg-type]: typer requires str | None so Click maps it to STRING; the callback delivers DevBuildSpec at runtime
Aligns the field name with the version_override= kwarg already used when forwarding it to get_product_artifact_by_channel, and with the general pattern of "override" for externally-supplied values layered onto an existing object. Also fixes two stale ImageDevelopmentVersionFromProductStream references in test_channel.py and config.py that survived the stream→channel rename because they were in newly-added code rather than edited lines.
ci merge, ci readme, clean cache-registry, clean temp-registry, get tags, and run dgoss all received the deprecated --dev-stream alias and coalesce block in the dev-build-dispatch branch, but only bakery build and ci matrix had CLI coverage for that behavior. Each command gets two tests: --dev-stream alone coalesces to dev_channel, and --dev-channel takes precedence when both are given.
Both test_dev_spec.py and test_dev_stream_deprecated.py duplicated the same from_context call-args extraction logic. Extract it to test/cli/conftest.py as settings_from_call so there is one place to update if the calling convention changes.
Add ProductEnum.POSITRON and support for DAILY release
channel. The POSITRON_DAILY_CDN_URL_TEMPLATE includes a
{positron_cdn_arch} placeholder that will be formatted at
fetch time by Task 3.
Add POSITRON to product_release_channel_url_map with a DAILY channel
backed by POSITRON_DAILY_CDN_URL_TEMPLATE. The channel URL contains a
{positron_cdn_arch} placeholder resolved via format_map before the
HTTP request, so the URL is arch-aware.
- Add positron_cdn_arch and positron_pkg_arch keys to
_make_resolver_metadata (amd64 → x86_64/x64)
- Apply format_map(metadata) to channel_url before fetching so
{positron_cdn_arch} resolves; no-op for static URLs used by
existing products
- Add POSITRON_DAILY_CDN_URL_TEMPLATE import
- Add 4 parametrized test cases for the new metadata keys
Created positron_daily_x86_64.json and positron_daily_arm64.json testdata files and added URL pattern matching in the patch_testdata_response fixture to map requests to the POSITRON_DAILY CDN endpoints to the local testdata files for unit tests.
The x86_64 identifier is used in some contexts (e.g., RHEL 9) and should map to the same CDN paths and package architecture as amd64, not trigger a fallback to x86_64 in the package arch mapping. Added test coverage for rocky, alma, and rhel with the x86_64 identifier to prevent regression.
Verify that pinned version_override bypasses the network call to CachedSession by asserting the mock is never called. This prevents future regressions where the offline path could be broken but the test would still pass due to CDN returning matching data by coincidence.
Positron was tracked as both a dependency (via SupportedDependencies.POSITRON in the matrix) and a product (via ProductEnum.POSITRON for daily preview builds), creating a dual-world conflict raised in issue #472. This replaces the product-side Positron code entirely with a generic ImageDevelopmentVersionFromDependencyPrerelease type (sourceType: dependency-prerelease) that resolves its version through the existing dependency constraint infrastructure with prerelease=True. The Containerfile template handles download URL construction from Image.Version and values passed via the devVersions values: field. - Add POSITRON_DAILY_URL_TEMPLATE to dependencies/const.py - Add prerelease: bool and daily_url() to PositronDependency; update _fetch_versions() to append the daily build when prerelease=True - Add ImageDevelopmentVersionFromDependencyPrerelease (sourceType: dependency-prerelease) in dev_version/dependency.py; register in DevelopmentVersionTypes union - Remove ProductEnum.POSITRON, POSITRON_DAILY_CDN_URL_TEMPLATE, and all product-side Positron resolver code from posit_product/ - Move positron daily testdata from posit_products/ to dependencies/ - Update conftest mock routing from product const to dependency const
Rename ImageDevelopmentVersionFromDependencyPrerelease to ImageDevelopmentVersionFromDependency and change sourceType from "dependency-prerelease" to "dependency". Add a prerelease: bool = False field to the class, passed through to the constraint constructor, replacing the hardcoded True. This makes prerelease intent explicit in bakery.yaml (on the devVersions entry) rather than implied by the sourceType name. The constraint class retains its prerelease field as an implementation detail; the devVersions prerelease field is the single user-facing point of control for prerelease resolution.
f6f027d to
cea46e4
Compare
8626fc7 to
9706fbb
Compare
cea46e4 to
ca1be12
Compare
Adds
POSITRONtoProductEnumand wires its daily CDN (cdn.posit.co/positron/dailies/pwb/{arch}/releases.json) intoproduct_release_channel_url_map, enablingworkbench-positron-initimages to be built from the Positron daily channel.The
ReleaseChannelPath.get()call now formatschannel_urlwith resolver metadata before fetching, which is how the arch-parameterized positron URL template gets resolved. The arch mapping handles bothamd64(Debian/Ubuntu) andx86_64(RHEL) identifiers.Stacked on: