Skip to content

feeds: enable an add-on feed by name in distro.feeds - #323

Merged
jetm merged 3 commits into
mainfrom
jschneck/distro-addon-feeds
Oct 21, 2026
Merged

jetm merged 3 commits into
mainfrom
jschneck/distro-addon-feeds

Conversation

@mobileoverlord

@mobileoverlord mobileoverlord commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

What

A project can enable an add-on feed the distro publishes, such as ROS 2 Jazzy, by name:

distro:
  release: 2026
  channel: next
  feeds: [ros2-jazzy]

An add-on is a set of packages built alongside the base feed and published next to it, never inside it, and it never ships a package name the base feed has. The project names it; the CLI knows where it lives.

How

  • Resolution (ResolvedFeedSet::resolve): a distro.feeds name that repos: does not define is an add-on. It expands to two feeds off the distro feed's base URL and releasever:

    • <name> → <url>/<release>/<channel>/target/<target>-<name>
    • <name>-sdk → <url>/<release>/<channel>/sdk/<target>-<name>

    Each half goes only where it belongs, matching the distro feed's own baked repos:

    • the target half to target dnf: rootfs, extensions and the SDK's target sysroot;
    • the SDK half to the SDK host.

    This is ResolvedFeed::placement, which is recorded in the canonical document only when set, so no other feed's document or hash changes. The SDK half exists even while an add-on ships no SDK packages, so host tools can arrive later without a config change. Both halves use the distro feed's base URL and releasever, so they follow a snapshot pin, and its TLS settings (ca, tls_verify, AVOCADO_REPO_CA, AVOCADO_REPO_INSECURE).

  • Ordering is unchanged. An add-on takes its position in distro.feeds as its dnf priority, like any other feed, and the distro feed stays first unless listed. A full example:

    distro:
      feeds: [acme-private, avocado, ros2-jazzy, vendor-tools]
    repos:
      acme-private: { org: 01a071ea, channel: main }
      vendor-tools: { url: "https://rpm.vendor.example/avocado/$releasever/$target" }

    This resolves to acme-private (10), the distro repos (20), ros2-jazzy (30), ros2-jazzy-sdk (40) and vendor-tools (50).

  • A typo fails before dnf runs (ResolvedFeedSet::check_addons). The check runs once per target, before the canonical document and the lock are written, so a refused add-on never reaches the lock. The CLI reads the channel's targets.json (machine → repo paths, already published per channel), even under a snapshot pin. An add-on is a target/<target>-<name> entry, other than the built-in -ext, whose sdk/<target>-<name> is listed too. Under a pin, the CLI also checks that the pinned snapshot has both halves. A requested add-on the target does not publish is refused with:

    distro.feeds: 'ros2-jazy' is not an add-on feed published for raspberrypi5; did you mean 'ros2-jazzy'?
      published for raspberrypi5: ros2-jazzy
    

    It also lists the other targets that publish the name. When targets.json cannot be read, the check warns and dnf reports whatever is really missing.

  • Mistakes are refused rather than read as add-ons:

    • a name close to one repos: defines gets a did-you-mean for repos:;
    • <name>-sdk, ext and <name>-ext are not add-on names;
    • a repos: entry named <name>-sdk next to an add-on <name> is refused.
  • Schema description and CHANGELOG updated.

The layer side, which builds an add-on as a multiconfig and publishes it, is avocado-linux/meta-avocado#529.

Testing

  • Unit tests:
    • expansion: both halves, URLs, order;
    • placement per stage, and the stage projection hash agreeing with it;
    • TLS inheritance;
    • pinned and channel index URLs;
    • name refusals (repos: typos, -sdk, ext, -ext, the -sdk clash);
    • the add-on check: did-you-mean, -ext excluded, SDK half required, unpublished target, other targets listed.
  • validation_errors now asserts the remaining refusal, an add-on name that cannot be a repo id or path segment, since an undefined name is no longer an error.
  • cargo fmt --check, cargo clippy --all-targets -- -D warnings, cargo test: all pass.
  • End to end on a Raspberry Pi 5 through a Rubicon, against a local feed with a ros2-jazzy add-on:
    • feeds: [ros2-jazy] failed with the message above before any dnf run;
    • feeds: [ros2-jazzy]: dnf read ros2-jazzy and ros2-jazzy-sdk, and the lock pinned the ROS packages to ros2-jazzy and the base packages to the distro repo;
    • the hub replayed sdk install with this build;
    • the provisioned board ran the ROS talker/listener demo.

