[merge] upstream
This commit is contained in:
commit
3cc67489fa
96
.github/workflows/ci.yml
vendored
Normal file
96
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
name: ci
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- calamares
|
||||||
|
pull_request:
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
- reopened
|
||||||
|
- synchronize
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
env:
|
||||||
|
BUILDDIR: /build
|
||||||
|
SRCDIR: ${{ github.workspace }}
|
||||||
|
CMAKE_ARGS: |
|
||||||
|
-DWEBVIEW_FORCE_WEBKIT=1
|
||||||
|
-DKDE_INSTALL_USE_QT_SYS_PATHS=ON
|
||||||
|
-DWITH_PYTHONQT=OFF"
|
||||||
|
-DCMAKE_BUILD_TYPE=Debug
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: docker://kdeneon/plasma:user
|
||||||
|
options: --tmpfs /build:rw --user 0:0
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: prepare env
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get -y install git-core
|
||||||
|
-
|
||||||
|
name: checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
-
|
||||||
|
name: install dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get -y install \
|
||||||
|
build-essential \
|
||||||
|
cmake \
|
||||||
|
extra-cmake-modules \
|
||||||
|
gettext \
|
||||||
|
kio-dev \
|
||||||
|
libatasmart-dev \
|
||||||
|
libboost-python-dev \
|
||||||
|
libkf5config-dev \
|
||||||
|
libkf5coreaddons-dev \
|
||||||
|
libkf5i18n-dev \
|
||||||
|
libkf5iconthemes-dev \
|
||||||
|
libkf5parts-dev \
|
||||||
|
libkf5service-dev \
|
||||||
|
libkf5solid-dev \
|
||||||
|
libkpmcore-dev \
|
||||||
|
libparted-dev \
|
||||||
|
libpolkit-qt5-1-dev \
|
||||||
|
libqt5svg5-dev \
|
||||||
|
libqt5webkit5-dev \
|
||||||
|
libyaml-cpp-dev \
|
||||||
|
os-prober \
|
||||||
|
pkg-config \
|
||||||
|
python3-dev \
|
||||||
|
qtbase5-dev \
|
||||||
|
qtdeclarative5-dev \
|
||||||
|
qttools5-dev \
|
||||||
|
qttools5-dev-tools
|
||||||
|
-
|
||||||
|
name: prepare build
|
||||||
|
run: |
|
||||||
|
test -n "$BUILDDIR" || { echo "! \$BUILDDIR not set" ; exit 1 ; }
|
||||||
|
mkdir -p $BUILDDIR
|
||||||
|
test -f $SRCDIR/CMakeLists.txt || { echo "! Missing $SRCDIR/CMakeLists.txt" ; exit 1 ; }
|
||||||
|
-
|
||||||
|
name: cmake
|
||||||
|
working-directory: ${{ env.BUILDDIR }}
|
||||||
|
run: cmake $CMAKE_ARGS $SRCDIR
|
||||||
|
-
|
||||||
|
name: make
|
||||||
|
working-directory: ${{ env.BUILDDIR }}
|
||||||
|
run: make -j2 VERBOSE=1
|
||||||
|
-
|
||||||
|
name: install
|
||||||
|
working-directory: ${{ env.BUILDDIR }}
|
||||||
|
run: |
|
||||||
|
make install VERBOSE=1
|
||||||
|
-
|
||||||
|
name: notify
|
||||||
|
uses: rectalogic/notify-irc@v1
|
||||||
|
with:
|
||||||
|
server: chat.freenode.net
|
||||||
|
channel: "#calamares"
|
||||||
|
nickname: gh-notify
|
||||||
|
message: |
|
||||||
|
${{ github.actor }} pushed ${{ github.event.ref }} CI ${{ steps.install.conclusion }} JOB ${{ github.job }} RUN ${{ github.run_id }}
|
17
.github/workflows/issues.yml
vendored
Normal file
17
.github/workflows/issues.yml
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
name: issues
|
||||||
|
|
||||||
|
on: issues
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
irc:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: notify
|
||||||
|
uses: rectalogic/notify-irc@v1
|
||||||
|
with:
|
||||||
|
server: chat.freenode.net
|
||||||
|
channel: "#calamares"
|
||||||
|
nickname: gh-issues
|
||||||
|
message: |
|
||||||
|
${{ github.actor }} issue ${{ github.event.issue.title }}
|
@ -1,5 +0,0 @@
|
|||||||
# SPDX-FileCopyrightText: 2017 Rohan Garg <rohan@garg.io>
|
|
||||||
# SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
|
|
||||||
FROM kdeneon/all:user
|
|
||||||
RUN sudo apt-get update && sudo apt-get -y install build-essential cmake extra-cmake-modules gettext kio-dev libatasmart-dev libboost-python-dev libkf5config-dev libkf5coreaddons-dev libkf5i18n-dev libkf5iconthemes-dev libkf5parts-dev libkf5service-dev libkf5solid-dev libkpmcore-dev libparted-dev libpolkit-qt5-1-dev libqt5svg5-dev libqt5webkit5-dev libyaml-cpp-dev os-prober pkg-config python3-dev qtbase5-dev qtdeclarative5-dev qttools5-dev qttools5-dev-tools
|
|
@ -6,6 +6,7 @@
|
|||||||
---------
|
---------
|
||||||
|
|
||||||
[![GitHub release](https://img.shields.io/github/release/calamares/calamares.svg)](https://github.com/calamares/calamares/releases)
|
[![GitHub release](https://img.shields.io/github/release/calamares/calamares.svg)](https://github.com/calamares/calamares/releases)
|
||||||
|
[![GitHub Build Status](https://img.shields.io/github/workflow/status/calamares/calamares/ci?label=GH%20build)](https://github.com/calamares/calamares/actions?query=workflow%3Aci)
|
||||||
[![Travis Build Status](https://travis-ci.org/calamares/calamares.svg?branch=calamares)](https://travis-ci.org/calamares/calamares)
|
[![Travis Build Status](https://travis-ci.org/calamares/calamares.svg?branch=calamares)](https://travis-ci.org/calamares/calamares)
|
||||||
[![Coverity Scan Build Status](https://scan.coverity.com/projects/5389/badge.svg)](https://scan.coverity.com/projects/5389)
|
[![Coverity Scan Build Status](https://scan.coverity.com/projects/5389/badge.svg)](https://scan.coverity.com/projects/5389)
|
||||||
[![GitHub license](https://img.shields.io/github/license/calamares/calamares.svg)](https://github.com/calamares/calamares/blob/calamares/LICENSE)
|
[![GitHub license](https://img.shields.io/github/license/calamares/calamares.svg)](https://github.com/calamares/calamares/blob/calamares/LICENSE)
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
# Build configuration on Travis.
|
|
||||||
#
|
|
||||||
# SPDX-FileCopyrightText: 2018 Adriaan de Groot <groot@kde.org>
|
|
||||||
# SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
#
|
|
||||||
# Defines a CMAKE_ARGS variable for use with cmake
|
|
||||||
#
|
|
||||||
# This file is sourced by travis.sh, and exports the variables
|
|
||||||
# to the environment.
|
|
||||||
CMAKE_ARGS="\
|
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
|
||||||
-DWEBVIEW_FORCE_WEBKIT=1 \
|
|
||||||
-DKDE_INSTALL_USE_QT_SYS_PATHS=ON \
|
|
||||||
-DWITH_PYTHONQT=OFF"
|
|
||||||
|
|
||||||
export CMAKE_ARGS
|
|
@ -1,58 +0,0 @@
|
|||||||
#! /bin/sh
|
|
||||||
#
|
|
||||||
# SPDX-FileCopyrightText: 2017 Adriaan de Groot <groot@kde.org>
|
|
||||||
# SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
#
|
|
||||||
# Travis CI script for use on every-commit:
|
|
||||||
# - build and install Calamares
|
|
||||||
#
|
|
||||||
test -n "$BUILDDIR" || { echo "! \$BUILDDIR not set" ; exit 1 ; }
|
|
||||||
test -n "$SRCDIR" || { echo "! \$SRCDIR not set" ; exit 1 ; }
|
|
||||||
|
|
||||||
test -d $BUILDDIR || { echo "! $BUILDDIR not a directory" ; exit 1 ; }
|
|
||||||
test -d $SRCDIR || { echo "! $SRCDIR not a directory" ; exit 1 ; }
|
|
||||||
test -f $SRCDIR/CMakeLists.txt || { echo "! Missing $SRCDIR/CMakeLists.txt" ; exit 1 ; }
|
|
||||||
|
|
||||||
cd $BUILDDIR || exit 1
|
|
||||||
|
|
||||||
section() {
|
|
||||||
echo "###"
|
|
||||||
echo "### $1"
|
|
||||||
echo "###"
|
|
||||||
pwd -P
|
|
||||||
df -h
|
|
||||||
}
|
|
||||||
|
|
||||||
section "cmake $CMAKE_ARGS $SRCDIR"
|
|
||||||
cmake $CMAKE_ARGS $SRCDIR || { echo "! CMake failed" ; exit 1 ; }
|
|
||||||
|
|
||||||
section "make"
|
|
||||||
make -j2 VERBOSE=1 || { echo "! Make recheck" ; pwd -P ; df -h ; make -j1 VERBOSE=1 ; echo "! Make failed" ; exit 1 ; }
|
|
||||||
|
|
||||||
section "make install"
|
|
||||||
|
|
||||||
install_debugging() {
|
|
||||||
ls -la $( find "$1" -type f -name '*.so' )
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "# Build results"
|
|
||||||
install_debugging "$BUILDDIR"
|
|
||||||
|
|
||||||
echo "# Install"
|
|
||||||
DESTDIR=/build/INSTALL_ROOT
|
|
||||||
mkdir -p "$DESTDIR"
|
|
||||||
|
|
||||||
if make install VERBOSE=1 DESTDIR="$DESTDIR" ;
|
|
||||||
then
|
|
||||||
echo "# .. install OK"
|
|
||||||
result=true
|
|
||||||
else
|
|
||||||
echo "# .. install failed"
|
|
||||||
result=false
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
section "Install results"
|
|
||||||
install_debugging "$DESTDIR"
|
|
||||||
|
|
||||||
$result || { echo "! Install failed" ; exit 1 ; } # Result of make install, above
|
|
@ -1,37 +0,0 @@
|
|||||||
#! /bin/sh
|
|
||||||
#
|
|
||||||
# SPDX-FileCopyrightText: 2017 Adriaan de Groot <groot@kde.org>
|
|
||||||
# SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
#
|
|
||||||
# Travis CI script for weekly (cron) use:
|
|
||||||
# - use the coverity tool to build and and upload results
|
|
||||||
#
|
|
||||||
test -n "$COVERITY_SCAN_TOKEN" || { echo "! Missing Coverity token" ; exit 1 ; }
|
|
||||||
test -n "$BUILDDIR" || { echo "! \$BUILDDIR not set" ; exit 1 ; }
|
|
||||||
test -n "$SRCDIR" || { echo "! \$SRCDIR not set" ; exit 1 ; }
|
|
||||||
|
|
||||||
test -d $BUILDDIR || { echo "! $BUILDDIR not a directory" ; exit 1 ; }
|
|
||||||
test -d $SRCDIR || { echo "! $SRCDIR not a directory" ; exit 1 ; }
|
|
||||||
test -f $SRCDIR/CMakeLists.txt || { echo "! Missing $SRCDIR/CMakeLists.txt" ; exit 1 ; }
|
|
||||||
|
|
||||||
cd $BUILDDIR || exit 1
|
|
||||||
|
|
||||||
curl -k -o coverity_tool.tar.gz \
|
|
||||||
-d "token=$COVERITY_SCAN_TOKEN&project=calamares%2Fcalamares" \
|
|
||||||
https://scan.coverity.com/download/cxx/linux64 || exit 1
|
|
||||||
mkdir "$BUILDDIR/coveritytool"
|
|
||||||
tar xvf coverity_tool.tar.gz -C "$BUILDDIR/coveritytool" --strip-components 2
|
|
||||||
export PATH="$BUILDDIR/coveritytool/bin:$PATH"
|
|
||||||
|
|
||||||
echo "# cmake -DCMAKE_BUILD_TYPE=Debug $CMAKE_ARGS $SRCDIR"
|
|
||||||
cmake -DCMAKE_BUILD_TYPE=Debug $CMAKE_ARGS $SRCDIR || exit 1
|
|
||||||
cov-build --dir cov-int make -j2
|
|
||||||
|
|
||||||
tar caf calamares-ci.tar.xz cov-int
|
|
||||||
|
|
||||||
curl -k --form token=$COVERITY_SCAN_TOKEN \
|
|
||||||
--form email=groot@kde.org \
|
|
||||||
--form file=@calamares-ci.tar.xz \
|
|
||||||
--form version="calamares-`date -u +%Y%m%d`" \
|
|
||||||
--form description="calamares on `date -u`" \
|
|
||||||
https://scan.coverity.com/builds?project=calamares%2Fcalamares
|
|
24
ci/travis.sh
24
ci/travis.sh
@ -1,24 +0,0 @@
|
|||||||
#! /bin/sh
|
|
||||||
#
|
|
||||||
# SPDX-FileCopyrightText: 2017 Adriaan de Groot <groot@kde.org>
|
|
||||||
# SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
#
|
|
||||||
# Travis build driver script:
|
|
||||||
# - the regular CI runs, triggered by commits, run a script that builds
|
|
||||||
# and installs calamares, and then runs the tests.
|
|
||||||
# - the cronjob CI runs, triggered weekly, run a script that uses the
|
|
||||||
# coverity tools to submit a build. This is slightly more resource-
|
|
||||||
# intensive than the coverity add-on, but works on master.
|
|
||||||
#
|
|
||||||
D=`dirname "$0"`
|
|
||||||
test -d "$D" || { echo "! No directory $D" ; exit 1 ; }
|
|
||||||
test -x "$D/travis-continuous.sh" || { echo "! Missing -continuous" ; exit 1 ; }
|
|
||||||
test -x "$D/travis-coverity.sh" || { echo "! Missing -coverity" ; exit 1 ; }
|
|
||||||
|
|
||||||
test -f "$D/travis-config.sh" && . "$D/travis-config.sh"
|
|
||||||
|
|
||||||
if test "$TRAVIS_EVENT_TYPE" = "cron" ; then
|
|
||||||
exec "$D/travis-coverity.sh"
|
|
||||||
else
|
|
||||||
exec "$D/travis-continuous.sh"
|
|
||||||
fi
|
|
@ -261,12 +261,12 @@ PythonJob::exec()
|
|||||||
{
|
{
|
||||||
m_description.truncate( i_newline );
|
m_description.truncate( i_newline );
|
||||||
}
|
}
|
||||||
cDebug() << "Job description from __doc__" << prettyName() << '=' << m_description;
|
cDebug() << Logger::SubEntry << "Job description from __doc__" << prettyName() << '=' << m_description;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cDebug() << "Job description from pretty_name" << prettyName() << '=' << m_description;
|
cDebug() << Logger::SubEntry << "Job description from pretty_name" << prettyName() << '=' << m_description;
|
||||||
}
|
}
|
||||||
emit progress( 0 );
|
emit progress( 0 );
|
||||||
|
|
||||||
|
@ -188,7 +188,7 @@ System::runCommand( System::RunLocation location,
|
|||||||
: -1 ) )
|
: -1 ) )
|
||||||
{
|
{
|
||||||
cWarning() << "Process" << args.first() << "timed out after" << timeoutSec.count() << "s. Output so far:\n"
|
cWarning() << "Process" << args.first() << "timed out after" << timeoutSec.count() << "s. Output so far:\n"
|
||||||
<< Logger::NoQuote {} << process.readAllStandardOutput();
|
<< Logger::NoQuote << process.readAllStandardOutput();
|
||||||
return ProcessResult::Code::TimedOut;
|
return ProcessResult::Code::TimedOut;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,7 +196,7 @@ System::runCommand( System::RunLocation location,
|
|||||||
|
|
||||||
if ( process.exitStatus() == QProcess::CrashExit )
|
if ( process.exitStatus() == QProcess::CrashExit )
|
||||||
{
|
{
|
||||||
cWarning() << "Process" << args.first() << "crashed. Output so far:\n" << Logger::NoQuote {} << output;
|
cWarning() << "Process" << args.first() << "crashed. Output so far:\n" << Logger::NoQuote << output;
|
||||||
return ProcessResult::Code::Crashed;
|
return ProcessResult::Code::Crashed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,7 +206,7 @@ System::runCommand( System::RunLocation location,
|
|||||||
{
|
{
|
||||||
if ( showDebug && !output.isEmpty() )
|
if ( showDebug && !output.isEmpty() )
|
||||||
{
|
{
|
||||||
cDebug() << Logger::SubEntry << "Finished. Exit code:" << r << "output:\n" << Logger::NoQuote {} << output;
|
cDebug() << Logger::SubEntry << "Finished. Exit code:" << r << "output:\n" << Logger::NoQuote << output;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -218,7 +218,7 @@ System::runCommand( System::RunLocation location,
|
|||||||
if ( !output.isEmpty() )
|
if ( !output.isEmpty() )
|
||||||
{
|
{
|
||||||
cDebug() << Logger::SubEntry << "Target cmd:" << RedactedList( args ) << "Exit code:" << r << "output:\n"
|
cDebug() << Logger::SubEntry << "Target cmd:" << RedactedList( args ) << "Exit code:" << r << "output:\n"
|
||||||
<< Logger::NoQuote {} << output;
|
<< Logger::NoQuote << output;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -207,6 +207,8 @@ constexpr FuncSuppressor::FuncSuppressor( const char s[] )
|
|||||||
|
|
||||||
const constexpr FuncSuppressor Continuation( s_Continuation );
|
const constexpr FuncSuppressor Continuation( s_Continuation );
|
||||||
const constexpr FuncSuppressor SubEntry( s_SubEntry );
|
const constexpr FuncSuppressor SubEntry( s_SubEntry );
|
||||||
|
const constexpr NoQuote_t NoQuote {};
|
||||||
|
const constexpr Quote_t Quote {};
|
||||||
|
|
||||||
QString
|
QString
|
||||||
toString( const QVariant& v )
|
toString( const QVariant& v )
|
||||||
|
@ -25,15 +25,17 @@ struct FuncSuppressor
|
|||||||
const char* m_s;
|
const char* m_s;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct NoQuote
|
struct NoQuote_t
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
struct Quote
|
struct Quote_t
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|
||||||
DLLEXPORT extern const FuncSuppressor Continuation;
|
DLLEXPORT extern const FuncSuppressor Continuation;
|
||||||
DLLEXPORT extern const FuncSuppressor SubEntry;
|
DLLEXPORT extern const FuncSuppressor SubEntry;
|
||||||
|
DLLEXPORT extern const NoQuote_t NoQuote;
|
||||||
|
DLLEXPORT extern const Quote_t Quote;
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
@ -74,13 +76,13 @@ operator<<( QDebug& s, const FuncSuppressor& f )
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline QDebug&
|
inline QDebug&
|
||||||
operator<<( QDebug& s, const NoQuote& )
|
operator<<( QDebug& s, const NoQuote_t& )
|
||||||
{
|
{
|
||||||
return s.noquote().nospace();
|
return s.noquote().nospace();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline QDebug&
|
inline QDebug&
|
||||||
operator<<( QDebug& s, const Quote& )
|
operator<<( QDebug& s, const Quote_t& )
|
||||||
{
|
{
|
||||||
return s.quote().space();
|
return s.quote().space();
|
||||||
}
|
}
|
||||||
@ -254,7 +256,7 @@ operator<<( QDebug& s, const DebugMap& t )
|
|||||||
inline QDebug&
|
inline QDebug&
|
||||||
operator<<( QDebug& s, const Pointer& p )
|
operator<<( QDebug& s, const Pointer& p )
|
||||||
{
|
{
|
||||||
s << NoQuote {} << '@' << p.ptr << Quote {};
|
s << NoQuote << '@' << p.ptr << Quote;
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
} // namespace Logger
|
} // namespace Logger
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "String.h"
|
#include "String.h"
|
||||||
|
#include "Logger.h"
|
||||||
|
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
|
||||||
@ -121,4 +122,61 @@ obscure( const QString& string )
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QString
|
||||||
|
truncateMultiLine( const QString& string, CalamaresUtils::LinesStartEnd lines, CalamaresUtils::CharCount chars )
|
||||||
|
{
|
||||||
|
const char NEWLINE = '\n';
|
||||||
|
const int maxLines = lines.atStart + lines.atEnd;
|
||||||
|
if ( maxLines < 1 )
|
||||||
|
{
|
||||||
|
QString shorter( string );
|
||||||
|
shorter.truncate( chars.total );
|
||||||
|
return shorter;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int linesInString = string.count( NEWLINE ) + ( string.endsWith( NEWLINE ) ? 0 : 1 );
|
||||||
|
if ( ( string.length() <= chars.total ) && ( linesInString <= maxLines ) )
|
||||||
|
{
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString front, back;
|
||||||
|
if ( string.count( NEWLINE ) >= maxLines )
|
||||||
|
{
|
||||||
|
int from = -1;
|
||||||
|
for ( int i = 0; i < lines.atStart; ++i )
|
||||||
|
{
|
||||||
|
from = string.indexOf( NEWLINE, from + 1 );
|
||||||
|
if ( from < 0 )
|
||||||
|
{
|
||||||
|
// That's strange, we counted at least maxLines newlines before
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( from > 0 )
|
||||||
|
{
|
||||||
|
front = string.left( from + 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
int lastNewLine = -1;
|
||||||
|
int lastCount = string.endsWith( NEWLINE ) ? -1 : 0;
|
||||||
|
for ( auto i = string.rbegin(); i != string.rend() && lastCount < lines.atEnd; ++i )
|
||||||
|
{
|
||||||
|
if ( *i == NEWLINE )
|
||||||
|
{
|
||||||
|
++lastCount;
|
||||||
|
lastNewLine = int( i - string.rbegin() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( ( lastNewLine >= 0 ) && ( lastCount >= lines.atEnd ) )
|
||||||
|
{
|
||||||
|
back = string.right( lastNewLine );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return front + back;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace CalamaresUtils
|
} // namespace CalamaresUtils
|
||||||
|
@ -61,6 +61,43 @@ DLLEXPORT QString removeDiacritics( const QString& string );
|
|||||||
* @return the obfuscated string.
|
* @return the obfuscated string.
|
||||||
*/
|
*/
|
||||||
DLLEXPORT QString obscure( const QString& string );
|
DLLEXPORT QString obscure( const QString& string );
|
||||||
|
|
||||||
|
/** @brief Parameter for counting lines at beginning and end of string
|
||||||
|
*
|
||||||
|
* This is used by truncateMultiLine() to indicate how many lines from
|
||||||
|
* the beginning and how many from the end should be kept.
|
||||||
|
*/
|
||||||
|
struct LinesStartEnd
|
||||||
|
{
|
||||||
|
int atStart = 0;
|
||||||
|
int atEnd = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @brief Parameter for counting characters in truncateMultiLine()
|
||||||
|
*/
|
||||||
|
struct CharCount
|
||||||
|
{
|
||||||
|
int total = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @brief Truncate a string to some reasonable length for display
|
||||||
|
*
|
||||||
|
* Keep the first few, or last few (or both) lines of a possibly lengthy
|
||||||
|
* message @p string and reduce it to a displayable size (e.g. for
|
||||||
|
* pop-up windows that display the message). If the message is longer
|
||||||
|
* than @p chars, then characters are removed from the front (if
|
||||||
|
* @p lines.atStart is zero) or end (if @p lines.atEnd is zero) or in the middle
|
||||||
|
* (if both are nonzero).
|
||||||
|
*
|
||||||
|
* @param string the input string.
|
||||||
|
* @param lines number of lines to preserve.
|
||||||
|
* @param chars maximum number of characters in the returned string.
|
||||||
|
* @return a string built from parts of the input string.
|
||||||
|
*/
|
||||||
|
DLLEXPORT QString truncateMultiLine( const QString& string,
|
||||||
|
LinesStartEnd lines = LinesStartEnd { 3, 5 },
|
||||||
|
CharCount chars = CharCount { 812 } );
|
||||||
|
|
||||||
} // namespace CalamaresUtils
|
} // namespace CalamaresUtils
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include "Entropy.h"
|
#include "Entropy.h"
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
#include "RAII.h"
|
#include "RAII.h"
|
||||||
|
#include "String.h"
|
||||||
#include "Traits.h"
|
#include "Traits.h"
|
||||||
#include "UMask.h"
|
#include "UMask.h"
|
||||||
#include "Variant.h"
|
#include "Variant.h"
|
||||||
@ -63,6 +64,9 @@ private Q_SLOTS:
|
|||||||
void testVariantStringListYAMLDashed();
|
void testVariantStringListYAMLDashed();
|
||||||
void testVariantStringListYAMLBracketed();
|
void testVariantStringListYAMLBracketed();
|
||||||
|
|
||||||
|
/** @brief Test smart string truncation. */
|
||||||
|
void testStringTruncation();
|
||||||
|
void testStringTruncationShorter();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void recursiveCompareMap( const QVariantMap& a, const QVariantMap& b, int depth );
|
void recursiveCompareMap( const QVariantMap& a, const QVariantMap& b, int depth );
|
||||||
@ -495,6 +499,141 @@ strings: [ aap, noot, mies ]
|
|||||||
QVERIFY( !getStringList( m, key ).contains( "lam" ) );
|
QVERIFY( !getStringList( m, key ).contains( "lam" ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LibCalamaresTests::testStringTruncation()
|
||||||
|
{
|
||||||
|
Logger::setupLogLevel( Logger::LOGDEBUG );
|
||||||
|
|
||||||
|
using namespace CalamaresUtils;
|
||||||
|
|
||||||
|
const QString longString( R"(---
|
||||||
|
--- src/libcalamares/utils/String.h
|
||||||
|
+++ src/libcalamares/utils/String.h
|
||||||
|
@@ -62,15 +62,22 @@ DLLEXPORT QString removeDiacritics( const QString& string );
|
||||||
|
*/
|
||||||
|
DLLEXPORT QString obscure( const QString& string );
|
||||||
|
|
||||||
|
+/** @brief Parameter for counting lines at beginning and end of string
|
||||||
|
+ *
|
||||||
|
+ * This is used by truncateMultiLine() to indicate how many lines from
|
||||||
|
+ * the beginning and how many from the end should be kept.
|
||||||
|
+ */
|
||||||
|
struct LinesStartEnd
|
||||||
|
{
|
||||||
|
- int atStart;
|
||||||
|
- int atEnd;
|
||||||
|
+ int atStart = 0;
|
||||||
|
+ int atEnd = 0;
|
||||||
|
)" );
|
||||||
|
|
||||||
|
const int sufficientLength = 812;
|
||||||
|
// There's 18 lines in all
|
||||||
|
QCOMPARE( longString.count( '\n' ), 18 );
|
||||||
|
QVERIFY( longString.length() < sufficientLength );
|
||||||
|
|
||||||
|
// If we ask for more, we get everything back
|
||||||
|
QCOMPARE( longString, truncateMultiLine( longString, LinesStartEnd { 20, 0 }, CharCount { sufficientLength } ) );
|
||||||
|
QCOMPARE( longString, truncateMultiLine( longString, LinesStartEnd { 0, 20 }, CharCount { sufficientLength } ) );
|
||||||
|
|
||||||
|
// If we ask for no lines, only characters, we get that
|
||||||
|
{
|
||||||
|
auto s = truncateMultiLine( longString, LinesStartEnd { 0, 0 }, CharCount { 4 } );
|
||||||
|
QCOMPARE( s.length(), 4 );
|
||||||
|
QCOMPARE( s, QString( "---\n" ) );
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto s = truncateMultiLine( longString, LinesStartEnd { 0, 0 }, CharCount { sufficientLength } );
|
||||||
|
QCOMPARE( s, longString );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lines at the start
|
||||||
|
{
|
||||||
|
auto s = truncateMultiLine( longString, LinesStartEnd { 4, 0 }, CharCount { sufficientLength } );
|
||||||
|
QVERIFY( s.length() > 1 );
|
||||||
|
QVERIFY( longString.startsWith( s ) );
|
||||||
|
cDebug() << "Result-line" << Logger::Quote << s;
|
||||||
|
QCOMPARE( s.count( '\n' ), 4 );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lines at the end
|
||||||
|
{
|
||||||
|
auto s = truncateMultiLine( longString, LinesStartEnd { 0, 4 }, CharCount { sufficientLength } );
|
||||||
|
QVERIFY( s.length() > 1 );
|
||||||
|
QVERIFY( longString.endsWith( s ) );
|
||||||
|
cDebug() << "Result-line" << Logger::Quote << s;
|
||||||
|
QCOMPARE( s.count( '\n' ), 4 );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lines at both ends
|
||||||
|
{
|
||||||
|
auto s = truncateMultiLine( longString, LinesStartEnd { 2, 2 }, CharCount { sufficientLength } );
|
||||||
|
QVERIFY( s.length() > 1 );
|
||||||
|
cDebug() << "Result-line" << Logger::Quote << s;
|
||||||
|
QCOMPARE( s.count( '\n' ), 4 );
|
||||||
|
|
||||||
|
auto firsttwo = truncateMultiLine( s, LinesStartEnd { 2, 0 }, CharCount { sufficientLength } );
|
||||||
|
auto lasttwo = truncateMultiLine( s, LinesStartEnd { 0, 2 }, CharCount { sufficientLength } );
|
||||||
|
QCOMPARE( firsttwo + lasttwo, s );
|
||||||
|
QCOMPARE( firsttwo.count( '\n' ), 2 );
|
||||||
|
QVERIFY( longString.startsWith( firsttwo ) );
|
||||||
|
QVERIFY( longString.endsWith( lasttwo ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LibCalamaresTests::testStringTruncationShorter()
|
||||||
|
{
|
||||||
|
Logger::setupLogLevel( Logger::LOGDEBUG );
|
||||||
|
|
||||||
|
using namespace CalamaresUtils;
|
||||||
|
|
||||||
|
const QString longString( R"(Some strange string artifacts appeared, leading to `{1?}` being
|
||||||
|
displayed in various user-facing messages. These have been removed
|
||||||
|
and the translations updated.)" );
|
||||||
|
const char NEWLINE = '\n';
|
||||||
|
|
||||||
|
const int insufficientLength = 42;
|
||||||
|
// There's 2 newlines in all, no trailing newline
|
||||||
|
QVERIFY( !longString.endsWith( NEWLINE ) );
|
||||||
|
QCOMPARE( longString.count( NEWLINE ), 2 );
|
||||||
|
QVERIFY( longString.length() > insufficientLength );
|
||||||
|
|
||||||
|
// Grab first line, untruncated
|
||||||
|
{
|
||||||
|
auto s = truncateMultiLine( longString, LinesStartEnd { 1, 0 } );
|
||||||
|
QVERIFY( s.length() > 1 );
|
||||||
|
QVERIFY( longString.startsWith( s ) );
|
||||||
|
QVERIFY( s.endsWith( NEWLINE ) );
|
||||||
|
QVERIFY( s.endsWith( "being\n" ) );
|
||||||
|
QVERIFY( s.startsWith( "Some " ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grab last line, untruncated
|
||||||
|
{
|
||||||
|
auto s = truncateMultiLine( longString, LinesStartEnd { 0, 1 } );
|
||||||
|
QVERIFY( s.length() > 1 );
|
||||||
|
QVERIFY( longString.endsWith( s ) );
|
||||||
|
QVERIFY( !s.endsWith( NEWLINE ) );
|
||||||
|
QVERIFY( s.endsWith( "updated." ) );
|
||||||
|
QCOMPARE( s.count( NEWLINE ), 0 ); // Because last line doesn't end with a newline
|
||||||
|
QVERIFY( s.startsWith( "and the " ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grab last two lines, untruncated
|
||||||
|
{
|
||||||
|
auto s = truncateMultiLine( longString, LinesStartEnd { 0, 2 } );
|
||||||
|
QVERIFY( s.length() > 1 );
|
||||||
|
QVERIFY( longString.endsWith( s ) );
|
||||||
|
QVERIFY( !s.endsWith( NEWLINE ) );
|
||||||
|
QVERIFY( s.endsWith( "updated." ) );
|
||||||
|
cDebug() << "Result-line" << Logger::Quote << s;
|
||||||
|
QCOMPARE( s.count( NEWLINE ), 1 ); // Because last line doesn't end with a newline
|
||||||
|
QVERIFY( s.startsWith( "displayed in " ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
QTEST_GUILESS_MAIN( LibCalamaresTests )
|
QTEST_GUILESS_MAIN( LibCalamaresTests )
|
||||||
|
|
||||||
#include "utils/moc-warnings.h"
|
#include "utils/moc-warnings.h"
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
[[noreturn]] static void
|
[[noreturn]] static void
|
||||||
bail( const QString& descriptorPath, const QString& message )
|
bail( const QString& descriptorPath, const QString& message )
|
||||||
{
|
{
|
||||||
cError() << "FATAL in" << descriptorPath << Logger::Continuation << Logger::NoQuote {} << message;
|
cError() << "FATAL in" << descriptorPath << Logger::Continuation << Logger::NoQuote << message;
|
||||||
::exit( EXIT_FAILURE );
|
::exit( EXIT_FAILURE );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "utils/Logger.h"
|
#include "utils/Logger.h"
|
||||||
#include "utils/Paste.h"
|
#include "utils/Paste.h"
|
||||||
#include "utils/Retranslator.h"
|
#include "utils/Retranslator.h"
|
||||||
|
#include "utils/String.h"
|
||||||
#include "viewpages/BlankViewStep.h"
|
#include "viewpages/BlankViewStep.h"
|
||||||
#include "viewpages/ExecutionViewStep.h"
|
#include "viewpages/ExecutionViewStep.h"
|
||||||
#include "viewpages/ViewStep.h"
|
#include "viewpages/ViewStep.h"
|
||||||
@ -30,10 +31,11 @@
|
|||||||
#include <QMetaObject>
|
#include <QMetaObject>
|
||||||
|
|
||||||
#define UPDATE_BUTTON_PROPERTY( name, value ) \
|
#define UPDATE_BUTTON_PROPERTY( name, value ) \
|
||||||
|
do \
|
||||||
{ \
|
{ \
|
||||||
m_##name = value; \
|
m_##name = value; \
|
||||||
emit name##Changed( m_##name ); \
|
emit name##Changed( m_##name ); \
|
||||||
}
|
} while ( false )
|
||||||
|
|
||||||
namespace Calamares
|
namespace Calamares
|
||||||
{
|
{
|
||||||
@ -136,15 +138,14 @@ ViewManager::insertViewStep( int before, ViewStep* step )
|
|||||||
emit endInsertRows();
|
emit endInsertRows();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
ViewManager::onInstallationFailed( const QString& message, const QString& details )
|
ViewManager::onInstallationFailed( const QString& message, const QString& details )
|
||||||
{
|
{
|
||||||
bool shouldOfferWebPaste = false; // TODO: config var
|
bool shouldOfferWebPaste = false; // TODO: config var
|
||||||
|
|
||||||
cError() << "Installation failed:";
|
cError() << "Installation failed:" << message;
|
||||||
cDebug() << "- message:" << message;
|
cDebug() << Logger::SubEntry << "- message:" << message;
|
||||||
cDebug() << "- details:" << details;
|
cDebug() << Logger::SubEntry << "- details:" << Logger::NoQuote << details;
|
||||||
|
|
||||||
QString heading
|
QString heading
|
||||||
= Calamares::Settings::instance()->isSetupMode() ? tr( "Setup Failed" ) : tr( "Installation Failed" );
|
= Calamares::Settings::instance()->isSetupMode() ? tr( "Setup Failed" ) : tr( "Installation Failed" );
|
||||||
@ -152,7 +153,7 @@ ViewManager::onInstallationFailed( const QString& message, const QString& detail
|
|||||||
QString text = "<p>" + message + "</p>";
|
QString text = "<p>" + message + "</p>";
|
||||||
if ( !details.isEmpty() )
|
if ( !details.isEmpty() )
|
||||||
{
|
{
|
||||||
text += "<p>" + details + "</p>";
|
text += "<p>" + CalamaresUtils::truncateMultiLine( details, CalamaresUtils::LinesStartEnd { 8, 0 } ) + "</p>";
|
||||||
}
|
}
|
||||||
if ( shouldOfferWebPaste )
|
if ( shouldOfferWebPaste )
|
||||||
{
|
{
|
||||||
@ -365,8 +366,8 @@ ViewManager::next()
|
|||||||
{
|
{
|
||||||
// Reached the end in a weird state (e.g. no finished step after an exec)
|
// Reached the end in a weird state (e.g. no finished step after an exec)
|
||||||
executing = false;
|
executing = false;
|
||||||
UPDATE_BUTTON_PROPERTY( nextEnabled, false )
|
UPDATE_BUTTON_PROPERTY( nextEnabled, false );
|
||||||
UPDATE_BUTTON_PROPERTY( backEnabled, false )
|
UPDATE_BUTTON_PROPERTY( backEnabled, false );
|
||||||
}
|
}
|
||||||
updateCancelEnabled( !settings->disableCancel() && !( executing && settings->disableCancelDuringExec() ) );
|
updateCancelEnabled( !settings->disableCancel() && !( executing && settings->disableCancelDuringExec() ) );
|
||||||
updateBackAndNextVisibility( !( executing && settings->hideBackAndNextDuringExec() ) );
|
updateBackAndNextVisibility( !( executing && settings->hideBackAndNextDuringExec() ) );
|
||||||
@ -378,8 +379,8 @@ ViewManager::next()
|
|||||||
|
|
||||||
if ( m_currentStep < m_steps.count() )
|
if ( m_currentStep < m_steps.count() )
|
||||||
{
|
{
|
||||||
UPDATE_BUTTON_PROPERTY( nextEnabled, !executing && m_steps.at( m_currentStep )->isNextEnabled() )
|
UPDATE_BUTTON_PROPERTY( nextEnabled, !executing && m_steps.at( m_currentStep )->isNextEnabled() );
|
||||||
UPDATE_BUTTON_PROPERTY( backEnabled, !executing && m_steps.at( m_currentStep )->isBackEnabled() )
|
UPDATE_BUTTON_PROPERTY( backEnabled, !executing && m_steps.at( m_currentStep )->isBackEnabled() );
|
||||||
}
|
}
|
||||||
|
|
||||||
updateButtonLabels();
|
updateButtonLabels();
|
||||||
@ -401,26 +402,26 @@ ViewManager::updateButtonLabels()
|
|||||||
// If we're going into the execution step / install phase, other message
|
// If we're going into the execution step / install phase, other message
|
||||||
if ( stepIsExecute( m_steps, m_currentStep + 1 ) )
|
if ( stepIsExecute( m_steps, m_currentStep + 1 ) )
|
||||||
{
|
{
|
||||||
UPDATE_BUTTON_PROPERTY( nextLabel, nextIsInstallationStep )
|
UPDATE_BUTTON_PROPERTY( nextLabel, nextIsInstallationStep );
|
||||||
UPDATE_BUTTON_PROPERTY( nextIcon, "run-install" )
|
UPDATE_BUTTON_PROPERTY( nextIcon, "run-install" );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
UPDATE_BUTTON_PROPERTY( nextLabel, tr( "&Next" ) )
|
UPDATE_BUTTON_PROPERTY( nextLabel, tr( "&Next" ) );
|
||||||
UPDATE_BUTTON_PROPERTY( nextIcon, "go-next" )
|
UPDATE_BUTTON_PROPERTY( nextIcon, "go-next" );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Going back is always simple
|
// Going back is always simple
|
||||||
UPDATE_BUTTON_PROPERTY( backLabel, tr( "&Back" ) )
|
UPDATE_BUTTON_PROPERTY( backLabel, tr( "&Back" ) );
|
||||||
UPDATE_BUTTON_PROPERTY( backIcon, "go-previous" )
|
UPDATE_BUTTON_PROPERTY( backIcon, "go-previous" );
|
||||||
|
|
||||||
// Cancel button changes label at the end
|
// Cancel button changes label at the end
|
||||||
if ( isAtVeryEnd( m_steps, m_currentStep ) )
|
if ( isAtVeryEnd( m_steps, m_currentStep ) )
|
||||||
{
|
{
|
||||||
UPDATE_BUTTON_PROPERTY( quitLabel, tr( "&Done" ) )
|
UPDATE_BUTTON_PROPERTY( quitLabel, tr( "&Done" ) );
|
||||||
UPDATE_BUTTON_PROPERTY( quitTooltip, quitOnCompleteTooltip )
|
UPDATE_BUTTON_PROPERTY( quitTooltip, quitOnCompleteTooltip );
|
||||||
UPDATE_BUTTON_PROPERTY( quitVisible, true )
|
UPDATE_BUTTON_PROPERTY( quitVisible, true );
|
||||||
UPDATE_BUTTON_PROPERTY( quitIcon, "dialog-ok-apply" )
|
UPDATE_BUTTON_PROPERTY( quitIcon, "dialog-ok-apply" );
|
||||||
updateCancelEnabled( true );
|
updateCancelEnabled( true );
|
||||||
if ( settings->quitAtEnd() )
|
if ( settings->quitAtEnd() )
|
||||||
{
|
{
|
||||||
@ -431,14 +432,14 @@ ViewManager::updateButtonLabels()
|
|||||||
{
|
{
|
||||||
if ( settings->disableCancel() )
|
if ( settings->disableCancel() )
|
||||||
{
|
{
|
||||||
UPDATE_BUTTON_PROPERTY( quitVisible, false )
|
UPDATE_BUTTON_PROPERTY( quitVisible, false );
|
||||||
}
|
}
|
||||||
updateCancelEnabled( !settings->disableCancel()
|
updateCancelEnabled( !settings->disableCancel()
|
||||||
&& !( stepIsExecute( m_steps, m_currentStep ) && settings->disableCancelDuringExec() ) );
|
&& !( stepIsExecute( m_steps, m_currentStep ) && settings->disableCancelDuringExec() ) );
|
||||||
|
|
||||||
UPDATE_BUTTON_PROPERTY( quitLabel, tr( "&Cancel" ) )
|
UPDATE_BUTTON_PROPERTY( quitLabel, tr( "&Cancel" ) );
|
||||||
UPDATE_BUTTON_PROPERTY( quitTooltip, cancelBeforeInstallationTooltip )
|
UPDATE_BUTTON_PROPERTY( quitTooltip, cancelBeforeInstallationTooltip );
|
||||||
UPDATE_BUTTON_PROPERTY( quitIcon, "dialog-cancel" )
|
UPDATE_BUTTON_PROPERTY( quitIcon, "dialog-cancel" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -468,11 +469,11 @@ ViewManager::back()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
UPDATE_BUTTON_PROPERTY( nextEnabled, m_steps.at( m_currentStep )->isNextEnabled() )
|
UPDATE_BUTTON_PROPERTY( nextEnabled, m_steps.at( m_currentStep )->isNextEnabled() );
|
||||||
UPDATE_BUTTON_PROPERTY( backEnabled,
|
UPDATE_BUTTON_PROPERTY( backEnabled,
|
||||||
( m_currentStep == 0 && m_steps.first()->isAtBeginning() )
|
( m_currentStep == 0 && m_steps.first()->isAtBeginning() )
|
||||||
? false
|
? false
|
||||||
: m_steps.at( m_currentStep )->isBackEnabled() )
|
: m_steps.at( m_currentStep )->isBackEnabled() );
|
||||||
|
|
||||||
updateButtonLabels();
|
updateButtonLabels();
|
||||||
}
|
}
|
||||||
@ -525,14 +526,14 @@ ViewManager::confirmCancelInstallation()
|
|||||||
void
|
void
|
||||||
ViewManager::updateCancelEnabled( bool enabled )
|
ViewManager::updateCancelEnabled( bool enabled )
|
||||||
{
|
{
|
||||||
UPDATE_BUTTON_PROPERTY( quitEnabled, enabled )
|
UPDATE_BUTTON_PROPERTY( quitEnabled, enabled );
|
||||||
emit cancelEnabled( enabled );
|
emit cancelEnabled( enabled );
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ViewManager::updateBackAndNextVisibility( bool visible)
|
ViewManager::updateBackAndNextVisibility( bool visible )
|
||||||
{
|
{
|
||||||
UPDATE_BUTTON_PROPERTY( backAndNextVisible, visible )
|
UPDATE_BUTTON_PROPERTY( backAndNextVisible, visible );
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant
|
QVariant
|
||||||
|
@ -183,7 +183,7 @@ class FstabGenerator(object):
|
|||||||
print(FSTAB_HEADER, file=fstab_file)
|
print(FSTAB_HEADER, file=fstab_file)
|
||||||
|
|
||||||
for partition in self.partitions:
|
for partition in self.partitions:
|
||||||
# Special treatment for a btrfs root with @ and @home
|
# Special treatment for a btrfs root with @, @home and @swap
|
||||||
# subvolumes
|
# subvolumes
|
||||||
if (partition["fs"] == "btrfs"
|
if (partition["fs"] == "btrfs"
|
||||||
and partition["mountPoint"] == "/"):
|
and partition["mountPoint"] == "/"):
|
||||||
@ -206,6 +206,13 @@ class FstabGenerator(object):
|
|||||||
dct = self.generate_fstab_line_info(home_entry)
|
dct = self.generate_fstab_line_info(home_entry)
|
||||||
if dct:
|
if dct:
|
||||||
self.print_fstab_line(dct, file=fstab_file)
|
self.print_fstab_line(dct, file=fstab_file)
|
||||||
|
elif line.endswith(b'path @swap'):
|
||||||
|
swap_part_entry = partition
|
||||||
|
swap_part_entry["mountPoint"] = "/swap"
|
||||||
|
swap_part_entry["subvol"] = "@swap"
|
||||||
|
dct = self.generate_fstab_line_info(swap_part_entry)
|
||||||
|
if dct:
|
||||||
|
self.print_fstab_line(dct, file=fstab_file)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
dct = self.generate_fstab_line_info(partition)
|
dct = self.generate_fstab_line_info(partition)
|
||||||
@ -319,14 +326,19 @@ def create_swapfile(root_mount_point, root_btrfs):
|
|||||||
The swapfile-creation covers progress from 0.2 to 0.5
|
The swapfile-creation covers progress from 0.2 to 0.5
|
||||||
"""
|
"""
|
||||||
libcalamares.job.setprogress(0.2)
|
libcalamares.job.setprogress(0.2)
|
||||||
swapfile_path = os.path.join(root_mount_point, "swapfile")
|
|
||||||
with open(swapfile_path, "wb") as f:
|
|
||||||
pass
|
|
||||||
if root_btrfs:
|
if root_btrfs:
|
||||||
|
# btrfs swapfiles must reside on a subvolume that is not snapshotted to prevent file system corruption
|
||||||
|
swapfile_path = os.path.join(root_mount_point, "swap/swapfile")
|
||||||
|
with open(swapfile_path, "wb") as f:
|
||||||
|
pass
|
||||||
o = subprocess.check_output(["chattr", "+C", swapfile_path])
|
o = subprocess.check_output(["chattr", "+C", swapfile_path])
|
||||||
libcalamares.utils.debug("swapfile attributes: {!s}".format(o))
|
libcalamares.utils.debug("swapfile attributes: {!s}".format(o))
|
||||||
o = subprocess.check_output(["btrfs", "property", "set", swapfile_path, "compression", "none"])
|
o = subprocess.check_output(["btrfs", "property", "set", swapfile_path, "compression", "none"])
|
||||||
libcalamares.utils.debug("swapfile compression: {!s}".format(o))
|
libcalamares.utils.debug("swapfile compression: {!s}".format(o))
|
||||||
|
else:
|
||||||
|
swapfile_path = os.path.join(root_mount_point, "swapfile")
|
||||||
|
with open(swapfile_path, "wb") as f:
|
||||||
|
pass
|
||||||
# Create the swapfile; swapfiles are small-ish
|
# Create the swapfile; swapfiles are small-ish
|
||||||
zeroes = bytes(16384)
|
zeroes = bytes(16384)
|
||||||
with open(swapfile_path, "wb") as f:
|
with open(swapfile_path, "wb") as f:
|
||||||
@ -374,7 +386,12 @@ def run():
|
|||||||
swap_choice = swap_choice.get( "swap", None )
|
swap_choice = swap_choice.get( "swap", None )
|
||||||
if swap_choice and swap_choice == "file":
|
if swap_choice and swap_choice == "file":
|
||||||
# There's no formatted partition for it, so we'll sneak in an entry
|
# There's no formatted partition for it, so we'll sneak in an entry
|
||||||
partitions.append( dict(fs="swap", mountPoint=None, claimed=True, device="/swapfile", uuid=None) )
|
root_partitions = [ p["fs"].lower() for p in partitions if p["mountPoint"] == "/" ]
|
||||||
|
root_btrfs = (root_partitions[0] == "btrfs") if root_partitions else False
|
||||||
|
if root_btrfs:
|
||||||
|
partitions.append( dict(fs="swap", mountPoint=None, claimed=True, device="/swap/swapfile", uuid=None) )
|
||||||
|
else:
|
||||||
|
partitions.append( dict(fs="swap", mountPoint=None, claimed=True, device="/swapfile", uuid=None) )
|
||||||
else:
|
else:
|
||||||
swap_choice = None
|
swap_choice = None
|
||||||
|
|
||||||
|
@ -38,6 +38,7 @@ def mount_partition(root_mount_point, partition, partitions):
|
|||||||
"""
|
"""
|
||||||
# Create mount point with `+` rather than `os.path.join()` because
|
# Create mount point with `+` rather than `os.path.join()` because
|
||||||
# `partition["mountPoint"]` starts with a '/'.
|
# `partition["mountPoint"]` starts with a '/'.
|
||||||
|
global_storage = libcalamares.globalstorage
|
||||||
raw_mount_point = partition["mountPoint"]
|
raw_mount_point = partition["mountPoint"]
|
||||||
if not raw_mount_point:
|
if not raw_mount_point:
|
||||||
return
|
return
|
||||||
@ -77,6 +78,7 @@ def mount_partition(root_mount_point, partition, partitions):
|
|||||||
# for the root mount point.
|
# for the root mount point.
|
||||||
# If a separate /home partition isn't defined, we also create
|
# If a separate /home partition isn't defined, we also create
|
||||||
# a subvolume "@home".
|
# a subvolume "@home".
|
||||||
|
# If a swapfile is used, we also create a subvolume "@swap".
|
||||||
# Finally we remount all of the above on the correct paths.
|
# Finally we remount all of the above on the correct paths.
|
||||||
if fstype == "btrfs" and partition["mountPoint"] == '/':
|
if fstype == "btrfs" and partition["mountPoint"] == '/':
|
||||||
has_home_mount_point = False
|
has_home_mount_point = False
|
||||||
@ -86,6 +88,12 @@ def mount_partition(root_mount_point, partition, partitions):
|
|||||||
if p["mountPoint"] == "/home":
|
if p["mountPoint"] == "/home":
|
||||||
has_home_mount_point = True
|
has_home_mount_point = True
|
||||||
break
|
break
|
||||||
|
needs_swap_subvolume = False
|
||||||
|
swap_choice = global_storage.value( "partitionChoices" )
|
||||||
|
if swap_choice:
|
||||||
|
swap_choice = swap_choice.get( "swap", None )
|
||||||
|
if swap_choice and swap_choice == "file":
|
||||||
|
needs_swap_subvolume = True
|
||||||
|
|
||||||
subprocess.check_call(['btrfs', 'subvolume', 'create',
|
subprocess.check_call(['btrfs', 'subvolume', 'create',
|
||||||
root_mount_point + '/@'])
|
root_mount_point + '/@'])
|
||||||
@ -93,6 +101,9 @@ def mount_partition(root_mount_point, partition, partitions):
|
|||||||
if not has_home_mount_point:
|
if not has_home_mount_point:
|
||||||
subprocess.check_call(['btrfs', 'subvolume', 'create',
|
subprocess.check_call(['btrfs', 'subvolume', 'create',
|
||||||
root_mount_point + '/@home'])
|
root_mount_point + '/@home'])
|
||||||
|
if needs_swap_subvolume:
|
||||||
|
subprocess.check_call(['btrfs', 'subvolume', 'create',
|
||||||
|
root_mount_point + '/@swap'])
|
||||||
|
|
||||||
subprocess.check_call(["umount", "-v", root_mount_point])
|
subprocess.check_call(["umount", "-v", root_mount_point])
|
||||||
|
|
||||||
@ -113,6 +124,13 @@ def mount_partition(root_mount_point, partition, partitions):
|
|||||||
fstype,
|
fstype,
|
||||||
",".join(["subvol=@home", partition.get("options", "")])) != 0:
|
",".join(["subvol=@home", partition.get("options", "")])) != 0:
|
||||||
libcalamares.utils.warning("Cannot mount {}".format(device))
|
libcalamares.utils.warning("Cannot mount {}".format(device))
|
||||||
|
|
||||||
|
if needs_swap_subvolume:
|
||||||
|
if libcalamares.utils.mount(device,
|
||||||
|
root_mount_point + "/swap",
|
||||||
|
fstype,
|
||||||
|
",".join(["subvol=@swap", partition.get("options", "")])) != 0:
|
||||||
|
libcalamares.utils.warning("Cannot mount {}".format(device))
|
||||||
|
|
||||||
|
|
||||||
def run():
|
def run():
|
||||||
|
54
src/modules/shellprocess/shellprocess.schema.yaml
Normal file
54
src/modules/shellprocess/shellprocess.schema.yaml
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
$schema: http://json-schema.org/draft-07/schema#
|
||||||
|
$id: https://calamares.io/schemas/shellprocess
|
||||||
|
definitions:
|
||||||
|
command:
|
||||||
|
$id: '#definitions/command'
|
||||||
|
type: string
|
||||||
|
description: This is one command that is executed. 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.
|
||||||
|
commandObj:
|
||||||
|
$id: '#definitions/commandObj'
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
command:
|
||||||
|
$ref: '#definitions/command'
|
||||||
|
timeout:
|
||||||
|
type: number
|
||||||
|
description: the (optional) timeout for this specific command (differently
|
||||||
|
from the global setting)
|
||||||
|
required:
|
||||||
|
- command
|
||||||
|
type: object
|
||||||
|
description: Configuration for the shell process job.
|
||||||
|
properties:
|
||||||
|
dontChroot:
|
||||||
|
type: boolean
|
||||||
|
description: 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.
|
||||||
|
timeout:
|
||||||
|
type: number
|
||||||
|
description: The (global) timeout for the command list in seconds. If unset, defaults
|
||||||
|
to 30 seconds.
|
||||||
|
script:
|
||||||
|
anyOf:
|
||||||
|
- $ref: '#definitions/command'
|
||||||
|
- $ref: '#definitions/commandObj'
|
||||||
|
- type: array
|
||||||
|
description: these commands are executed one at a time, by separate shells (/bin/sh
|
||||||
|
-c is invoked for each command).
|
||||||
|
items:
|
||||||
|
anyOf:
|
||||||
|
- $ref: '#definitions/command'
|
||||||
|
- $ref: '#definitions/commandObj'
|
||||||
|
i18n:
|
||||||
|
type: object
|
||||||
|
description: To change description of the job (as it is displayed in the progress
|
||||||
|
bar during installation) use *name* field and optionally, translations as *name[lang]*.
|
||||||
|
Without a translation, the default name from the source code is used, "Shell Processes Job".
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- name
|
Loading…
Reference in New Issue
Block a user