[shellprocess] Allow gs[key] variables in commands

This commit is contained in:
Adriaan de Groot 2024-05-14 22:35:50 +02:00
parent 448633ab02
commit 8a14ddc97c
3 changed files with 68 additions and 3 deletions

View File

@ -55,6 +55,41 @@ get_variant_stringlist( const QVariantList& l )
return retl; 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 static Calamares::String::DictionaryExpander
get_gs_expander( System::RunLocation location ) 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; return expander;
} }

View File

@ -282,9 +282,17 @@ LibCalamaresTests::testCommandExpansion_data()
QTest::addColumn< QString >( "command" ); QTest::addColumn< QString >( "command" );
QTest::addColumn< QString >( "expected" ); QTest::addColumn< QString >( "expected" );
QTest::newRow( "empty" ) << QString() << QString(); QTest::newRow( "empty " ) << QString() << QString();
QTest::newRow( "ls " ) << QStringLiteral( "ls" ) << QStringLiteral( "ls" ); 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( "${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 void
@ -295,6 +303,12 @@ LibCalamaresTests::testCommandExpansion()
QVERIFY( gs ); QVERIFY( gs );
gs->insert( QStringLiteral( "username" ), QStringLiteral( "alice" ) ); 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, command );
QFETCH( QString, expected ); QFETCH( QString, expected );
Calamares::CommandLine c( command, std::chrono::seconds( 0 ) ); Calamares::CommandLine c( command, std::chrono::seconds( 0 ) );

View File

@ -16,6 +16,17 @@
# of Calamares, set on the welcome page. This may not reflect the # of Calamares, set on the welcome page. This may not reflect the
# chosen system language from the locale page. # 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}`. # Variables are written as `${var}`, e.g. `${ROOT}`.
# Write `$$` to get a shell-escaped `\$` in the shell command. # Write `$$` to get a shell-escaped `\$` in the shell command.
# It is not possible to get an un-escaped `$` in the shell command # It is not possible to get an un-escaped `$` in the shell command