Merge branch 'scripting'

This commit is contained in:
Adriaan de Groot 2018-01-15 09:05:30 -05:00
commit 15d4245074
17 changed files with 734 additions and 60 deletions

View File

@ -22,6 +22,7 @@ set( libSources
set( utilsSources
utils/CalamaresUtils.cpp
utils/CalamaresUtilsSystem.cpp
utils/CommandList.cpp
utils/Logger.cpp
utils/PluginFactory.cpp
utils/Retranslator.cpp

View File

@ -82,7 +82,7 @@ ProcessJob::exec()
QString(),
m_timeoutSec );
return explainProcess( ec, m_command, output, m_timeoutSec );
return CalamaresUtils::ProcessResult::explainProcess( this, ec, m_command, output, m_timeoutSec );
}
@ -144,41 +144,4 @@ ProcessJob::callOutput( const QString& command,
return process.exitCode();
}
JobResult
ProcessJob::explainProcess( int ec, const QString& command, const QString& output, int timeout )
{
if ( ec == 0 )
return JobResult::ok();
if ( ec == -1 ) //Crash!
return JobResult::error( tr( "External command crashed" ),
tr( "Command %1 crashed.\nOutput:\n%2" )
.arg( command )
.arg( output ) );
if ( ec == -2 )
return JobResult::error( tr( "External command failed to start" ),
tr( "Command %1 failed to start." )
.arg( command ) );
if ( ec == -3 )
return JobResult::error( tr( "Internal error when starting command" ),
tr( "Bad parameters for process job call." ) );
if ( ec == -4 )
return JobResult::error( tr( "External command failed to finish" ),
tr( "Command %1 failed to finish in %2s.\nOutput:\n%3" )
.arg( command )
.arg( timeout )
.arg( output ) );
//Any other exit code
return JobResult::error( tr( "External command finished with errors" ),
tr( "Command %1 finished with exit code %2.\nOutput:\n%3" )
.arg( command )
.arg( ec )
.arg( output ) );
}
} // namespace Calamares

View File