@mobileoverlord
mobileoverlord requested review from jetm and a balanced review from Copilot October 21, 2026 19:38

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Add-on validation mishandles snapshot URLs, ignores configured CA trust, and can bypass an earlier rejection.

3 open issues
What changed in this PR

Enables projects to select distro-published add-on feeds by name without defining repository URLs.

Changes:

  • Expands add-on names into target and SDK repositories in priority order.
  • Checks availability before materialization and suggests corrections.
  • Adds unit tests and updates configuration documentation.
File Description
src/​utils/​feeds.rs Adds expansion, validation, and unit tests.
src/​utils/​config.rs Checks add-ons before materializing feeds.
schemas/​avocado-config.json Documents add-on names and examples.
CHANGELOG.md Describes the new feed support.

Review effort: Balanced

Comment thread src/utils/feeds.rs Outdated
Comment thread src/utils/feeds.rs Outdated
Comment thread src/utils/feeds.rs Outdated

@jetm jetm left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-checked at a26ca45. My earlier comment on add-on repos ignoring repo.insecure is addressed: the add-on repos now take tls_verify and ca from the same getters as the distro feed, and a new test asserts both halves resolve to tls_verify == false. The three open Copilot threads are covered too: the targets.json client now honours the configured CA and the insecure setting, the pinned-snapshot check no longer depends on the order of snapshot pinning, and a rejected add-on is re-checked on the next call. No new blocking findings. I did not run the tests. No inline comments on the new commits; a few minor notes were left off.

Comment thread src/utils/feeds.rs
A name in distro.feeds that repos: does not define is now an add-on the
distro feed publishes beside its own repos. It expands to two feeds, the
target half at target/<target>-<name> and the SDK half (<name>-sdk) at
sdk/<target>-<name>, both off the distro feed's base URL and releasever and
both enabled at every stage, so an add-on that later ships SDK tools or
cross-compile -dev packages needs no config change.

Before the first dnf run the CLI reads the channel's targets.json and
refuses an add-on the target does not list, with the nearest published name,
what the target does publish, and which other targets carry it. When the
index cannot be read the check warns and dnf reports what is missing.
…re the lock

From review of the add-on feed change:
- An add-on's target half is written only where target dnf reads it (rootfs,
  extensions, the SDK's target sysroot) and its SDK half only for the SDK host
  (ResolvedFeed::placement, recorded in the canonical document only when set,
  so no other feed's document or hash moves).
- Both halves carry the distro feed's CA and tls_verify, so a private-CA or
  insecure distro feed reaches its add-ons too.
- The check reads the channel's targets.json, which exists under a snapshot
  pin, where the releasever's path does not. It requires both halves in the
  index, uses the distro feed's TLS client, and runs once per target before the
  canonical document and the lock are written, so a refused add-on never
  reaches the lock.
- A name close to one repos: defines is refused as a typo with a did-you-mean
  for repos:. `<name>-sdk`, `ext` and `<name>-ext` are refused as add-on names.
The channel's targets.json lists an add-on only while the channel's current
snapshot has it, so checking a snapshot-pinned project against it could refuse
an add-on the pinned snapshot does have, or pass one it does not. Under a pin,
probe the snapshot's target and sdk repodata for each add-on, and use the index
only to explain a miss (did-you-mean, where else it is published).
@mobileoverlord
mobileoverlord force-pushed the jschneck/distro-addon-feeds branch from f25ee88 to a26ca45 Compare October 21, 2026 21:06
@mobileoverlord
mobileoverlord requested a review from jetm October 21, 2026 21:35
@jetm
jetm merged commit cfaa128 into main Oct 21, 2026
11 checks passed
@jetm
jetm deleted the jschneck/distro-addon-feeds branch October 21, 2026 22:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants