diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index da15643d..f212cc71 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -74,14 +74,14 @@ jobs: run: make -j $(getconf _NPROCESSORS_ONLN) check-meson: - name: Ubuntu 22.04 meson build - runs-on: ubuntu-22.04 + name: Ubuntu meson build + runs-on: ${{ matrix.os }} strategy: matrix: + os: ['ubuntu-22.04', 'ubuntu-24.04'] compiler: ['gcc', 'clang'] env: - UBUNTU_VERSION: '22.04' CC: ${{ matrix.compiler }} BASE_CFLAGS: -Wp,-D_FORTIFY_SOURCE=2 BUILDDIR: builddir diff --git a/doc/flatpak-builder.xml b/doc/flatpak-builder.xml index 847bb57e..6cc19e2b 100644 --- a/doc/flatpak-builder.xml +++ b/doc/flatpak-builder.xml @@ -589,6 +589,21 @@ + + + + + Set the AppStream compose URL policy. Accepted values + are partial and full. + full requires AppStream version >= 0.16.3. + Defaults to partial if unspecified. + This policy only takes effect when used in conjunction + with ; + otherwise the Appstream catalogue will preserve + the source media URLs. + + + diff --git a/src/builder-context.c b/src/builder-context.c index 508ab91f..4458a229 100644 --- a/src/builder-context.c +++ b/src/builder-context.c @@ -88,6 +88,8 @@ struct BuilderContext char *opt_mirror_screenshots_url; BuilderSdkConfig *sdk_config; + + BuilderAsUrlPolicy as_url_policy; }; typedef struct @@ -1241,6 +1243,19 @@ builder_context_create_state_dir (BuilderContext *self, return TRUE; } +void +builder_context_set_as_url_policy (BuilderContext *self, + BuilderAsUrlPolicy policy) +{ + self->as_url_policy = policy; +} + +BuilderAsUrlPolicy +builder_context_get_as_url_policy (BuilderContext *self) +{ + return self->as_url_policy; +} + BuilderContext * builder_context_new (GFile *run_dir, GFile *app_dir, diff --git a/src/builder-context.h b/src/builder-context.h index 69671398..f6c12329 100644 --- a/src/builder-context.h +++ b/src/builder-context.h @@ -29,6 +29,11 @@ G_BEGIN_DECLS +typedef enum { + BUILDER_AS_URL_POLICY_PARTIAL = 0, + BUILDER_AS_URL_POLICY_FULL, +} BuilderAsUrlPolicy; + /* Same as SOUP_HTTP_URI_FLAGS, means all possible flags for http uris */ #if GLIB_CHECK_VERSION (2, 68, 0) @@ -185,6 +190,10 @@ BuilderSdkConfig * builder_context_get_sdk_config (BuilderContext *self); gboolean builder_context_create_state_dir (BuilderContext *self, GError **error); +void builder_context_set_as_url_policy (BuilderContext *self, + BuilderAsUrlPolicy policy); +BuilderAsUrlPolicy builder_context_get_as_url_policy (BuilderContext *self); + G_DEFINE_AUTOPTR_CLEANUP_FUNC (BuilderContext, g_object_unref) G_END_DECLS diff --git a/src/builder-main.c b/src/builder-main.c index aa6d0e4d..dfb61d66 100644 --- a/src/builder-main.c +++ b/src/builder-main.c @@ -89,6 +89,7 @@ static gboolean opt_log_session_bus; static gboolean opt_log_system_bus; static gboolean opt_yes; static gint64 opt_source_date_epoch = -1; +static gchar *opt_as_url_policy = NULL; static GOptionEntry entries[] = { { "verbose", 'v', 0, G_OPTION_ARG_NONE, &opt_verbose, "Print debug information during command processing", NULL }, @@ -144,6 +145,7 @@ static GOptionEntry entries[] = { { "assumeyes", 'y', 0, G_OPTION_ARG_NONE, &opt_yes, N_("Automatically answer yes for all questions"), NULL }, { "no-shallow-clone", 0, 0, G_OPTION_ARG_NONE, &opt_no_shallow_clone, "Don't use shallow clones when mirroring git repos", NULL }, { "override-source-date-epoch", 0, 0, G_OPTION_ARG_INT64, &opt_source_date_epoch, "Use this timestamp to perform the build, instead of the last modification time of the manifest.", NULL }, + { "compose-url-policy", 0, 0, G_OPTION_ARG_STRING, &opt_as_url_policy, "Set the AppStream compose URL policy to either 'partial' (default) or 'full'", "POLICY" }, { NULL } }; @@ -608,6 +610,29 @@ main (int argc, builder_context_set_opt_export_only (build_context, opt_export_only); builder_context_set_opt_mirror_screenshots_url (build_context, opt_mirror_screenshots_url); + if (opt_mirror_screenshots_url) + { + BuilderAsUrlPolicy policy = BUILDER_AS_URL_POLICY_PARTIAL; + + if (g_strcmp0 (opt_as_url_policy, "full") == 0) + policy = BUILDER_AS_URL_POLICY_FULL; + else if (g_strcmp0 (opt_as_url_policy, "partial") == 0) + policy = BUILDER_AS_URL_POLICY_PARTIAL; + else if (opt_as_url_policy != NULL) + { + g_printerr ("Invalid value for --compose-url-policy: %s\n", opt_as_url_policy); + return 1; + } + + if (policy == BUILDER_AS_URL_POLICY_FULL && !appstream_has_version (0, 16, 3)) + { + g_printerr ("AppStream version >= 0.16.3 required for 'full' compose URL policy\n"); + return 1; + } + + builder_context_set_as_url_policy (build_context, policy); + } + git_init_email (); if (!builder_context_create_state_dir (build_context, &error)) diff --git a/src/builder-manifest.c b/src/builder-manifest.c index 50596368..0fc94ebe 100644 --- a/src/builder-manifest.c +++ b/src/builder-manifest.c @@ -2422,7 +2422,8 @@ cmpstringp (const void *p1, const void *p2) } static gboolean -appstreamcli_compose (GError **error, +appstreamcli_compose (GError **error, + BuilderAsUrlPolicy as_url_policy, ...) { g_autoptr(GPtrArray) args = NULL; @@ -2433,7 +2434,10 @@ appstreamcli_compose (GError **error, g_ptr_array_add (args, g_strdup ("appstreamcli")); g_ptr_array_add (args, g_strdup ("compose")); - va_start (ap, error); + if (as_url_policy == BUILDER_AS_URL_POLICY_FULL) + g_ptr_array_add (args, g_strdup ("--no-partial-urls")); + + va_start (ap, as_url_policy); while ((arg = va_arg (ap, const gchar *))) g_ptr_array_add (args, g_strdup (arg)); g_ptr_array_add (args, NULL); @@ -3066,6 +3070,7 @@ builder_manifest_cleanup (BuilderManifest *self, flatpak_file_get_path_cached (icon_out)); const char *opt_mirror_screenshots_url = builder_context_get_opt_mirror_screenshots_url (context); gboolean opt_export_only = builder_context_get_opt_export_only (context); + BuilderAsUrlPolicy as_url_policy = builder_context_get_as_url_policy (context); if (opt_mirror_screenshots_url && !opt_export_only) { @@ -3077,6 +3082,7 @@ builder_manifest_cleanup (BuilderManifest *self, g_print ("Running appstreamcli compose\n"); g_print ("Saving screenshots in %s\n", flatpak_file_get_path_cached (media_dir)); if (!appstreamcli_compose (error, + as_url_policy, "--prefix=/", origin, arg_base_url, @@ -3093,6 +3099,7 @@ builder_manifest_cleanup (BuilderManifest *self, { g_print ("Running appstreamcli compose\n"); if (!appstreamcli_compose (error, + as_url_policy, "--prefix=/", origin, result_root_arg, diff --git a/src/builder-utils.c b/src/builder-utils.c index 060fdeed..7a8b0083 100644 --- a/src/builder-utils.c +++ b/src/builder-utils.c @@ -1837,3 +1837,52 @@ flatpak_version_check (int major, return FALSE; } + +gboolean +appstream_has_version (int major, + int minor, + int micro) +{ + static int as_major = 0; + static int as_minor = 0; + static int as_micro = 0; + + if (as_major == 0 && + as_minor == 0 && + as_micro == 0) + { + const char * argv[] = { "appstreamcli", "--version", NULL }; + g_autoptr(GSubprocess) subp = NULL; + g_autofree char *out = NULL; + g_auto(GStrv) lines = NULL; + + subp = g_subprocess_newv (argv, G_SUBPROCESS_FLAGS_STDOUT_PIPE, NULL); + g_subprocess_communicate_utf8 (subp, NULL, NULL, &out, NULL, NULL); + + lines = g_strsplit (out, "\n", -1); + + for (size_t i = 0; lines[i] != NULL; i++) + { + /* Only prefer library version over cli version in case of mismatch */ + if (g_str_has_prefix (lines[i], "AppStream library version:")) + { + if (sscanf (lines[i], "AppStream library version: %d.%d.%d", &as_major, &as_minor, &as_micro) == 3) + break; + } + else if (g_str_has_prefix (lines[i], "AppStream version:")) + { + if (sscanf (lines[i], "AppStream version: %d.%d.%d", &as_major, &as_minor, &as_micro) == 3) + break; + } + } + + if (as_major == 0 && as_minor == 0 && as_micro == 0) + g_warning ("Failed to find appstream version"); + else + g_debug ("Found AppStream version %d.%d.%d", as_major, as_minor, as_micro); + } + + return (as_major > major) || + (as_major == major && as_minor > minor) || + (as_major == major && as_minor == minor && as_micro >= micro); +} diff --git a/src/builder-utils.h b/src/builder-utils.h index 05dfd839..ddbc5a00 100644 --- a/src/builder-utils.h +++ b/src/builder-utils.h @@ -216,6 +216,10 @@ gboolean flatpak_version_check (int major, int minor, int micro); +gboolean appstream_has_version (int major, + int minor, + int micro); + G_DEFINE_AUTOPTR_CLEANUP_FUNC (FlatpakXml, flatpak_xml_free); G_END_DECLS diff --git a/tests/Makefile.am.inc b/tests/Makefile.am.inc index ff10308d..a16c74f3 100644 --- a/tests/Makefile.am.inc +++ b/tests/Makefile.am.inc @@ -61,6 +61,8 @@ dist_installed_test_data = \ tests/org.flatpak_builder.gui.desktop \ tests/org.flatpak_builder.gui.json \ tests/org.flatpak_builder.gui.metainfo.xml \ + tests/org.flatpak.appstream_media.json \ + tests/org.test.Hello-256.png \ $(NULL) installed_test_keyringdir = $(installed_testdir)/test-keyring diff --git a/tests/libtest.sh b/tests/libtest.sh index 3536e922..75c1f215 100644 --- a/tests/libtest.sh +++ b/tests/libtest.sh @@ -309,6 +309,47 @@ skip_without_python2 () { fi } +appstream_has_version () { + req_major=$1 + req_minor=$2 + req_micro=$3 + + maj=0; min=0; mic=0 + + out=$(appstreamcli --version 2>/dev/null) || return 1 + + while IFS= read -r line; do + case "$line" in + "AppStream library version:"* ) + ver=$(echo "$line" | awk '{print $4}') + ;; + "AppStream version:"* ) + ver=$(echo "$line" | awk '{print $3}') + ;; + * ) continue ;; + esac + + maj=$(echo "$ver" | cut -d. -f1) + min=$(echo "$ver" | cut -d. -f2) + mic=$(echo "$ver" | cut -d. -f3) + break + done <&2 || true if test -n "${TEST_SKIP_CLEANUP:-}"; then diff --git a/tests/meson.build b/tests/meson.build index 46c84fa9..373ed557 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -108,6 +108,8 @@ if get_option('installed_tests') 'org.flatpak_builder.gui.desktop', 'org.flatpak_builder.gui.json', 'org.flatpak_builder.gui.metainfo.xml', + 'org.flatpak.appstream_media.json', + 'org.test.Hello-256.png', install_dir: installed_testdir, install_mode: 'rw-r--r--', diff --git a/tests/org.flatpak.appstream_media.json b/tests/org.flatpak.appstream_media.json new file mode 100644 index 00000000..c9adf661 --- /dev/null +++ b/tests/org.flatpak.appstream_media.json @@ -0,0 +1,44 @@ +{ + "id": "org.flatpak.appstream_media", + "runtime": "org.test.Platform", + "sdk": "org.test.Sdk", + "rename-desktop-file": "org.flatpak_builder.gui.desktop", + "rename-appdata-file": "org.flatpak_builder.gui.metainfo.xml", + "rename-icon": "org.test.Hello-256", + "command": "hello", + "modules": [ + { + "name": "appstream_media", + "buildsystem": "simple", + "build-commands": [ + "mkdir -p ${FLATPAK_DEST}/bin ${FLATPAK_DEST}/share/metainfo ${FLATPAK_DEST}/share/applications", + "mkdir -p ${FLATPAK_DEST}/share/icons/hicolor/256x256/apps", + "cp -vf hello.sh ${FLATPAK_DEST}/bin/hello", + "cp -vf org.flatpak_builder.gui.metainfo.xml ${FLATPAK_DEST}/share/metainfo", + "cp -vf org.flatpak_builder.gui.desktop ${FLATPAK_DEST}/share/applications", + "cp -vf org.test.Hello-256.png ${FLATPAK_DEST}/share/icons/hicolor/256x256/apps" + ], + "sources": [ + { + "type": "script", + "dest-filename": "hello.sh", + "commands": [ + "echo \"Hello world, from a sandbox\"" + ] + }, + { + "type": "file", + "path": "org.flatpak_builder.gui.desktop" + }, + { + "type": "file", + "path": "org.flatpak_builder.gui.metainfo.xml" + }, + { + "type": "file", + "path": "org.test.Hello-256.png" + } + ] + } + ] +} diff --git a/tests/org.test.Hello-256.png b/tests/org.test.Hello-256.png new file mode 100644 index 00000000..68473c5a Binary files /dev/null and b/tests/org.test.Hello-256.png differ diff --git a/tests/test-builder.sh b/tests/test-builder.sh index 8b22ac8a..0b29ef1f 100755 --- a/tests/test-builder.sh +++ b/tests/test-builder.sh @@ -23,7 +23,7 @@ set -euo pipefail skip_without_fuse -echo "1..8" +echo "1..10" setup_repo install_repo @@ -53,6 +53,8 @@ cp $(dirname $0)/org.flatpak_builder.gui.desktop . cp $(dirname $0)/org.flatpak_builder.gui.json . cp $(dirname $0)/org.flatpak_builder.gui.metainfo.xml . cp $(dirname $0)/org.test.Hello.png . +cp $(dirname $0)/org.test.Hello-256.png . +cp $(dirname $0)/org.flatpak.appstream_media.json . mkdir include1 cp $(dirname $0)/module1.json include1/ cp $(dirname $0)/module1.yaml include1/ @@ -144,3 +146,30 @@ ostree checkout --repo=$REPO/repo_sc -U screenshots/$(flatpak --default-arch) ou find outdir_sc -path "*/icons/64x64/org.test.Hello.png" -type f | grep -q . echo "ok screenshot ref commit" + +# test compose partial url policy +${FLATPAK_BUILDER} --force-clean builddir_sc \ + --mirror-screenshots-url=https://example.org/media \ + --state-dir .fp-compose-url-policy-partial \ + --compose-url-policy=partial \ + org.flatpak.appstream_media.json >&2 +# we test for the icon tag instead of screenshot +# the former works offline the latter does not +gzip -cdq builddir_sc/files/share/app-info/xmls/org.flatpak.appstream_media.xml.gz|grep -Eq '>org/flatpak/appstream_media/[^/]+/icons/128x128/org.flatpak.appstream_media.png' + +echo "ok compose partial url policy" + +# test compose full url policy +if appstream_has_version 0 16 3; then + ${FLATPAK_BUILDER} --force-clean builddir_sc \ + --mirror-screenshots-url=https://example.org/media \ + --state-dir .fp-compose-url-policy-full \ + --compose-url-policy=full \ + org.flatpak.appstream_media.json >&2 + + gzip -cdq builddir_sc/files/share/app-info/xmls/org.flatpak.appstream_media.xml.gz|grep -Eq '>https://example.org/media/org/flatpak/appstream_media/[^/]+/icons/128x128/org.flatpak.appstream_media.png' + + echo "ok compose full url policy" +else + echo "ok # Skip AppStream < 0.16.3" +fi