@ -39,10 +39,6 @@ public:
QString prettyStatusMessage() const override;
JobResult exec() override;
protected:
/** @brief Explain a typical external process failure. */
static JobResult explainProcess( int errorCode, const QString& command, const QString& output, int timeout );
private:
int callOutput( const QString& command,
QString& output,

View File

@ -1,7 +1,7 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2014, Teo Mrnjavac <teo@kde.org>
* Copyright 2017, Adriaan de Groot <groot@kde.org>
* Copyright 2017-2018, Adriaan de Groot <groot@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -93,7 +93,8 @@ System::mount( const QString& devicePath,
}
ProcessResult
System::targetEnvCommand(
System::runCommand(
System::RunLocation location,
const QStringList& args,
const QString& workingPath,
const QString& stdInput,
@ -105,8 +106,8 @@ System::targetEnvCommand(
return -3;
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
if ( !gs ||
( m_doChroot && !gs->contains( "rootMountPoint" ) ) )
if ( ( location == System::RunLocation::RunInTarget ) &&
( !gs || !gs->contains( "rootMountPoint" ) ) )
{
cLog() << "No rootMountPoint in global storage";
return -3;
@ -116,7 +117,7 @@ System::targetEnvCommand(
QString program;
QStringList arguments;
if ( m_doChroot )
if ( location == System::RunLocation::RunInTarget )
{
QString destDir = gs->value( "rootMountPoint" ).toString();
if ( !QDir( destDir ).exists() )
@ -249,4 +250,45 @@ System::doChroot() const
return m_doChroot;
}
Calamares::JobResult
ProcessResult::explainProcess( const QObject* parent, int ec, const QString& command, const QString& output, int timeout )
{
using Calamares::JobResult;
if ( ec == 0 )
return JobResult::ok();
QString outputMessage = output.isEmpty() ? QStringLiteral("\nThere was no output from the command.")
: (parent->tr("\nOutput:\n") + output);
if ( ec == -1 ) //Crash!
return JobResult::error( parent->tr( "External command crashed." ),
parent->tr( "Command <i>%1</i> crashed." )
.arg( command )
+ outputMessage );
if ( ec == -2 )
return JobResult::error( parent->tr( "External command failed to start." ),
parent->tr( "Command <i>%1</i> failed to start." )
.arg( command ) );
if ( ec == -3 )
return JobResult::error( parent->tr( "Internal error when starting command." ),
parent->tr( "Bad parameters for process job call." ) );
if ( ec == -4 )
return JobResult::error( parent->tr( "External command failed to finish." ),
parent->tr( "Command <i>%1</i> failed to finish in %2 seconds." )
.arg( command )
.arg( timeout )
+ outputMessage );
//Any other exit code
return JobResult::error( parent->tr( "External command finished with errors." ),
parent->tr( "Command <i>%1</i> finished with exit code %2." )
.arg( command )
.arg( ec )
+ outputMessage );
}
} // namespace

View File

@ -1,7 +1,7 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2014, Teo Mrnjavac <teo@kde.org>
* Copyright 2017, Adriaan de Groot <groot@kde.org>
* Copyright 2017-2018, Adriaan de Groot <groot@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -21,6 +21,8 @@
#include "DllMacro.h"
#include "Job.h"
#include <QObject>
#include <QPair>
#include <QString>
@ -33,6 +35,37 @@ public:
/** @brief Implicit one-argument constructor has no output, only a return code */
ProcessResult( int r ) : QPair< int, QString >( r, QString() ) {}
ProcessResult( int r, QString s ) : QPair< int, QString >( r, s ) {}
int getExitCode() const { return first; }
QString getOutput() const { return second; }
/** @brief Explain a typical external process failure.
*
* @param parent Used as context for translation calls.
* @param errorCode Return code from runCommand() or similar
* (negative values get special explanation). The member
* function uses the exit code stored in the ProcessResult
* @param output (error) output from the command, used when there is
* an error to report (exit code > 0). The member
* function uses the output stored in the ProcessResult.
* @param command String or split-up string of the command
* that was invoked.
* @param timeout Timeout passed to the process runner, for explaining
* error code -4 (timeout).
*/
static Calamares::JobResult explainProcess( const QObject* parent, int errorCode, const QString& command, const QString& output, int timeout );
/// @brief Convenience wrapper for explainProcess()
inline Calamares::JobResult explainProcess( const QObject* parent, const QString& command, int timeout ) const
{
return explainProcess( parent, getExitCode(), command, getOutput(), timeout );
}
/// @brief Convenience wrapper for explainProcess()
inline Calamares::JobResult explainProcess( const QObject* parent, const QStringList& command, int timeout ) const
{
return explainProcess( parent, getExitCode(), command.join( ' ' ), getOutput(), timeout );
}
} ;
/**
@ -71,15 +104,20 @@ public:
const QString& options = QString() );
/** (Typed) Boolean describing where a particular command should be run,
* whether in the host (live) system or in the (chroot) target system.
*/
enum class RunLocation { RunInHost, RunInTarget };
/**
* Runs the specified command in the chroot of the target system.
* @param args the command with arguments, as a string list.
* @param workingPath the current working directory for the QProcess
* call (optional).
* call (optional).
* @param stdInput the input string to send to the running process as
* standard input (optional).
* standard input (optional).
* @param timeoutSec the timeout after which the process will be
* killed (optional, default is 0 i.e. no timeout).
* killed (optional, default is 0 i.e. no timeout).
*
* @returns the program's exit code and its output (if any). Special
* exit codes (which will never have any output) are:
@ -88,12 +126,32 @@ public:
* -3 = bad arguments
* -4 = QProcess timeout
*/
DLLEXPORT ProcessResult targetEnvCommand(
static DLLEXPORT ProcessResult runCommand(
RunLocation location,
const QStringList &args,
const QString& workingPath = QString(),
const QString& stdInput = QString(),
int timeoutSec = 0 );
/** @brief Convenience wrapper for runCommand().
* Runs the command in the location specified through the boolean
* doChroot(), which is what you usually want for running commands
* during installation.
*/
inline ProcessResult targetEnvCommand(
const QStringList &args,
const QString& workingPath = QString(),
const QString& stdInput = QString(),
int timeoutSec = 0 )
{
return runCommand(
m_doChroot ? RunLocation::RunInTarget : RunLocation::RunInHost,
args,
workingPath,
stdInput,
timeoutSec );
}
/** @brief Convenience wrapper for targetEnvCommand() which returns only the exit code */
inline int targetEnvCall( const QStringList& args,
const QString& workingPath = QString(),

View File

@ -0,0 +1,120 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2018, Adriaan de Groot <groot@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CommandList.h"
#include "GlobalStorage.h"
#include "JobQueue.h"
#include "utils/CalamaresUtilsSystem.h"
#include "utils/Logger.h"
#include <QVariantList>
static QStringList get_variant_stringlist( const QVariantList& l )
{
QStringList retl;
unsigned int c = 0;
for ( const auto& v : l )
{
if ( v.type() == QVariant::String )
retl.append( v.toString() );
else
cDebug() << "WARNING Bad CommandList element" << c << v.type() << v;
++c;
}
return retl;
}
namespace CalamaresUtils
{
CommandList::CommandList( bool doChroot )
: m_doChroot( doChroot )
{
}
CommandList::CommandList::CommandList( const QVariant& v, bool doChroot )
: CommandList( doChroot )
{
if ( v.type() == QVariant::List )
{
const auto v_list = v.toList();
if ( v_list.count() )
append( get_variant_stringlist( v_list ) );
else
cDebug() << "WARNING: Empty CommandList";
}
else if ( v.type() == QVariant::String )
append( v.toString() );
else
cDebug() << "WARNING: CommandList does not understand variant" << v.type();
}
CommandList::~CommandList()
{
}
Calamares::JobResult CommandList::run( const QObject* parent )
{
System::RunLocation location = m_doChroot ? System::RunLocation::RunInTarget : System::RunLocation::RunInHost;
/* Figure out the replacement for @@ROOT@@ */
QString root = QStringLiteral( "/" );
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
if ( location == System::RunLocation::RunInTarget )
{
if ( !gs || !gs->contains( "rootMountPoint" ) )
{
cDebug() << "ERROR: No rootMountPoint defined.";
return Calamares::JobResult::error( parent->tr( "Could not run command." ),
parent->tr( "No rootMountPoint is defined, so command cannot be run in the target environment." ) );
}
root = gs->value( "rootMountPoint" ).toString();
}
for ( CommandList::const_iterator i = cbegin(); i != cend(); ++i )
{
QString processed_cmd = *i;
processed_cmd.replace( "@@ROOT@@", root ); // FIXME?
bool suppress_result = false;
if ( processed_cmd.startsWith( '-' ) )
{
suppress_result = true;
processed_cmd.remove( 0, 1 ); // Drop the - // FIXME?
}
QStringList shell_cmd { "/bin/sh", "-c" };
shell_cmd << processed_cmd;
ProcessResult r = System::runCommand(
location, shell_cmd, QString(), QString(), 10 );
if ( r.getExitCode() != 0 )
{
if ( suppress_result )
cDebug() << "Error code" << r.getExitCode() << "ignored by CommandList configuration.";
else
return r.explainProcess( parent, processed_cmd, 10 );
}
}
return Calamares::JobResult::ok();
}
} // namespace

View File

@ -0,0 +1,55 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2018, Adriaan de Groot <groot@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef COMMANDLIST_H
#define COMMANDLIST_H
#include "Job.h"
#include <QStringList>
#include <QVariant>
namespace CalamaresUtils
{
class CommandList : protected QStringList
{
public:
CommandList( bool doChroot = true );
CommandList( const QVariant& v, bool doChroot = true );
~CommandList();
bool doChroot() const
{
return m_doChroot;
}
Calamares::JobResult run( const QObject* parent );
using QStringList::isEmpty;
using QStringList::count;
using QStringList::cbegin;
using QStringList::cend;
using QStringList::const_iterator;
private:
bool m_doChroot;
} ;
} // namespace
#endif // COMMANDLIST_H

View File

@ -1,6 +1,6 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2017, Adriaan de Groot <groot@kde.org>
* Copyright 2017-2018, Adriaan de Groot <groot@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -26,8 +26,37 @@
#include "JobQueue.h"
#include "GlobalStorage.h"
#include "utils/CalamaresUtils.h"
#include "utils/CommandList.h"
#include "utils/Logger.h"
struct ContextualProcessBinding
{
ContextualProcessBinding( const QString& _n, const QString& _v, CalamaresUtils::CommandList* _c )
: variable( _n )
, value( _v )
, commands( _c )
{
}
~ContextualProcessBinding();
int count() const
{
return commands ? commands->count() : 0;
}
QString variable;
QString value;
CalamaresUtils::CommandList* commands;
} ;
ContextualProcessBinding::~ContextualProcessBinding()
{
delete commands;
}
ContextualProcessJob::ContextualProcessJob( QObject* parent )
: Calamares::CppJob( parent )
{
@ -36,6 +65,7 @@ ContextualProcessJob::ContextualProcessJob( QObject* parent )
ContextualProcessJob::~ContextualProcessJob()
{
qDeleteAll( m_commands );
}
@ -49,8 +79,17 @@ ContextualProcessJob::prettyName() const
Calamares::JobResult
ContextualProcessJob::exec()
{
QThread::sleep( 3 );
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
for ( const ContextualProcessBinding* binding : m_commands )
{
if ( gs->contains( binding->variable ) && ( gs->value( binding->variable ).toString() == binding->value ) )
{
Calamares::JobResult r = binding->commands->run( this );
if ( !r )
return r;
}
}
return Calamares::JobResult::ok();
}
@ -58,7 +97,41 @@ ContextualProcessJob::exec()
void
ContextualProcessJob::setConfigurationMap( const QVariantMap& configurationMap )
{
m_configurationMap = configurationMap;
m_dontChroot = CalamaresUtils::getBool( configurationMap, "dontChroot", false );
for ( QVariantMap::const_iterator iter = configurationMap.cbegin(); iter != configurationMap.cend(); ++iter )
{
QString variableName = iter.key();
if ( variableName.isEmpty() || ( variableName == "dontChroot" ) )
continue;
if ( iter.value().type() != QVariant::Map )
{
cDebug() << "WARNING:" << moduleInstanceKey() << "bad configuration values for" << variableName;
continue;
}
QVariantMap values = iter.value().toMap();
for ( QVariantMap::const_iterator valueiter = values.cbegin(); valueiter != values.cend(); ++valueiter )
{
QString valueString = valueiter.key();
if ( variableName.isEmpty() )
{
cDebug() << "WARNING:" << moduleInstanceKey() << "variable" << variableName << "unrecognized value" << valueiter.key();
continue;
}
CalamaresUtils::CommandList* commands = new CalamaresUtils::CommandList( valueiter.value(), !m_dontChroot );
if ( commands->count() > 0 )
{
m_commands.append( new ContextualProcessBinding( variableName, valueString, commands ) );
cDebug() << variableName << '=' << valueString << "will execute" << commands->count() << "commands";
}
else
delete commands;
}
}
}
CALAMARES_PLUGIN_FACTORY_DEFINITION( ContextualProcessJobFactory, registerPlugin<ContextualProcessJob>(); )

View File

@ -1,6 +1,6 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2017, Adriaan de Groot <groot@kde.org>
* Copyright 2017-2018, Adriaan de Groot <groot@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -22,11 +22,12 @@
#include <QObject>
#include <QVariantMap>
#include <CppJob.h>
#include "CppJob.h"
#include "PluginDllMacro.h"
#include <utils/PluginFactory.h>
#include "utils/PluginFactory.h"
#include <PluginDllMacro.h>
struct ContextualProcessBinding;
class PLUGINDLLEXPORT ContextualProcessJob : public Calamares::CppJob
{
@ -43,7 +44,8 @@ public:
void setConfigurationMap( const QVariantMap& configurationMap ) override;
private:
QVariantMap m_configurationMap;
QList<ContextualProcessBinding*> m_commands;
bool m_dontChroot;
};
CALAMARES_PLUGIN_FACTORY_DECLARATION( ContextualProcessJobFactory )

View File

@ -9,11 +9,28 @@
# If the variable has that particular value, the corresponding
# value is executed as a shell command in the target environment.
#
# You can check for an empty value with "".
#
# The special configuration key *dontChroot* specifies whether
# the commands are run in the target system (default, value *false*),
# or in the host system. This key is not used for comparisons
# with global configuration values.
#
# If a command starts with "-" (a single minus sign), then the
# return value of the command following the - is ignored; otherwise,
# a failing command will abort the installation. This is much like
# make's use of - in a command.
#
# Global configuration variables are not checked in a deterministic
# order, so do not rely on commands from one variable-check to
# always happen before (or after) checks on another
# variable. Similarly, the value-equality checks are not
# done in a deterministic order, but all of the value-checks
# for a given variable happen together.
#
---
dontChroot: false
firmwareType:
efi: "-pkg remove efi-firmware"
bios: "-pkg remove bios-firmware"
"": "/bin/false no-firmware-type-set"

View File

@ -0,0 +1,28 @@
calamares_add_plugin( shellprocess
TYPE job
EXPORT_MACRO PLUGINDLLEXPORT_PRO
SOURCES
ShellProcessJob.cpp
LINK_PRIVATE_LIBRARIES
calamares
SHARED_LIB
)
find_package(ECM ${ECM_VERSION} NO_MODULE)
if( ECM_FOUND )
find_package( Qt5 COMPONENTS Test REQUIRED )
include( ECMAddTests )
ecm_add_test(
Tests.cpp
TEST_NAME
shellprocesstest
LINK_LIBRARIES
${CALAMARES_LIBRARIES}
calamaresui
${YAMLCPP_LIBRARY}
Qt5::Core
Qt5::Test
)
set_target_properties( shellprocesstest PROPERTIES AUTOMOC TRUE )
endif()

View File

@ -0,0 +1,85 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2018, Adriaan de Groot <groot@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ShellProcessJob.h"
#include <QProcess>
#include <QDateTime>
#include <QThread>
#include "CalamaresVersion.h"
#include "JobQueue.h"
#include "GlobalStorage.h"
#include "utils/CalamaresUtils.h"
#include "utils/CalamaresUtilsSystem.h"
#include "utils/CommandList.h"
#include "utils/Logger.h"
ShellProcessJob::ShellProcessJob( QObject* parent )
: Calamares::CppJob( parent )
, m_commands( nullptr )
, m_dontChroot( false )
{
}
ShellProcessJob::~ShellProcessJob()
{
delete m_commands;
m_commands = nullptr; // TODO: UniquePtr
}
QString
ShellProcessJob::prettyName() const
{
return tr( "Shell Processes Job" );
}
Calamares::JobResult
ShellProcessJob::exec()
{
if ( ! m_commands || m_commands->isEmpty() )
{
cDebug() << "WARNING: No commands to execute" << moduleInstanceKey();
return Calamares::JobResult::ok();
}
return m_commands->run( this );
}
void
ShellProcessJob::setConfigurationMap( const QVariantMap& configurationMap )
{
m_dontChroot = CalamaresUtils::getBool( configurationMap, "dontChroot", false );
if ( configurationMap.contains( "script" ) )
{
m_commands = new CalamaresUtils::CommandList( configurationMap.value( "script" ), !m_dontChroot );
if ( m_commands->isEmpty() )
cDebug() << "ShellProcessJob: \"script\" contains no commands for" << moduleInstanceKey();
}
else
cDebug() << "WARNING: No script given for ShellProcessJob" << moduleInstanceKey();
}
CALAMARES_PLUGIN_FACTORY_DEFINITION( ShellProcessJobFactory, registerPlugin<ShellProcessJob>(); )

View File

@ -0,0 +1,54 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2018, Adriaan de Groot <groot@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SHELLPROCESSJOB_H
#define SHELLPROCESSJOB_H
#include <QObject>
#include <QVariantMap>
#include <CppJob.h>
#include <utils/CommandList.h>
#include <utils/PluginFactory.h>
#include <PluginDllMacro.h>
class PLUGINDLLEXPORT ShellProcessJob : public Calamares::CppJob
{
Q_OBJECT
public:
explicit ShellProcessJob( QObject* parent = nullptr );
virtual ~ShellProcessJob() override;
QString prettyName() const override;
Calamares::JobResult exec() override;
void setConfigurationMap( const QVariantMap& configurationMap ) override;
private:
CalamaresUtils::CommandList* m_commands;
bool m_dontChroot;
};
CALAMARES_PLUGIN_FACTORY_DECLARATION( ShellProcessJobFactory )
#endif // SHELLPROCESSJOB_H

View File

@ -0,0 +1,115 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2017, Adriaan de Groot <groot@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Tests.h"
#include "utils/CommandList.h"
#include "utils/YamlUtils.h"
#include <yaml-cpp/yaml.h>
#include <QtTest/QtTest>
#include <QFileInfo>
#include <QStringList>
QTEST_GUILESS_MAIN( ShellProcessTests )
using CommandList = CalamaresUtils::CommandList;
ShellProcessTests::ShellProcessTests()
{
}
ShellProcessTests::~ShellProcessTests()
{
}
void
ShellProcessTests::initTestCase()
{
}
void
ShellProcessTests::testProcessListSampleConfig()
{
YAML::Node doc;
QStringList dirs { "src/modules/shellprocess", "." };
for ( const auto& dir : dirs )
{
QString filename = dir + "/shellprocess.conf";
if ( QFileInfo::exists( filename ) )
{
doc = YAML::LoadFile( filename.toStdString() );
break;
}
}
CommandList cl(
CalamaresUtils::yamlMapToVariant( doc ).toMap().value( "script" ) );
QVERIFY( !cl.isEmpty() );
QCOMPARE( cl.count(), 2 );
}
void ShellProcessTests::testProcessListFromList()
{
YAML::Node doc = YAML::Load( R"(---
script:
- "ls /tmp"
- "ls /nonexistent"
- "/bin/false"
)" );
CommandList cl(
CalamaresUtils::yamlMapToVariant( doc ).toMap().value( "script" ) );
QVERIFY( !cl.isEmpty() );
QCOMPARE( cl.count(), 3 );
// Contains 1 bad element
doc = YAML::Load( R"(---
script:
- "ls /tmp"
- false
- "ls /nonexistent"
)" );
CommandList cl1(
CalamaresUtils::yamlMapToVariant( doc ).toMap().value( "script" ) );
QVERIFY( !cl1.isEmpty() );
QCOMPARE( cl1.count(), 2 ); // One element ignored
}
void ShellProcessTests::testProcessListFromString()
{
YAML::Node doc = YAML::Load( R"(---
script: "ls /tmp"
)" );
CommandList cl(
CalamaresUtils::yamlMapToVariant( doc ).toMap().value( "script" ) );
QVERIFY( !cl.isEmpty() );
QCOMPARE( cl.count(), 1 );
// Not a string
doc = YAML::Load( R"(---
script: false
)" );
CommandList cl1(
CalamaresUtils::yamlMapToVariant( doc ).toMap().value( "script" ) );
QVERIFY( cl1.isEmpty() );
QCOMPARE( cl1.count(), 0 );
}

View File

@ -0,0 +1,41 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2018, Adriaan de Groot <groot@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TESTS_H
#define TESTS_H
#include <QObject>
class ShellProcessTests : public QObject
{
Q_OBJECT
public:
ShellProcessTests();
~ShellProcessTests() override;
private Q_SLOTS:
void initTestCase();
// Check the sample config file is processed correctly
void testProcessListSampleConfig();
// Create from a YAML list
void testProcessListFromList();
// Create from a simple YAML string
void testProcessListFromString();
};
#endif

View File

@ -0,0 +1,5 @@
---
type: "job"
name: "shellprocess"
interface: "qtplugin"
load: "libcalamares_job_shellprocess.so"

View File

@ -0,0 +1,19 @@
# Configuration for the shell process job.
#
# Executes a list of commands found under the key *script*.
# If the top-level key *dontChroot* is true, then the commands
# are executed in the context of the live system, otherwise
# in the context of the target system. In all of the commands,
# `@@ROOT@@` is replaced by the root mount point of the **target**
# system from the point of view of the command (for chrooted
# commands, that will be */*).
#
# If a command starts with "-" (a single minus sign), then the
# return value of the command following the - is ignored; otherwise,
# a failing command will abort the installation. This is much like
# make's use of - in a command.
---
dontChroot: false
script:
- "-touch @@ROOT@@/tmp/thingy"
- "/usr/bin/false"