From 8c57517a013a36e07bbb8e6650dd8e72a9ddbd92 Mon Sep 17 00:00:00 2001 From: Gagandeep Bhatia Date: Mon, 23 Sep 2024 15:52:24 +0200 Subject: [PATCH 01/11] Implements a proof of concept - Search using jsonpath passed to postgres as a filter expression --- config/config.exs | 1 + lib/trento/activity_log.ex | 60 +++++++++++++++++-- lib/trento/postgrex/jsonpath.ex | 37 ++++++++++++ lib/trento/postgrex/types.ex | 5 ++ .../controllers/v1/activity_log_controller.ex | 5 ++ mix.exs | 1 + ...093640_add_activity_log_metadata_index.exs | 10 ++++ 7 files changed, 114 insertions(+), 5 deletions(-) create mode 100644 lib/trento/postgrex/jsonpath.ex create mode 100644 lib/trento/postgrex/types.ex create mode 100644 priv/repo/migrations/20240920093640_add_activity_log_metadata_index.exs diff --git a/config/config.exs b/config/config.exs index e65145eefb..a2cd373dbf 100644 --- a/config/config.exs +++ b/config/config.exs @@ -224,6 +224,7 @@ config :trento, config :trento, :activity_log, refresh_interval: 60_000 +config :trento, Trento.Repo, types: Trento.Postgrex.Types # Import environment specific config. This must remain at the bottom # of this file so it overrides the configuration defined above. import_config "#{config_env()}.exs" diff --git a/lib/trento/activity_log.ex b/lib/trento/activity_log.ex index 201c9b2d35..8e98020179 100644 --- a/lib/trento/activity_log.ex +++ b/lib/trento/activity_log.ex @@ -65,11 +65,19 @@ defmodule Trento.ActivityLog do def list_activity_log(params, include_all_log_types? \\ false) do parsed_params = parse_params(params) - case ActivityLog - |> maybe_exclude_user_logs(include_all_log_types?) - |> Flop.validate_and_run(parsed_params, for: ActivityLog) do - {:ok, {activity_log_entries, meta}} -> - {:ok, activity_log_entries, meta} + ActivityLog + |> maybe_exclude_user_logs(include_all_log_types?) + |> maybe_search_by_metadata(params) + |> case do + {:ok, query} -> + case Flop.validate_and_run(query, parsed_params, for: ActivityLog) do + {:ok, {activity_log_entries, meta}} -> + {:ok, activity_log_entries, meta} + + error -> + Logger.error("Activity log fetch error: #{inspect(error)}") + {:error, :activity_log_fetch_error} + end error -> Logger.error("Activity log fetch error: #{inspect(error)}") @@ -77,6 +85,33 @@ defmodule Trento.ActivityLog do end end + defp maybe_search_by_metadata(query, params) do + case parse_metadata_query_string(params) do + {:ok, validated_query_string} -> + {:ok, + from( + q in query, + select: %{ + id: q.id, + metadata: q.metadata, + md: + fragment( + "jsonb_path_query(?, ?)", + q.metadata, + ^validated_query_string + ), + type: q.type, + actor: q.actor, + inserted_at: q.inserted_at, + updated_at: q.updated_at + } + )} + + :error -> + {:error, :jsonpath_error} + end + end + defp maybe_exclude_user_logs(ActivityLog = q, true = _include_all_log_types?), do: q defp maybe_exclude_user_logs(ActivityLog = q, false = _include_all_log_types?) do @@ -123,6 +158,21 @@ defmodule Trento.ActivityLog do end) end + def parse_metadata_query_string(query_params) do + maybe_metadata_search = query_params[:metadata] + + case is_binary(maybe_metadata_search) do + true -> + case {:ok, :add_parser_here} do + {:ok, _} -> {:ok, maybe_metadata_search} + _ -> :error + end + + _ -> + :error + end + end + defp log_error({:error, _} = error, message) do Logger.error("#{message}: #{inspect(error)}") error diff --git a/lib/trento/postgrex/jsonpath.ex b/lib/trento/postgrex/jsonpath.ex new file mode 100644 index 0000000000..bd94e4b8f6 --- /dev/null +++ b/lib/trento/postgrex/jsonpath.ex @@ -0,0 +1,37 @@ +defmodule Trento.Postgrex.Jsonpath do + @behaviour Postgrex.Extension + + @impl Postgrex.Extension + def init(opts) do + Keyword.get(opts, :decode_copy, :copy) + end + + @impl Postgrex.Extension + def matching(_state), do: [type: "jsonpath"] + + @impl Postgrex.Extension + def format(_state), do: :text + + @impl Postgrex.Extension + def encode(_state) do + quote do + bin when is_binary(bin) -> + [<> | bin] + end + end + + @impl Postgrex.Extension + def decode(:reference) do + quote do + <> -> + bin + end + end + + def decode(:copy) do + quote do + <> -> + :binary.copy(bin) + end + end +end diff --git a/lib/trento/postgrex/types.ex b/lib/trento/postgrex/types.ex new file mode 100644 index 0000000000..84ebd9f8af --- /dev/null +++ b/lib/trento/postgrex/types.ex @@ -0,0 +1,5 @@ +Postgrex.Types.define( + Trento.Postgrex.Types, + [Trento.Postgrex.Jsonpath], + decode_binary: :reference +) diff --git a/lib/trento_web/controllers/v1/activity_log_controller.ex b/lib/trento_web/controllers/v1/activity_log_controller.ex index cfc9c74dec..8f2995d9cf 100644 --- a/lib/trento_web/controllers/v1/activity_log_controller.ex +++ b/lib/trento_web/controllers/v1/activity_log_controller.ex @@ -53,6 +53,11 @@ defmodule TrentoWeb.V1.ActivityLogController do in: :query, schema: %OpenApiSpex.Schema{type: :array}, required: false + ], + metadata: [ + in: :query, + schema: %OpenApiSpex.Schema{type: :string}, + required: false ] ], responses: [ diff --git a/mix.exs b/mix.exs index 8117079c02..dde4c632d8 100644 --- a/mix.exs +++ b/mix.exs @@ -116,6 +116,7 @@ defmodule Trento.MixProject do {:ecto_commons, "~> 0.3.4"}, {:bodyguard, "~> 2.4"}, {:nimble_totp, "~> 1.0"}, + {:nimble_parsec, "~> 1.0"}, {:phoenix_view, "~> 2.0"}, {:phoenix_html_helpers, "~> 1.0"}, {:pow_assent, "~> 0.4.18"}, diff --git a/priv/repo/migrations/20240920093640_add_activity_log_metadata_index.exs b/priv/repo/migrations/20240920093640_add_activity_log_metadata_index.exs new file mode 100644 index 0000000000..3074d801b4 --- /dev/null +++ b/priv/repo/migrations/20240920093640_add_activity_log_metadata_index.exs @@ -0,0 +1,10 @@ +defmodule Trento.Repo.Migrations.AddActivityLogMetadataIndex do + use Ecto.Migration + + def change do + create index("activity_logs", ["metadata jsonb_path_ops"], + name: "activity_logs_metadata_containment_idx", + using: "GIN" + ) + end +end From b58a9ed956442a9054511e8f6a4555aeb2b8c435 Mon Sep 17 00:00:00 2001 From: Gagandeep Bhatia Date: Tue, 24 Sep 2024 12:51:22 +0200 Subject: [PATCH 02/11] Implements UI for search by metadata --- assets/js/pages/ActivityLogPage/ActivityLogPage.jsx | 7 +++++++ assets/js/pages/ActivityLogPage/searchParams.js | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/assets/js/pages/ActivityLogPage/ActivityLogPage.jsx b/assets/js/pages/ActivityLogPage/ActivityLogPage.jsx index d9502d93ac..18cdc537b2 100644 --- a/assets/js/pages/ActivityLogPage/ActivityLogPage.jsx +++ b/assets/js/pages/ActivityLogPage/ActivityLogPage.jsx @@ -16,6 +16,7 @@ import { PaginationPrevNext, defaultItemsPerPageOptions, } from '@common/Pagination'; +import Input from '@common/Input'; import { applyItemsPerPage, @@ -66,6 +67,7 @@ function ActivityLogPage() { const [activityLogDetailModalOpen, setActivityLogDetailModalOpen] = useState(false); const { abilities } = useSelector(getUserProfile); + const [metadataSearchString, setMetadataSearchString] = useState('$.** ? (@ == "cluster")'); const filters = [ { @@ -128,6 +130,11 @@ function ActivityLogPage() {
+ {searchParams.set('metadata', e.target.value); setMetadataSearchString(e.target.value); }} + + /> ); const paginationFields = ['after', 'before', 'first', 'last']; -const scalarKeys = [...paginationFields]; +const scalarKeys = [...paginationFields, 'metadata']; const searchParamsToEntries = (searchParams) => pipe(Array.from, uniq, (keys) => From fd5a1f99e5f29923d69027edbccf214fca687f73 Mon Sep 17 00:00:00 2001 From: Gagandeep Bhatia Date: Tue, 24 Sep 2024 13:05:21 +0200 Subject: [PATCH 03/11] Improves error handling ... in case of nils and empty strings --- lib/trento/activity_log.ex | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/lib/trento/activity_log.ex b/lib/trento/activity_log.ex index 8e98020179..785b4b70fc 100644 --- a/lib/trento/activity_log.ex +++ b/lib/trento/activity_log.ex @@ -80,13 +80,15 @@ defmodule Trento.ActivityLog do end error -> - Logger.error("Activity log fetch error: #{inspect(error)}") + Logger.error("Activity log fetch error, metadata parse failure: #{inspect(error)}") {:error, :activity_log_fetch_error} end end defp maybe_search_by_metadata(query, params) do - case parse_metadata_query_string(params) do + maybe_metadata_search_string = params[:metadata] + + case parse_metadata_query_string(maybe_metadata_search_string) do {:ok, validated_query_string} -> {:ok, from( @@ -107,8 +109,11 @@ defmodule Trento.ActivityLog do } )} + :noop -> + {:ok, query} + :error -> - {:error, :jsonpath_error} + {:ok, query} end end @@ -158,17 +163,19 @@ defmodule Trento.ActivityLog do end) end - def parse_metadata_query_string(query_params) do - maybe_metadata_search = query_params[:metadata] - - case is_binary(maybe_metadata_search) do + def parse_metadata_query_string(maybe_metadata_search) do + case is_binary(maybe_metadata_search) && maybe_metadata_search != "" do true -> case {:ok, :add_parser_here} do {:ok, _} -> {:ok, maybe_metadata_search} _ -> :error end - _ -> + false when is_nil(maybe_metadata_search) -> + :noop + + false -> + Logger.error("Not a binary #{inspect(maybe_metadata_search)}") :error end end From 67e8c2fe6cef0edd874bc2b917246444d1eeec04 Mon Sep 17 00:00:00 2001 From: Gagandeep Bhatia Date: Wed, 25 Sep 2024 10:57:53 +0200 Subject: [PATCH 04/11] Updates UI to make input larger --- assets/js/pages/ActivityLogPage/ActivityLogPage.jsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/assets/js/pages/ActivityLogPage/ActivityLogPage.jsx b/assets/js/pages/ActivityLogPage/ActivityLogPage.jsx index 18cdc537b2..adbbe71532 100644 --- a/assets/js/pages/ActivityLogPage/ActivityLogPage.jsx +++ b/assets/js/pages/ActivityLogPage/ActivityLogPage.jsx @@ -67,7 +67,7 @@ function ActivityLogPage() { const [activityLogDetailModalOpen, setActivityLogDetailModalOpen] = useState(false); const { abilities } = useSelector(getUserProfile); - const [metadataSearchString, setMetadataSearchString] = useState('$.** ? (@ == "cluster")'); + const [metadataSearchString, setMetadataSearchString] = useState(''); const filters = [ { @@ -127,14 +127,19 @@ function ActivityLogPage() { return ( <> Activity Log -
-
-
+
{searchParams.set('metadata', e.target.value); setMetadataSearchString(e.target.value); }} /> +
+
+
+
+ + Date: Wed, 25 Sep 2024 10:59:12 +0200 Subject: [PATCH 05/11] Adds a very roughcut query lang --- lib/trento/activity_log.ex | 79 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 4 deletions(-) diff --git a/lib/trento/activity_log.ex b/lib/trento/activity_log.ex index 785b4b70fc..b1c024ca59 100644 --- a/lib/trento/activity_log.ex +++ b/lib/trento/activity_log.ex @@ -112,7 +112,7 @@ defmodule Trento.ActivityLog do :noop -> {:ok, query} - :error -> + _ -> {:ok, query} end end @@ -166,9 +166,13 @@ defmodule Trento.ActivityLog do def parse_metadata_query_string(maybe_metadata_search) do case is_binary(maybe_metadata_search) && maybe_metadata_search != "" do true -> - case {:ok, :add_parser_here} do - {:ok, _} -> {:ok, maybe_metadata_search} - _ -> :error + case do_parse_metadata_query_string(maybe_metadata_search) do + {:ok, parsed_string} -> + {:ok, parsed_string} + + error -> + Logger.warning("Metadata query parse failed #{inspect(error)}") + error end false when is_nil(maybe_metadata_search) -> @@ -180,6 +184,73 @@ defmodule Trento.ActivityLog do end end + defp do_parse_metadata_query_string(query_string) do + Logger.warning(query_string) + starting_char = query_string |> String.trim() |> String.first() + + case starting_char do + "$" -> + {:ok, query_string} + + "|" -> + parsed_rest = + query_string + |> String.trim_leading("|") + |> String.split(",") + |> Enum.map(&String.trim/1) + + case(length(parsed_rest) > 0) do + true -> + splice_this = + Enum.map(parsed_rest, fn op_p -> "(@ #{op_p})" end) |> Enum.join(" || ") + + {:ok, "$.** ? (#{splice_this})"} + + _ -> + {:error, :parse_failed_or} + end + + "&" -> + parsed_rest = + query_string + |> String.trim_leading("&") + |> String.split(",") + |> Enum.map(&String.trim/1) + + case(length(parsed_rest) > 0) do + true -> + splice_this = + Enum.map(parsed_rest, fn op_p -> "(@ #{op_p})" end) |> Enum.join(" || ") + + {:ok, "$.** ? (#{splice_this})"} + + _ -> + {:error, :parse_failed_and} + end + + "=" -> + parsed_rest = + query_string + |> String.trim() + |> String.trim_leading("=") + |> String.trim() + + {:ok, "$.** ? (@ == #{parsed_rest})"} + + "~" -> + parsed_rest = + query_string + |> String.trim() + |> String.trim_leading("~") + |> String.trim() + + {:ok, "$.** ? (@ like_regex #{parsed_rest})"} |> IO.inspect() + + _ -> + {:error, :parse_failed} + end + end + defp log_error({:error, _} = error, message) do Logger.error("#{message}: #{inspect(error)}") error From cc752da47a79333efb868ba27150ae0ceff22d3f Mon Sep 17 00:00:00 2001 From: Gagandeep Bhatia Date: Wed, 25 Sep 2024 17:31:29 +0200 Subject: [PATCH 06/11] Simplifies query text interface According to feedback --- lib/trento/activity_log.ex | 92 +++++++++++++++----------------------- 1 file changed, 36 insertions(+), 56 deletions(-) diff --git a/lib/trento/activity_log.ex b/lib/trento/activity_log.ex index b1c024ca59..ef9b46ef1f 100644 --- a/lib/trento/activity_log.ex +++ b/lib/trento/activity_log.ex @@ -185,69 +185,49 @@ defmodule Trento.ActivityLog do end defp do_parse_metadata_query_string(query_string) do - Logger.warning(query_string) - starting_char = query_string |> String.trim() |> String.first() + query_words_list = + query_string + |> String.trim() + |> String.split() - case starting_char do - "$" -> - {:ok, query_string} + word_handler = fn + "AND" -> + "&&" - "|" -> - parsed_rest = - query_string - |> String.trim_leading("|") - |> String.split(",") - |> Enum.map(&String.trim/1) + "OR" -> + "||" - case(length(parsed_rest) > 0) do - true -> - splice_this = - Enum.map(parsed_rest, fn op_p -> "(@ #{op_p})" end) |> Enum.join(" || ") + <<"~"::binary, _rest::binary>> = regex -> + "(@ like_regex \"#{String.trim_leading(regex, "~")}\")" - {:ok, "$.** ? (#{splice_this})"} - - _ -> - {:error, :parse_failed_or} - end - - "&" -> - parsed_rest = - query_string - |> String.trim_leading("&") - |> String.split(",") - |> Enum.map(&String.trim/1) - - case(length(parsed_rest) > 0) do - true -> - splice_this = - Enum.map(parsed_rest, fn op_p -> "(@ #{op_p})" end) |> Enum.join(" || ") + qws -> + "(@ == \"#{qws}\")" + end - {:ok, "$.** ? (#{splice_this})"} + case Enum.any?(query_words_list, fn qws -> qws == "OR" or qws == "AND" end) do + true -> + {:ok, + query_words_list + |> Enum.map(word_handler) + |> Enum.join(" ") + |> (&"$.** ? (#{&1})").()} - _ -> - {:error, :parse_failed_and} + false -> + case length(query_words_list) do + 1 -> + {:ok, + query_words_list + |> Enum.map(word_handler) + |> Enum.join("") + |> (&"$.** ? #{&1}").()} + + len when len > 1 -> + {:ok, + query_words_list + |> Enum.map(word_handler) + |> Enum.join(" || ") + |> (&"$.** ? (#{&1})").()} end - - "=" -> - parsed_rest = - query_string - |> String.trim() - |> String.trim_leading("=") - |> String.trim() - - {:ok, "$.** ? (@ == #{parsed_rest})"} - - "~" -> - parsed_rest = - query_string - |> String.trim() - |> String.trim_leading("~") - |> String.trim() - - {:ok, "$.** ? (@ like_regex #{parsed_rest})"} |> IO.inspect() - - _ -> - {:error, :parse_failed} end end From 5b4f2bb4302bf9927842c623ff1b6c2d5d6f61d8 Mon Sep 17 00:00:00 2001 From: Gagandeep Bhatia Date: Wed, 25 Sep 2024 18:07:34 +0200 Subject: [PATCH 07/11] Improves search UI Makes the search input part of the composed filters. Improves the UI/code-org compared to the initial implementation. --- .../js/common/ComposedFilter/ComposedFilter.jsx | 5 +++++ .../pages/ActivityLogPage/ActivityLogPage.jsx | 17 +++++------------ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/assets/js/common/ComposedFilter/ComposedFilter.jsx b/assets/js/common/ComposedFilter/ComposedFilter.jsx index 7a5f05f034..09234fd540 100644 --- a/assets/js/common/ComposedFilter/ComposedFilter.jsx +++ b/assets/js/common/ComposedFilter/ComposedFilter.jsx @@ -2,9 +2,14 @@ import React, { useEffect, useState } from 'react'; import Button from '@common/Button'; import Filter from '@common/Filter'; import DateFilter from '@common/DateFilter'; +import Input from '@common/Input'; const renderFilter = (key, { type, ...filterProps }, value, onChange) => { switch (type) { + case 'search_input': + return ( + onChange(e.target.value)} /> + ); case 'select': return ( diff --git a/assets/js/pages/ActivityLogPage/ActivityLogPage.jsx b/assets/js/pages/ActivityLogPage/ActivityLogPage.jsx index adbbe71532..f76529e2b1 100644 --- a/assets/js/pages/ActivityLogPage/ActivityLogPage.jsx +++ b/assets/js/pages/ActivityLogPage/ActivityLogPage.jsx @@ -16,7 +16,6 @@ import { PaginationPrevNext, defaultItemsPerPageOptions, } from '@common/Pagination'; -import Input from '@common/Input'; import { applyItemsPerPage, @@ -67,9 +66,13 @@ function ActivityLogPage() { const [activityLogDetailModalOpen, setActivityLogDetailModalOpen] = useState(false); const { abilities } = useSelector(getUserProfile); - const [metadataSearchString, setMetadataSearchString] = useState(''); const filters = [ + { + key: 'metadata', + type: 'search_input', + title: 'Metadata', + }, { key: 'type', type: 'select', @@ -127,19 +130,9 @@ function ActivityLogPage() { return ( <> Activity Log -
- {searchParams.set('metadata', e.target.value); setMetadataSearchString(e.target.value); }} - - /> -
- - Date: Wed, 25 Sep 2024 18:17:23 +0200 Subject: [PATCH 08/11] Formatting runs code formatter --- assets/js/common/ComposedFilter/ComposedFilter.jsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/assets/js/common/ComposedFilter/ComposedFilter.jsx b/assets/js/common/ComposedFilter/ComposedFilter.jsx index 09234fd540..da8484fa52 100644 --- a/assets/js/common/ComposedFilter/ComposedFilter.jsx +++ b/assets/js/common/ComposedFilter/ComposedFilter.jsx @@ -6,9 +6,14 @@ import Input from '@common/Input'; const renderFilter = (key, { type, ...filterProps }, value, onChange) => { switch (type) { - case 'search_input': + case 'search_input': return ( - onChange(e.target.value)} /> + onChange(e.target.value)} + /> ); case 'select': return ( From 0d6aa4f3549c17076260336810ac1845af426436 Mon Sep 17 00:00:00 2001 From: Gagandeep Bhatia Date: Thu, 26 Sep 2024 10:03:26 +0200 Subject: [PATCH 09/11] Updates runner image Possible fix for observed cert errors; only updates the image in the part of the work flow that needs to run photofinish --- .github/workflows/pr_env.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr_env.yaml b/.github/workflows/pr_env.yaml index 30250993db..ea52c6c42d 100644 --- a/.github/workflows/pr_env.yaml +++ b/.github/workflows/pr_env.yaml @@ -138,7 +138,7 @@ jobs: run-photofinish-demo-env: name: Use photofinish to push mock data to the pr environment - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 needs: create_pr_environment env: TRENTO_PR_ENV_URL: "${{ github.event.pull_request.number }}.prenv.trento.suse.com" From b77ea8ab33b28b9db7564537061e63d1d4927033 Mon Sep 17 00:00:00 2001 From: Gagandeep Bhatia Date: Thu, 26 Sep 2024 10:10:59 +0200 Subject: [PATCH 10/11] Address credo --- lib/trento/activity_log.ex | 9 +++------ lib/trento/postgrex/jsonpath.ex | 2 ++ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/trento/activity_log.ex b/lib/trento/activity_log.ex index ef9b46ef1f..ff0f0b42b2 100644 --- a/lib/trento/activity_log.ex +++ b/lib/trento/activity_log.ex @@ -208,8 +208,7 @@ defmodule Trento.ActivityLog do true -> {:ok, query_words_list - |> Enum.map(word_handler) - |> Enum.join(" ") + |> Enum.map_join(" ", word_handler) |> (&"$.** ? (#{&1})").()} false -> @@ -217,15 +216,13 @@ defmodule Trento.ActivityLog do 1 -> {:ok, query_words_list - |> Enum.map(word_handler) - |> Enum.join("") + |> Enum.map_join("", word_handler) |> (&"$.** ? #{&1}").()} len when len > 1 -> {:ok, query_words_list - |> Enum.map(word_handler) - |> Enum.join(" || ") + |> Enum.map_join(" || ", word_handler) |> (&"$.** ? (#{&1})").()} end end diff --git a/lib/trento/postgrex/jsonpath.ex b/lib/trento/postgrex/jsonpath.ex index bd94e4b8f6..5872cc55de 100644 --- a/lib/trento/postgrex/jsonpath.ex +++ b/lib/trento/postgrex/jsonpath.ex @@ -1,4 +1,6 @@ defmodule Trento.Postgrex.Jsonpath do + @moduledoc false + @behaviour Postgrex.Extension @impl Postgrex.Extension From da5740feb2bce0cc55facf4fa08bb218c62a2fcb Mon Sep 17 00:00:00 2001 From: Gagandeep Bhatia Date: Thu, 26 Sep 2024 10:21:49 +0200 Subject: [PATCH 11/11] Revert "Updates runner image" This reverts commit 0d6aa4f3549c17076260336810ac1845af426436. --- .github/workflows/pr_env.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr_env.yaml b/.github/workflows/pr_env.yaml index ea52c6c42d..30250993db 100644 --- a/.github/workflows/pr_env.yaml +++ b/.github/workflows/pr_env.yaml @@ -138,7 +138,7 @@ jobs: run-photofinish-demo-env: name: Use photofinish to push mock data to the pr environment - runs-on: ubuntu-24.04 + runs-on: ubuntu-20.04 needs: create_pr_environment env: TRENTO_PR_ENV_URL: "${{ github.event.pull_request.number }}.prenv.trento.suse.com"