From 8a14ddc97ccc4da7c25ea787c7c0c41788a7cf80 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 14 May 2024 22:35:50 +0200 Subject: [PATCH] [shellprocess] Allow gs[key] variables in commands --- src/libcalamares/utils/CommandList.cpp | 40 ++++++++++++++++++++++ src/libcalamares/utils/Tests.cpp | 20 +++++++++-- src/modules/shellprocess/shellprocess.conf | 11 ++++++ 3 files changed, 68 insertions(+), 3 deletions(-) diff --git a/src/libcalamares/utils/CommandList.cpp b/src/libcalamares/utils/CommandList.cpp index 86efe1d38..0f5111eac 100644 --- a/src/libcalamares/utils/CommandList.cpp +++ b/src/libcalamares/utils/CommandList.cpp @@ -55,6 +55,41 @@ get_variant_stringlist( const QVariantList& l ) return retl; } +/** @brief Inserts the keys from @p map into @p expander as "gs"-keys + * + * For each key k in @p map, a key with literal `gs[` + prefix + '.' + key + + * literal `]` is inserted into the exapander. + */ +static void +expand_tree( Calamares::String::DictionaryExpander& expander, const QString& prefix, const QVariantMap& map ) +{ + // With the current prefix, turn a key into gs[prefix.key] + auto gs_key = [ &prefix ]( const QString& k ) -> QString + { return QStringLiteral( "gs[" ) + ( prefix.isEmpty() ? QString() : prefix + '.' ) + k + ']'; }; + + for ( QVariantMap::const_iterator valueiter = map.cbegin(); valueiter != map.cend(); ++valueiter ) + { + const QString key = valueiter.key(); + const QVariant value = valueiter.value(); + + switch ( Calamares::typeOf( value ) ) + { + case Calamares::MapVariantType: + expand_tree( expander, prefix.isEmpty() ? key : ( prefix + '.' + key ), value.toMap() ); + break; + case Calamares::StringVariantType: + expander.add( gs_key( key ), value.toString() ); + break; + case Calamares::IntVariantType: + expander.add( gs_key( key ), QString::number( value.toInt() ) ); + break; + default: + // Silently ignore + break; + } + } +} + static Calamares::String::DictionaryExpander get_gs_expander( System::RunLocation location ) { @@ -88,6 +123,11 @@ get_gs_expander( System::RunLocation location ) } } + if ( gs ) + { + expand_tree( expander, QString(), gs->data() ); + } + return expander; } diff --git a/src/libcalamares/utils/Tests.cpp b/src/libcalamares/utils/Tests.cpp index 2e1b5655c..89dd45dfb 100644 --- a/src/libcalamares/utils/Tests.cpp +++ b/src/libcalamares/utils/Tests.cpp @@ -282,9 +282,17 @@ LibCalamaresTests::testCommandExpansion_data() QTest::addColumn< QString >( "command" ); QTest::addColumn< QString >( "expected" ); - QTest::newRow( "empty" ) << QString() << QString(); - QTest::newRow( "ls " ) << QStringLiteral( "ls" ) << QStringLiteral( "ls" ); - QTest::newRow( "user " ) << QStringLiteral( "chmod $USER" ) << QStringLiteral( "chmod alice" ); + QTest::newRow( "empty " ) << QString() << QString(); + QTest::newRow( "ls " ) << QStringLiteral( "ls" ) << QStringLiteral( "ls" ); + QTest::newRow( "$USER " ) << QStringLiteral( "chmod $USER" ) << QStringLiteral( "chmod alice" ); + QTest::newRow( "${USER}" ) << QStringLiteral( "chmod ${USER}" ) << QStringLiteral( "chmod alice" ); + QTest::newRow( "gs-user" ) << QStringLiteral( "chmod ${gs[username]}" ) << QStringLiteral( "chmod alice" ); + QTest::newRow( "gs-* " ) << QStringLiteral( + "${gs[username]} has ${gs[branding.bootloader]} ${gs[branding.ducks]} ducks" ) + << QStringLiteral( "alice has found 3 ducks" ); + // QStringList does not expand + QTest::newRow( "gs-list" ) << QStringLiteral( "colors ${gs[branding.color]}" ) + << QStringLiteral( "colors ${gs[branding.color]}" ); } void @@ -295,6 +303,12 @@ LibCalamaresTests::testCommandExpansion() QVERIFY( gs ); gs->insert( QStringLiteral( "username" ), QStringLiteral( "alice" ) ); + QVariantMap m; + m.insert( QStringLiteral( "bootloader" ), QStringLiteral( "found" ) ); + m.insert( QStringLiteral( "ducks" ), 3 ); + m.insert( QStringLiteral( "color" ), QStringList { "green", "red" } ); + gs->insert( QStringLiteral( "branding" ), m ); + QFETCH( QString, command ); QFETCH( QString, expected ); Calamares::CommandLine c( command, std::chrono::seconds( 0 ) ); diff --git a/src/modules/shellprocess/shellprocess.conf b/src/modules/shellprocess/shellprocess.conf index 87e31c58c..41a7d2733 100644 --- a/src/modules/shellprocess/shellprocess.conf +++ b/src/modules/shellprocess/shellprocess.conf @@ -16,6 +16,17 @@ # of Calamares, set on the welcome page. This may not reflect the # chosen system language from the locale page. # +# As a special case, variables of the form `gs[key]` where `key` is +# a dotted-keys string and `gs` is literally the letters `g` and `s`, +# use **any** value from Global Storage. For example, +# +# gs[branding.bootloader] +# +# This variable refers to the GS value stored in `bootloader` in the +# `branding` map. Examine the Debug window for information about the +# keys stored in GS. Only strings and integers are exposed this way, +# lists and other data types do not set any variable this way. +# # Variables are written as `${var}`, e.g. `${ROOT}`. # Write `$$` to get a shell-escaped `\$` in the shell command. # It is not possible to get an un-escaped `$` in the shell command