From 46b6177f5e072aada3acdaf9ecaba072676667d1 Mon Sep 17 00:00:00 2001
From: Jorge Polanco <55784702+Jorge-Polanco-Roque@users.noreply.github.com>
Date: Wed, 16 Sep 2026 08:14:37 -0600
Subject: [PATCH 1/5] feat(switch): add --target-imgref to decouple pull source
from upgrade origin
When an image is side-loaded (e.g. scp + podman load into containers-storage
after losing registry connectivity) and applied with
`bootc switch --transport containers-storage `, bootc would then keep
fetching upgrades from containers-storage.
Add `--target-imgref` (and `--target-transport`, mirroring `bootc install`)
so the image can be pulled from a local source now while a different imgref is
persisted as the origin for future upgrades. Reuses the existing target_imgref
mechanism that deploy::pull already accepted and switch passed as None.
Closes: #2464
Assisted-by: Claude (AI)
Co-Authored-By: Claude Opus 4.8 (1M context)
Signed-off-by: Jorge Polanco <55784702+Jorge-Polanco-Roque@users.noreply.github.com>
---
crates/lib/src/bootc_composefs/switch.rs | 4 +
crates/lib/src/cli.rs | 122 +++++++++++++++++++++--
2 files changed, 116 insertions(+), 10 deletions(-)
diff --git a/crates/lib/src/bootc_composefs/switch.rs b/crates/lib/src/bootc_composefs/switch.rs
index 0cb0b9ebea..dfe0fe269d 100644
--- a/crates/lib/src/bootc_composefs/switch.rs
+++ b/crates/lib/src/bootc_composefs/switch.rs
@@ -40,6 +40,10 @@ pub(crate) async fn switch_composefs(
return apply_upgrade_from_downloaded(storage, booted_cfs, &host, &do_upgrade_opts).await;
}
+ if opts.target_imgref.is_some() {
+ anyhow::bail!("--target-imgref is not yet supported with the composefs backend");
+ }
+
let target = imgref_for_switch(&opts)?;
let new_spec = {
diff --git a/crates/lib/src/cli.rs b/crates/lib/src/cli.rs
index 4a8ccea630..563da14796 100644
--- a/crates/lib/src/cli.rs
+++ b/crates/lib/src/cli.rs
@@ -165,6 +165,22 @@ pub(crate) struct SwitchOpts {
#[clap(long, default_value = "registry")]
pub(crate) transport: String,
+ /// Specify the image to record as the origin for subsequent updates.
+ ///
+ /// By default the image is fetched from `--transport`/`` and that same
+ /// reference is also persisted as the origin used for future `bootc upgrade`s.
+ /// Use this to decouple the two: the image is still fetched now from
+ /// `--transport`/`` (e.g. a local `containers-storage` copy loaded via
+ /// `podman load`), but the origin recorded for subsequent updates is this
+ /// reference instead (e.g. the normal registry pull spec).
+ #[clap(long, conflicts_with = "from_downloaded")]
+ pub(crate) target_imgref: Option,
+
+ /// The transport for `--target-imgref`; e.g. registry, oci, oci-archive,
+ /// docker-daemon, containers-storage. Defaults to `registry`.
+ #[clap(long, default_value = "registry")]
+ pub(crate) target_transport: String,
+
#[clap(flatten)]
pub(crate) download_opts: DownloadOnlyOpts,
@@ -1495,6 +1511,27 @@ pub(crate) fn imgref_for_switch(opts: &SwitchOpts) -> Result {
return Ok(target);
}
+/// Build the optional `--target-imgref` for `switch`; this is the reference that will
+/// be recorded as the origin for subsequent updates, decoupled from the source the
+/// image is fetched from now. Returns `None` when `--target-imgref` was not provided.
+pub(crate) fn target_imgref_for_switch(
+ opts: &SwitchOpts,
+) -> Result> {
+ let Some(name) = opts.target_imgref.as_deref() else {
+ return Ok(None);
+ };
+ let transport = ostree_container::Transport::try_from(opts.target_transport.as_str())?;
+ let imgref = ostree_container::ImageReference {
+ transport,
+ name: name.to_string(),
+ };
+ let sigverify = sigpolicy_from_opt(opts.enforce_container_sigpolicy);
+ Ok(Some(ostree_container::OstreeImageReference {
+ sigverify,
+ imgref,
+ }))
+}
+
/// Implementation of the `bootc switch` CLI command for ostree backend.
#[context("Switching (ostree)")]
async fn switch_ostree(
@@ -1517,7 +1554,16 @@ async fn switch_ostree(
.await;
}
- let target = imgref_for_switch(&opts)?;
+ // The source we fetch the image from right now.
+ let source = imgref_for_switch(&opts)?;
+ // Optional decoupled reference to persist as the origin for subsequent updates.
+ let target_imgref = target_imgref_for_switch(&opts)?;
+ // What we record in the spec/origin for future upgrades: the `--target-imgref`
+ // if given, otherwise the same reference we're fetching from.
+ let origin_ref = match target_imgref.as_ref() {
+ Some(t) => ImageReference::from(t.clone()),
+ None => source.clone(),
+ };
let prog: ProgressWriter = opts.progress.try_into()?;
let cancellable = gio::Cancellable::NONE;
@@ -1525,7 +1571,7 @@ async fn switch_ostree(
let new_spec = {
let mut new_spec = host.spec.clone();
- new_spec.image = Some(target.clone());
+ new_spec.image = Some(origin_ref.clone());
new_spec
};
@@ -1549,11 +1595,13 @@ async fn switch_ostree(
tracing::info!(
message_id = SWITCH_JOURNAL_ID,
bootc.old_image_reference = old_image,
- bootc.new_image_reference = &target.image,
- bootc.new_image_transport = &target.transport,
+ bootc.new_image_reference = &origin_ref.image,
+ bootc.new_image_transport = &origin_ref.transport,
+ bootc.source_image_reference = &source.image,
+ bootc.source_image_transport = &source.transport,
"Switching from image {} to {}",
old_image,
- target.image
+ origin_ref.image
);
let new_spec = RequiredHostSpec::from_spec(&new_spec)?;
@@ -1564,14 +1612,14 @@ async fn switch_ostree(
let use_unified = if opts.unified_storage_exp {
true
} else {
- crate::deploy::image_exists_in_unified_storage(storage, &target).await?
+ crate::deploy::image_exists_in_unified_storage(storage, &source).await?
};
let fetched = if use_unified {
crate::deploy::pull_unified(
repo,
- &target,
- None,
+ &source,
+ target_imgref.as_ref(),
opts.quiet,
prog.clone(),
storage,
@@ -1581,8 +1629,8 @@ async fn switch_ostree(
} else {
crate::deploy::pull(
repo,
- &target,
- None,
+ &source,
+ target_imgref.as_ref(),
opts.quiet,
prog.clone(),
Some(&booted_ostree.deployment),
@@ -2722,6 +2770,60 @@ mod tests {
));
}
+ #[test]
+ fn test_parse_switch_target_imgref() {
+ // Without --target-imgref, source and origin are the same reference.
+ let o = Opt::try_parse_from([
+ "bootc",
+ "switch",
+ "--transport",
+ "containers-storage",
+ "localhost/someimage",
+ ])
+ .unwrap();
+ let opts = match o {
+ Opt::Switch(opts) => opts,
+ o => panic!("Expected switch opts, not {o:?}"),
+ };
+ assert_eq!(opts.transport, "containers-storage");
+ assert!(opts.target_imgref.is_none());
+ assert!(target_imgref_for_switch(&opts).unwrap().is_none());
+ let source = imgref_for_switch(&opts).unwrap();
+ assert_eq!(source.transport, "containers-storage");
+ assert_eq!(source.image, "localhost/someimage");
+
+ // With --target-imgref, the image is fetched from --transport/ but the
+ // origin recorded for upgrades is the (registry, by default) target imgref.
+ let o = Opt::try_parse_from([
+ "bootc",
+ "switch",
+ "--transport",
+ "containers-storage",
+ "--target-imgref",
+ "quay.io/example/os:latest",
+ "localhost/someimage",
+ ])
+ .unwrap();
+ let opts = match o {
+ Opt::Switch(opts) => opts,
+ o => panic!("Expected switch opts, not {o:?}"),
+ };
+ assert_eq!(
+ opts.target_imgref.as_deref(),
+ Some("quay.io/example/os:latest")
+ );
+ assert_eq!(opts.target_transport, "registry");
+ let source = imgref_for_switch(&opts).unwrap();
+ assert_eq!(source.transport, "containers-storage");
+ assert_eq!(source.image, "localhost/someimage");
+ let target = target_imgref_for_switch(&opts).unwrap().unwrap();
+ assert_eq!(
+ target.imgref.transport,
+ ostree_container::Transport::Registry
+ );
+ assert_eq!(target.imgref.name, "quay.io/example/os:latest");
+ }
+
#[test]
fn test_parse_selinux_is_unlabeled() {
let opt = Opt::try_parse_from([
From a898af4cdfa7d52f99587419b617cf546d9dfa1a Mon Sep 17 00:00:00 2001
From: Jorge Polanco <55784702+Jorge-Polanco-Roque@users.noreply.github.com>
Date: Wed, 16 Sep 2026 13:48:55 -0600
Subject: [PATCH 2/5] docs: regenerate bootc-switch man page for
--target-imgref
Ran `cargo xtask update-generated` after adding the --target-imgref and
--target-transport options so the generated man page matches the CLI and
the validate check passes.
Assisted-by: Claude (AI)
Co-Authored-By: Claude Opus 4.8 (1M context)
Signed-off-by: Jorge Polanco <55784702+Jorge-Polanco-Roque@users.noreply.github.com>
---
docs/src/man/bootc-switch.8.md | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/docs/src/man/bootc-switch.8.md b/docs/src/man/bootc-switch.8.md
index 407aa0bc09..2e5a8618ee 100644
--- a/docs/src/man/bootc-switch.8.md
+++ b/docs/src/man/bootc-switch.8.md
@@ -63,6 +63,16 @@ Soft reboot allows faster system restart by avoiding full hardware reboot when p
Default: registry
+**--target-imgref**=*TARGET_IMGREF*
+
+ Specify the image to record as the origin for subsequent updates
+
+**--target-transport**=*TARGET_TRANSPORT*
+
+ The transport for `--target-imgref`; e.g. registry, oci, oci-archive, docker-daemon, containers-storage. Defaults to `registry`
+
+ Default: registry
+
**--download-only**
Download and stage the update without applying it
From bff448d5011cbc41ccc9945d1f120f13fb62c51e Mon Sep 17 00:00:00 2001
From: Jorge Polanco <55784702+Jorge-Polanco-Roque@users.noreply.github.com>
Date: Wed, 16 Sep 2026 18:20:05 -0600
Subject: [PATCH 3/5] fix(switch): respect --target-imgref against
mutate-in-place and pull-source changes
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Address review: reject --target-imgref with the no-pull --mutate-in-place
mode (conflicts_with), and don't short-circuit switch as a no-op when only
the origin is unchanged but the pull source differs — that is exactly the
containers-storage recovery case from #2464.
Assisted-by: Claude (AI)
Co-Authored-By: Claude Opus 4.8 (1M context)
Signed-off-by: Jorge Polanco <55784702+Jorge-Polanco-Roque@users.noreply.github.com>
---
crates/lib/src/cli.rs | 22 ++++++++++++++++++++--
1 file changed, 20 insertions(+), 2 deletions(-)
diff --git a/crates/lib/src/cli.rs b/crates/lib/src/cli.rs
index 563da14796..a38cd859f9 100644
--- a/crates/lib/src/cli.rs
+++ b/crates/lib/src/cli.rs
@@ -173,7 +173,7 @@ pub(crate) struct SwitchOpts {
/// `--transport`/`` (e.g. a local `containers-storage` copy loaded via
/// `podman load`), but the origin recorded for subsequent updates is this
/// reference instead (e.g. the normal registry pull spec).
- #[clap(long, conflicts_with = "from_downloaded")]
+ #[clap(long, conflicts_with_all = ["from_downloaded", "mutate_in_place"])]
pub(crate) target_imgref: Option,
/// The transport for `--target-imgref`; e.g. registry, oci, oci-archive,
@@ -1575,7 +1575,11 @@ async fn switch_ostree(
new_spec
};
- if new_spec == host.spec {
+ // Only take the unchanged fast path when the pull source is also the origin.
+ // With `--target-imgref` the source is decoupled from the persisted origin, so a
+ // switch from a different source keeping the same origin (issue #2464) must still
+ // run the pull even though `new_spec == host.spec`.
+ if target_imgref.is_none() && new_spec == host.spec {
println!("Image specification is unchanged.");
if opts.apply && host.status.staged.is_some() {
crate::reboot::reboot()?;
@@ -2822,6 +2826,20 @@ mod tests {
ostree_container::Transport::Registry
);
assert_eq!(target.imgref.name, "quay.io/example/os:latest");
+
+ // --target-imgref performs a real pull; --mutate-in-place performs none, so the
+ // combination is rejected rather than silently ignoring --target-imgref.
+ assert!(
+ Opt::try_parse_from([
+ "bootc",
+ "switch",
+ "--mutate-in-place",
+ "--target-imgref",
+ "quay.io/example/os:latest",
+ "localhost/someimage",
+ ])
+ .is_err()
+ );
}
#[test]
From 22d8079a78d7845c6d19157d6e28ce61005db63a Mon Sep 17 00:00:00 2001
From: Jorge Polanco <55784702+Jorge-Polanco-Roque@users.noreply.github.com>
Date: Thu, 17 Sep 2026 16:54:27 -0600
Subject: [PATCH 4/5] feat(switch): support --target-imgref on the composefs
backend
The composefs backend previously bailed with "not yet supported" for
`switch --target-imgref`. Thread the decoupled origin through do_upgrade
so the image is fetched from the source (e.g. a local containers-storage
copy) while the reference persisted as the origin for future upgrades is
the `--target-imgref` value, matching the ostree backend (issue #2464).
- DoUpgradeOpts gains `origin_override`; write_composefs_state records it
as ORIGIN_CONTAINER, falling back to the pull source when absent.
- The unchanged fast path is skipped when `--target-imgref` is set, so a
switch from a different source keeping the same origin still pulls.
- The switch journal entry now records both source and target images.
Adds tmt/tests/booted/test-switch-target-imgref.nu covering composefs.
Generated-by: AI
I'm familiar with this area and reviewed the change; build/tests run in CI.
Signed-off-by: Jorge Polanco <55784702+Jorge-Polanco-Roque@users.noreply.github.com>
---
crates/lib/src/bootc_composefs/switch.rs | 37 +++++++++++-----
crates/lib/src/bootc_composefs/update.rs | 13 +++++-
tmt/plans/integration.fmf | 8 ++++
tmt/tests/booted/test-switch-target-imgref.nu | 42 +++++++++++++++++++
tmt/tests/tests.fmf | 5 +++
5 files changed, 93 insertions(+), 12 deletions(-)
create mode 100644 tmt/tests/booted/test-switch-target-imgref.nu
diff --git a/crates/lib/src/bootc_composefs/switch.rs b/crates/lib/src/bootc_composefs/switch.rs
index dfe0fe269d..042065192b 100644
--- a/crates/lib/src/bootc_composefs/switch.rs
+++ b/crates/lib/src/bootc_composefs/switch.rs
@@ -34,25 +34,31 @@ pub(crate) async fn switch_composefs(
use_unified: false,
quiet: opts.quiet,
prog,
+ origin_override: None,
};
if opts.download_opts.from_downloaded {
return apply_upgrade_from_downloaded(storage, booted_cfs, &host, &do_upgrade_opts).await;
}
- if opts.target_imgref.is_some() {
- anyhow::bail!("--target-imgref is not yet supported with the composefs backend");
- }
-
- let target = imgref_for_switch(&opts)?;
+ // The source we fetch the image from now.
+ let source = imgref_for_switch(&opts)?;
+ // Optional decoupled reference to persist as the origin for future upgrades
+ // (`--target-imgref`, issue #2464). `None` means source and origin coincide.
+ let origin_override =
+ crate::cli::target_imgref_for_switch(&opts)?.map(crate::spec::ImageReference::from);
let new_spec = {
let mut new_spec = host.spec.clone();
- new_spec.image = Some(target.clone());
+ new_spec.image = Some(origin_override.clone().unwrap_or_else(|| source.clone()));
new_spec
};
- if new_spec == host.spec {
+ // Only take the unchanged fast path when the pull source is also the origin.
+ // With `--target-imgref` the source is decoupled from the persisted origin, so a
+ // switch from a different source keeping the same origin (issue #2464) must still
+ // run the pull even though `new_spec == host.spec`.
+ if origin_override.is_none() && new_spec == host.spec {
println!("Image specification is unchanged.");
if opts.apply && host.status.staged.is_some() {
crate::reboot::reboot()?;
@@ -60,16 +66,25 @@ pub(crate) async fn switch_composefs(
return Ok(());
}
- let Some(target_imgref) = new_spec.image else {
- anyhow::bail!("Target image is undefined")
- };
+ // Persist the decoupled origin (if any); everything below pulls and validates
+ // against the source image.
+ do_upgrade_opts.origin_override = origin_override;
+ let target_imgref = source;
const COMPOSEFS_SWITCH_JOURNAL_ID: &str = "7a6b5c4d3e2f1a0b9c8d7e6f5a4b3c2d1";
+ // With `--target-imgref` the persisted origin is decoupled from the pull
+ // source, so record both (mirroring the ostree path's journal fields).
+ let origin_image = do_upgrade_opts
+ .origin_override
+ .as_ref()
+ .unwrap_or(&target_imgref);
+
tracing::info!(
message_id = COMPOSEFS_SWITCH_JOURNAL_ID,
bootc.operation = "switch",
- bootc.target_image = target_imgref.to_string(),
+ bootc.source_image = target_imgref.to_string(),
+ bootc.target_image = origin_image.to_string(),
bootc.apply_mode = opts.apply,
bootc.download_only = opts.download_opts.download_only,
bootc.from_downloaded = opts.download_opts.from_downloaded,
diff --git a/crates/lib/src/bootc_composefs/update.rs b/crates/lib/src/bootc_composefs/update.rs
index c2b43c8b86..bf3813845e 100644
--- a/crates/lib/src/bootc_composefs/update.rs
+++ b/crates/lib/src/bootc_composefs/update.rs
@@ -220,6 +220,10 @@ pub(crate) struct DoUpgradeOpts {
pub(crate) quiet: bool,
/// Structured (JSON-Lines) progress sink; see `--progress-fd`.
pub(crate) prog: ProgressWriter,
+ /// Origin to persist for future upgrades when it is decoupled from the pull
+ /// source (i.e. `switch --target-imgref`, issue #2464). `None` means the pull
+ /// source is also the origin, which is the default for plain upgrades.
+ pub(crate) origin_override: Option,
}
async fn apply_upgrade(
@@ -364,10 +368,15 @@ pub(crate) async fn do_upgrade(
finalization_locked: opts.download_only,
};
+ // The image is fetched from `imgref`, but the origin persisted for future
+ // upgrades may be decoupled from that pull source via `switch --target-imgref`
+ // (issue #2464); fall back to the source when they coincide.
+ let origin = opts.origin_override.as_ref().unwrap_or(imgref);
+
write_composefs_state(
&Utf8PathBuf::from("/sysroot"),
&id,
- imgref,
+ origin,
Some(staged_state),
boot_type,
boot_digest,
@@ -490,6 +499,8 @@ pub(crate) async fn upgrade_composefs(
use_unified: false,
quiet: opts.quiet,
prog,
+ // Plain upgrade never decouples the origin from the pull source.
+ origin_override: None,
};
if opts.download_opts.from_downloaded {
diff --git a/tmt/plans/integration.fmf b/tmt/plans/integration.fmf
index 7b52f4f02a..b5cdd7acfe 100644
--- a/tmt/plans/integration.fmf
+++ b/tmt/plans/integration.fmf
@@ -318,4 +318,12 @@ execute:
test:
- /tmt/tests/tests/test-48-composefs-uki-dumpfile
extra-skip_if_ostree: true
+
+/plan-49-switch-target-imgref:
+ summary: switch --target-imgref records a decoupled origin (composefs)
+ discover:
+ how: fmf
+ test:
+ - /tmt/tests/tests/test-49-switch-target-imgref
+ extra-skip_if_ostree: true
# END GENERATED PLANS
diff --git a/tmt/tests/booted/test-switch-target-imgref.nu b/tmt/tests/booted/test-switch-target-imgref.nu
new file mode 100644
index 0000000000..1a403493a5
--- /dev/null
+++ b/tmt/tests/booted/test-switch-target-imgref.nu
@@ -0,0 +1,42 @@
+# number: 49
+# tmt:
+# summary: switch --target-imgref records a decoupled origin (composefs)
+# duration: 30m
+# extra:
+# skip_if_ostree: true
+#
+# Verify that `bootc switch --target-imgref` fetches the image from the given
+# source (here a local containers-storage copy) but persists the *decoupled*
+# reference as the origin for subsequent upgrades (issue #2464), on the
+# composefs backend.
+use std assert
+use tap.nu
+
+tap begin "bootc switch --target-imgref decouples pull source from origin"
+
+# Make the booted image available in podman storage as our pull-source base.
+bootc image copy-to-storage
+
+# Derive a new image so its content — and therefore its composefs fs-verity
+# digest — differs from the booted deployment; otherwise the same-digest guard
+# (see test-43) would refuse the switch. This derived image is our pull source.
+("FROM localhost/bootc\n"
+ + "RUN touch /usr/share/testing-target-imgref\n") | podman build -t localhost/bootc-source -f - .
+
+# The reference we want recorded as the origin for future upgrades. It is never
+# pulled (so it needs no network / need not exist): switch fetches from the
+# local source above and only *records* this as the origin.
+let origin = "quay.io/example/os:latest"
+
+# Fetch from the local containers-storage copy, but decouple the persisted origin.
+bootc switch --transport containers-storage --target-imgref $origin localhost/bootc-source
+
+# The staged deployment's origin must be the --target-imgref value, not the
+# containers-storage source we actually pulled from.
+let st = bootc status --json | from json
+assert ($st.status.staged != null) "Expected a staged deployment after switch"
+let staged = $st.status.staged.image
+assert equal $staged.image.transport "registry"
+assert equal $staged.image.image $origin
+
+tap ok
diff --git a/tmt/tests/tests.fmf b/tmt/tests/tests.fmf
index c2c91422d9..c622c03680 100644
--- a/tmt/tests/tests.fmf
+++ b/tmt/tests/tests.fmf
@@ -197,3 +197,8 @@ check:
summary: Test composefs UKI dumpfile diff print
duration: 30m
test: nu booted/test-composefs-uki-dumpfile.nu
+
+/test-49-switch-target-imgref:
+ summary: switch --target-imgref records a decoupled origin (composefs)
+ duration: 30m
+ test: nu booted/test-switch-target-imgref.nu
From 8a7941b96c6670de6856cd88cd7c04704add9c8d Mon Sep 17 00:00:00 2001
From: Jorge Polanco <55784702+Jorge-Polanco-Roque@users.noreply.github.com>
Date: Fri, 18 Sep 2026 05:57:43 -0600
Subject: [PATCH 5/5] test(switch): skip --target-imgref test on UKI
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The test switches to a freshly derived image, whose composefs digest
doesn't match the one sealed into the booted UKI, so the switch is
rejected with "wrong composefs= parameter" before the origin is ever
recorded — the same limitation as test-composefs-gc. The decoupling
logic is boot-type agnostic and stays covered on the BLS composefs matrix.
Generated-by: AI
Signed-off-by: Jorge Polanco <55784702+Jorge-Polanco-Roque@users.noreply.github.com>
---
tmt/plans/integration.fmf | 1 +
tmt/tests/booted/test-switch-target-imgref.nu | 7 +++++++
2 files changed, 8 insertions(+)
diff --git a/tmt/plans/integration.fmf b/tmt/plans/integration.fmf
index b5cdd7acfe..7dbc7f6fce 100644
--- a/tmt/plans/integration.fmf
+++ b/tmt/plans/integration.fmf
@@ -326,4 +326,5 @@ execute:
test:
- /tmt/tests/tests/test-49-switch-target-imgref
extra-skip_if_ostree: true
+ extra-fixme_skip_if_uki: true
# END GENERATED PLANS
diff --git a/tmt/tests/booted/test-switch-target-imgref.nu b/tmt/tests/booted/test-switch-target-imgref.nu
index 1a403493a5..0433feb481 100644
--- a/tmt/tests/booted/test-switch-target-imgref.nu
+++ b/tmt/tests/booted/test-switch-target-imgref.nu
@@ -4,11 +4,18 @@
# duration: 30m
# extra:
# skip_if_ostree: true
+# fixme_skip_if_uki: true
#
# Verify that `bootc switch --target-imgref` fetches the image from the given
# source (here a local containers-storage copy) but persists the *decoupled*
# reference as the origin for subsequent upgrades (issue #2464), on the
# composefs backend.
+#
+# Skipped on UKI: the test switches to a freshly derived image, whose composefs
+# digest won't match the one sealed into the booted UKI, so the switch is
+# rejected before the origin is ever recorded (same limitation as
+# test-composefs-gc). The decoupling logic is boot-type agnostic and is covered
+# on the BLS composefs matrix.
use std assert
use tap.nu