diff --git a/src/app/rpmostree-builtin-status.c b/src/app/rpmostree-builtin-status.c index b326f79e..c85c823c 100644 --- a/src/app/rpmostree-builtin-status.c +++ b/src/app/rpmostree-builtin-status.c @@ -144,14 +144,15 @@ lookup_array_and_canonicalize (GVariantDict *dict, return g_steal_pointer (&ret); } -static char* -gv_nevra_to_evr (GVariant *gv_nevra) +static void +gv_nevra_to_evr (GString *buffer, + GVariant *gv_nevra) { guint64 epoch; const char *version, *release; g_variant_get (gv_nevra, "(sst&s&ss)", NULL, NULL, &epoch, &version, &release, NULL); - return rpmostree_custom_nevra_strdup (NULL, epoch, version, release, NULL, - PKG_NEVRA_FLAGS_EPOCH_VERSION_RELEASE); + rpmostree_custom_nevra (buffer, NULL, epoch, version, release, NULL, + PKG_NEVRA_FLAGS_EPOCH_VERSION_RELEASE); } /* We will have an optimized path for the case where there are just @@ -486,6 +487,10 @@ status_generic (RPMOSTreeSysroot *sysroot_proxy, if (origin_base_local_replacements) { g_autoptr(GString) str = g_string_new (""); + g_autoptr(GHashTable) grouped_diffs = + g_hash_table_new_full (g_str_hash, g_str_equal, g_free, + (GDestroyNotify)g_ptr_array_unref); + const guint n = g_variant_n_children (origin_base_local_replacements); for (guint i = 0; i < n; i++) { @@ -498,25 +503,54 @@ status_generic (RPMOSTreeSysroot *sysroot_proxy, g_variant_get_child (gv_nevra_new, 1, "&s", &name_new); g_variant_get_child (gv_nevra_old, 1, "&s", &name_old); - if (str->len) - g_string_append (str, ", "); - /* if pkgnames match, print a nicer version like treediff */ if (g_str_equal (name_new, name_old)) { - g_autofree char *old_evr = gv_nevra_to_evr (gv_nevra_old); - g_autofree char *new_evr = gv_nevra_to_evr (gv_nevra_new); - g_string_append_printf (str, "%s %s -> %s", name_new, old_evr, new_evr); + /* let's just use str as a scratchpad to avoid excessive mallocs; the str + * needs to be stretched anyway for the final output */ + gsize original_size = str->len; + gv_nevra_to_evr (str, gv_nevra_old); + g_string_append (str, " -> "); + gv_nevra_to_evr (str, gv_nevra_new); + const char *diff = str->str + original_size; + GPtrArray *pkgs = g_hash_table_lookup (grouped_diffs, diff); + if (!pkgs) + { + pkgs = g_ptr_array_new_with_free_func (g_free); + g_hash_table_insert (grouped_diffs, g_strdup (diff), pkgs); + } + g_ptr_array_add (pkgs, g_strdup (name_new)); + g_string_truncate (str, original_size); } else { + if (str->len) + g_string_append (str, ", "); + const char *nevra_old; g_variant_get_child (gv_nevra_old, 0, "&s", &nevra_old); g_string_append_printf (str, "%s -> %s", nevra_old, nevra_new); } g_ptr_array_add (active_replacements, g_strdup (nevra_new)); } + + GLNX_HASH_TABLE_FOREACH_KV (grouped_diffs, const char*, diff, GPtrArray*, pkgs) + { + if (str->len) + g_string_append (str, ", "); + for (guint i = 0, n = pkgs->len; i < n; i++) + { + const char *pkgname = g_ptr_array_index (pkgs, i); + if (i > 0) + g_string_append_c (str, ' '); + g_string_append (str, pkgname); + } + g_string_append_c (str, ' '); + g_string_append (str, diff); + } + g_ptr_array_add (active_replacements, NULL); + if (str->len) print_kv ("ReplacedBasePackages", max_key_len, str->str); } diff --git a/src/libpriv/rpmostree-rpm-util.c b/src/libpriv/rpmostree-rpm-util.c index 4103b14d..33321e41 100644 --- a/src/libpriv/rpmostree-rpm-util.c +++ b/src/libpriv/rpmostree-rpm-util.c @@ -81,6 +81,40 @@ pkg_envra_strdup (Header h1) return envra; } +void +rpmostree_custom_nevra (GString *buffer, + const char *name, + uint64_t epoch, + const char *version, + const char *release, + const char *arch, + RpmOstreePkgNevraFlags flags) +{ + gsize original_len = buffer->len; + + if (flags & PKG_NEVRA_FLAGS_NAME) + g_string_append (buffer, name); + + if (flags & (PKG_NEVRA_FLAGS_EPOCH_VERSION_RELEASE | + PKG_NEVRA_FLAGS_VERSION_RELEASE)) + { + if (buffer->len > original_len) + g_string_append_c (buffer, '-'); + + if ((flags & PKG_NEVRA_FLAGS_EPOCH_VERSION_RELEASE) && (epoch > 0)) + g_string_append_printf (buffer, "%" PRIu64 ":", epoch); + + g_string_append_printf (buffer, "%s-%s", version, release); + } + + if (flags & PKG_NEVRA_FLAGS_ARCH) + { + if (buffer->len > original_len) + g_string_append_c (buffer, '.'); + g_string_append (buffer, arch); + } +} + char * rpmostree_custom_nevra_strdup (const char *name, uint64_t epoch, @@ -90,29 +124,7 @@ rpmostree_custom_nevra_strdup (const char *name, RpmOstreePkgNevraFlags flags) { GString *nevra = g_string_new (""); - - if (flags & PKG_NEVRA_FLAGS_NAME) - g_string_append (nevra, name); - - if (flags & (PKG_NEVRA_FLAGS_EPOCH_VERSION_RELEASE | - PKG_NEVRA_FLAGS_VERSION_RELEASE)) - { - if (nevra->len) - g_string_append_c (nevra, '-'); - - if ((flags & PKG_NEVRA_FLAGS_EPOCH_VERSION_RELEASE) && (epoch > 0)) - g_string_append_printf (nevra, "%" PRIu64 ":", epoch); - - g_string_append_printf (nevra, "%s-%s", version, release); - } - - if (flags & PKG_NEVRA_FLAGS_ARCH) - { - if (nevra->len) - g_string_append_c (nevra, '.'); - g_string_append (nevra, arch); - } - + rpmostree_custom_nevra (nevra, name, epoch, version, release, arch, flags); return g_string_free (nevra, FALSE); } diff --git a/src/libpriv/rpmostree-rpm-util.h b/src/libpriv/rpmostree-rpm-util.h index 7a247c4c..ed95cb29 100644 --- a/src/libpriv/rpmostree-rpm-util.h +++ b/src/libpriv/rpmostree-rpm-util.h @@ -143,6 +143,15 @@ typedef enum { PKG_NEVRA_FLAGS_ARCH = (1 << 3) } RpmOstreePkgNevraFlags; +void +rpmostree_custom_nevra (GString *buffer, + const char *name, + uint64_t epoch, + const char *version, + const char *release, + const char *arch, + RpmOstreePkgNevraFlags flags); + char * rpmostree_custom_nevra_strdup (const char *name, uint64_t epoch, diff --git a/tests/vmcheck/test-override-local-replace.sh b/tests/vmcheck/test-override-local-replace.sh index f5105acd..8831c5e6 100755 --- a/tests/vmcheck/test-override-local-replace.sh +++ b/tests/vmcheck/test-override-local-replace.sh @@ -24,7 +24,7 @@ set -e set -x -REPO=/tmp/vmcheck/yumrepo/packages/x86_64 +YUMREPO=/tmp/vmcheck/yumrepo/packages/x86_64 # create a new vmcheck commit which has foo and bar in it already @@ -83,30 +83,35 @@ assert_replaced_local_pkg() { # try to replace foo without replacing the extension vm_build_rpm foo version 2.0 -if vm_rpmostree ex override replace $REPO/foo-2.0-1.x86_64.rpm 2>err.txt; then +if vm_rpmostree ex override replace $YUMREPO/foo-2.0-1.x86_64.rpm 2>err.txt; then assert_not_reached "successfully replaced foo without fooext?" fi assert_file_has_content err.txt "fooext" echo "ok failed to replace foo without fooext" vm_build_rpm fooext version 2.0 requires "foo = 2.0-1" -vm_rpmostree ex override replace $REPO/foo{,ext}-2.0-1.x86_64.rpm +vm_rpmostree ex override replace $YUMREPO/foo{,ext}-2.0-1.x86_64.rpm vm_assert_status_jq \ '.deployments[0]["base-local-replacements"]|length == 2' \ '.deployments[0]["requested-base-local-replacements"]|length == 2' assert_replaced_local_pkg foo-1.0-1.x86_64 foo-2.0-1.x86_64 assert_replaced_local_pkg fooext-1.0-1.x86_64 fooext-2.0-1.x86_64 +vm_cmd rpm-ostree status > status.txt +assert_file_has_content status.txt '\(foo fooext\|fooext foo\) 1\.0-1 -> 2\.0-1' echo "ok override replace foo and fooext" # replace bar with older version vm_build_rpm bar version 0.9 -vm_rpmostree ex override replace $REPO/bar-0.9-1.x86_64.rpm +vm_rpmostree ex override replace $YUMREPO/bar-0.9-1.x86_64.rpm vm_assert_status_jq \ '.deployments[0]["base-local-replacements"]|length == 3' \ '.deployments[0]["requested-base-local-replacements"]|length == 3' assert_replaced_local_pkg foo-1.0-1.x86_64 foo-2.0-1.x86_64 assert_replaced_local_pkg fooext-1.0-1.x86_64 fooext-2.0-1.x86_64 assert_replaced_local_pkg bar-1.0-1.x86_64 bar-0.9-1.x86_64 +vm_cmd rpm-ostree status > status.txt +assert_file_has_content status.txt '\(foo fooext\|fooext foo\) 1\.0-1 -> 2\.0-1' +assert_file_has_content_literal status.txt 'bar 1.0-1 -> 0.9-1' echo "ok override replace bar" vm_cmd ostree commit -b vmcheck --tree=ref=vmcheck @@ -138,7 +143,7 @@ vm_rpmostree cleanup -p # test inactive replacements vm_cmd ostree commit -b vmcheck --tree=ref=vmcheck_tmp/with_foo_and_bar vm_rpmostree upgrade -vm_rpmostree ex override replace $REPO/bar-0.9-1.x86_64.rpm +vm_rpmostree ex override replace $YUMREPO/bar-0.9-1.x86_64.rpm vm_assert_status_jq \ '.deployments[0]["base-local-replacements"]|length == 1' \ '.deployments[0]["requested-base-local-replacements"]|length == 1' @@ -164,8 +169,8 @@ vm_rpmostree cleanup -p vm_build_rpm baz vm_cmd ostree commit -b vmcheck --tree=ref=vmcheck_tmp/with_foo_and_bar vm_rpmostree upgrade -vm_rpmostree ex override replace $REPO/bar-0.9-1.x86_64.rpm \ - --install $REPO/baz-1.0-1.x86_64.rpm +vm_rpmostree ex override replace $YUMREPO/bar-0.9-1.x86_64.rpm \ + --install $YUMREPO/baz-1.0-1.x86_64.rpm vm_assert_status_jq \ '.deployments[0]["base-local-replacements"]|length == 1' \ '.deployments[0]["requested-base-local-replacements"]|length == 1' \