@@ -1128,6 +1128,8 @@ deploy_transaction_execute (RpmostreedTransaction *transaction, GCancellable *ca
11281128 if (upgrader == NULL )
11291129 return FALSE ;
11301130
1131+ rpmostree_sysroot_upgrader_set_allow_empty_transaction (upgrader, idempotent_layering);
1132+
11311133 g_autoptr (RpmOstreeOrigin) origin = rpmostree_sysroot_upgrader_dup_origin (upgrader);
11321134
11331135 /* Handle local repo remotes immediately; the idea is that the remote is "transient"
@@ -1191,6 +1193,7 @@ deploy_transaction_execute (RpmostreedTransaction *transaction, GCancellable *ca
11911193 }
11921194
11931195 gboolean changed = FALSE ;
1196+ gboolean remove_changed = FALSE ;
11941197 if (no_initramfs
11951198 && (rpmostree_origin_get_regenerate_initramfs (origin)
11961199 || rpmostree_origin_has_initramfs_etc_files (origin)))
@@ -1214,7 +1217,10 @@ deploy_transaction_execute (RpmostreedTransaction *transaction, GCancellable *ca
12141217 if (no_layering)
12151218 {
12161219 if (rpmostree_origin_remove_all_packages (origin))
1217- changed = TRUE ;
1220+ {
1221+ changed = TRUE ;
1222+ remove_changed = TRUE ;
1223+ }
12181224 }
12191225 else
12201226 {
@@ -1223,43 +1229,60 @@ deploy_transaction_execute (RpmostreedTransaction *transaction, GCancellable *ca
12231229 * create a new deployment; see https://github.com/projectatomic/rpm-ostree/issues/753 */
12241230 if (!rpmostree_origin_remove_packages (origin,
12251231 util::rust_stringvec_from_strv (uninstall_pkgs),
1226- idempotent_layering, &changed , error))
1232+ idempotent_layering, &remove_changed , error))
12271233 return FALSE ;
1234+ if (remove_changed)
1235+ changed = TRUE ;
12281236 }
12291237
12301238 /* lazily loaded cache that's used in a few conditional blocks */
12311239 g_autoptr (RpmOstreeRefSack) base_rsack = NULL ;
12321240
1241+ /* Check if we should skip the base-db check:
1242+ * When booted from a container image on an ostree host,
1243+ * we skip the base commit rpmdb check because container images don't have
1244+ * meaningful base-db separation like traditional ostree commits do. */
1245+ gboolean skip_base_check = FALSE ;
1246+ CXX_TRY_VAR (host_type, rpmostreecxx::get_system_host_type (), error);
1247+ if (host_type == rpmostreecxx::SystemHostType::OstreeHost)
1248+ {
1249+ const auto refspec = rpmostree_origin_get_refspec (origin);
1250+ skip_base_check = (refspec.kind == rpmostreecxx::RefspecType::Container);
1251+ }
1252+
12331253 if (install_pkgs)
12341254 {
12351255 /* we run a special check here; let's just not allow trying to install a pkg that will
12361256 * right away become inactive because it's already installed */
12371257
1238- if (!base_rsack )
1258+ if (!skip_base_check )
12391259 {
1240- const char *base = rpmostree_sysroot_upgrader_get_base (upgrader);
1241- base_rsack = rpmostree_get_refsack_for_commit (repo, base, cancellable, error);
1242- if (base_rsack == NULL )
1243- return FALSE ;
1244- }
1260+ if (!base_rsack)
1261+ {
1262+ const char *base = rpmostree_sysroot_upgrader_get_base (upgrader);
1263+ base_rsack = rpmostree_get_refsack_for_commit (repo, base, cancellable, error);
1264+ if (base_rsack == NULL )
1265+ return FALSE ;
1266+ }
12451267
1246- for (char **it = install_pkgs; it && *it; it++)
1247- {
1248- const char *pkg = *it;
1249- g_autoptr (GPtrArray) pkgs = rpmostree_get_matching_packages (base_rsack->sack , pkg);
1250- if (pkgs->len > 0 && !allow_inactive)
1268+ for (char **it = install_pkgs; it && *it; it++)
12511269 {
1252- g_autoptr (GString) pkgnames = g_string_new (" " );
1253- for (guint i = 0 ; i < pkgs->len ; i++)
1270+ const char *pkg = *it;
1271+ g_autoptr (GPtrArray) pkgs = rpmostree_get_matching_packages (base_rsack->sack , pkg);
1272+ if (pkgs->len > 0 && !allow_inactive)
12541273 {
1255- auto p = static_cast <DnfPackage *> (pkgs->pdata [i]);
1256- g_string_append_printf (pkgnames, " %s" , dnf_package_get_nevra (p));
1274+ g_autoptr (GString) pkgnames = g_string_new (" " );
1275+ for (guint i = 0 ; i < pkgs->len ; i++)
1276+ {
1277+ auto p = static_cast <DnfPackage *> (pkgs->pdata [i]);
1278+ g_string_append_printf (pkgnames, " %s" , dnf_package_get_nevra (p));
1279+ }
1280+ return glnx_throw (error,
1281+ " \" %s\" is already provided by:%s. Use "
1282+ " --allow-inactive to explicitly "
1283+ " require it." ,
1284+ pkg, pkgnames->str );
12571285 }
1258- return glnx_throw (error,
1259- " \" %s\" is already provided by:%s. Use "
1260- " --allow-inactive to explicitly "
1261- " require it." ,
1262- pkg, pkgnames->str );
12631286 }
12641287 }
12651288
@@ -1565,6 +1588,15 @@ deploy_transaction_execute (RpmostreedTransaction *transaction, GCancellable *ca
15651588 return FALSE ;
15661589 changed = changed || layering_changed;
15671590
1591+ /* When using containers and nothing changed after prep_layering, return early.
1592+ * This happens when all requested packages are already present in the container image.
1593+ * For idempotent mode, this avoids the "No packages in transaction" error.
1594+ * However, if packages were removed from the origin, we need to deploy to update the origin
1595+ * even if no layering is needed. Similarly, if we're rebasing (refspec) or deploying a
1596+ * specific revision, we need to deploy. */
1597+ if (skip_base_check && !layering_changed && !remove_changed && !self->refspec && !self->revision )
1598+ return TRUE ;
1599+
15681600 if (dry_run)
15691601 /* Note early return here; we printed the transaction already */
15701602 return TRUE ;
0 commit comments