feat: command-line arguments for setting portable directory (#7093)

Reviewed-by: Nerixyz <nerixdev@outlook.de>
Reviewed-by: pajlada <rasmus.karlsson@pajlada.com>
This commit is contained in:
teknsl
2026-07-18 11:53:18 +00:00
committed by GitHub
parent 14bf8c6fad
commit 78e856f7c9
13 changed files with 61 additions and 39 deletions
+1 -1
View File
@@ -27,7 +27,7 @@ int main(int argc, char **argv)
// Ensure settings are initialized before any benchmarks are run // Ensure settings are initialized before any benchmarks are run
QTemporaryDir settingsDir; QTemporaryDir settingsDir;
settingsDir.setAutoRemove(false); // we'll remove it manually settingsDir.setAutoRemove(false); // we'll remove it manually
chatterino::Settings settings(Modes(), args, settingsDir.path()); chatterino::Settings settings(Modes(args), args, settingsDir.path());
QTimer::singleShot(0, [&]() { QTimer::singleShot(0, [&]() {
::benchmark::RunSpecifiedBenchmarks(); ::benchmark::RunSpecifiedBenchmarks();
+2 -2
View File
@@ -294,9 +294,9 @@ public:
} }
QTemporaryDir settingsDir; QTemporaryDir settingsDir;
Modes modes_;
Paths paths_ = {modes_};
Args args_; Args args_;
Modes modes_{args_};
Paths paths_ = {args_, modes_};
}; };
} // namespace chatterino::mock } // namespace chatterino::mock
+19
View File
@@ -188,6 +188,12 @@ Args::Args(const QApplication &app)
"like you have to use this, please reach out to our issue tracker at " "like you have to use this, please reach out to our issue tracker at "
"https://github.com/Chatterino/chatterino2/issues"); "https://github.com/Chatterino/chatterino2/issues");
QCommandLineOption portableEnable("portable", "Enable portable mode.");
QCommandLineOption portableDirectory(
"portable-dir", "Directory to use when portable mode is enabled.",
"directory");
#ifndef NDEBUG #ifndef NDEBUG
QCommandLineOption useLocalEventsubOption( QCommandLineOption useLocalEventsubOption(
"use-local-eventsub", "use-local-eventsub",
@@ -207,6 +213,8 @@ Args::Args(const QApplication &app)
channelLayout, channelLayout,
activateOption, activateOption,
useOldScalingOption, useOldScalingOption,
portableEnable,
portableDirectory,
#ifndef NDEBUG #ifndef NDEBUG
useLocalEventsubOption, useLocalEventsubOption,
#endif #endif
@@ -283,6 +291,17 @@ Args::Args(const QApplication &app)
this->useOldScaling = true; this->useOldScaling = true;
} }
if (parser.isSet(portableEnable))
{
this->portableEnable = true;
}
if (parser.isSet(portableDirectory))
{
this->portableDirectory =
QDir(parser.value(portableDirectory)).absolutePath();
}
#ifndef NDEBUG #ifndef NDEBUG
if (parser.isSet(useLocalEventsubOption)) if (parser.isSet(useLocalEventsubOption))
{ {
+3
View File
@@ -68,6 +68,9 @@ public:
bool verbose{}; bool verbose{};
bool safeMode{}; bool safeMode{};
bool portableEnable{};
std::optional<QString> portableDirectory;
bool useOldScaling = false; bool useOldScaling = false;
#ifndef NDEBUG #ifndef NDEBUG
+7 -1
View File
@@ -4,14 +4,20 @@
#include "common/Modes.hpp" #include "common/Modes.hpp"
#include "common/Args.hpp"
#include "util/CombinePath.hpp" #include "util/CombinePath.hpp"
#include <QCoreApplication> #include <QCoreApplication>
namespace chatterino { namespace chatterino {
Modes::Modes() Modes::Modes(const Args &args)
{ {
if (args.portableEnable)
{
this->isPortable = true;
}
QFile file(combinePath(QCoreApplication::applicationDirPath(), "modes")); QFile file(combinePath(QCoreApplication::applicationDirPath(), "modes"));
if (!file.open(QIODevice::ReadOnly)) if (!file.open(QIODevice::ReadOnly))
{ {
+4 -1
View File
@@ -6,11 +6,14 @@
namespace chatterino { namespace chatterino {
class Args;
class Modes class Modes
{ {
public: public:
Modes(); explicit Modes(const Args &args);
/// Marked by the line `portable` or `portableEnable` from `Args`
bool isPortable{}; bool isPortable{};
/// Marked by the line `externally-packaged` /// Marked by the line `externally-packaged`
+3 -4
View File
@@ -50,7 +50,8 @@ int main(int argc, char **argv)
Version::instance().appUserModelID().c_str()); Version::instance().appUserModelID().c_str());
#endif #endif
const Modes modes; const Args args(a);
const Modes modes(args);
std::unique_ptr<Paths> paths; std::unique_ptr<Paths> paths;
// Optional logger override that logs to a file // Optional logger override that logs to a file
@@ -58,7 +59,7 @@ int main(int argc, char **argv)
try try
{ {
paths = std::make_unique<Paths>(modes); paths = std::make_unique<Paths>(args, modes);
} }
catch (std::runtime_error &error) catch (std::runtime_error &error)
{ {
@@ -85,8 +86,6 @@ int main(int argc, char **argv)
} }
ipc::initPaths(paths.get()); ipc::initPaths(paths.get());
const Args args(a);
#ifdef CHATTERINO_WITH_CRASHPAD #ifdef CHATTERINO_WITH_CRASHPAD
const auto crashpadHandler = installCrashHandler(args, *paths); const auto crashpadHandler = installCrashHandler(args, *paths);
#endif #endif
+10 -15
View File
@@ -4,6 +4,7 @@
#include "singletons/Paths.hpp" #include "singletons/Paths.hpp"
#include "common/Args.hpp"
#include "common/Modes.hpp" #include "common/Modes.hpp"
#include "singletons/Settings.hpp" #include "singletons/Settings.hpp"
#include "util/CombinePath.hpp" #include "util/CombinePath.hpp"
@@ -19,12 +20,11 @@ using namespace std::literals;
namespace chatterino { namespace chatterino {
Paths::Paths(const Modes &modes) Paths::Paths(const Args &args, const Modes &modes)
{ {
this->initAppFilePathHash(); this->initAppFilePathHash();
this->initCheckPortable(); this->initRootDirectory(args, modes);
this->initRootDirectory(modes);
this->initSubDirectories(); this->initSubDirectories();
} }
@@ -75,23 +75,18 @@ void Paths::initAppFilePathHash()
.replace("/", "x"); .replace("/", "x");
} }
void Paths::initCheckPortable() void Paths::initRootDirectory(const Args &args, const Modes &modes)
{ {
this->portable_ = QFileInfo::exists(
combinePath(QCoreApplication::applicationDirPath(), "portable"));
}
void Paths::initRootDirectory(const Modes &modes)
{
assert(this->portable_.has_value());
// Root path = %APPDATA%/Chatterino or the folder that the executable
// resides in
this->rootAppDataDirectory = [&]() -> QString { this->rootAppDataDirectory = [&]() -> QString {
// portable // portable
if (modes.isPortable) if (modes.isPortable)
{ {
// override
if (args.portableDirectory.has_value())
{
return args.portableDirectory.value();
}
return QCoreApplication::applicationDirPath(); return QCoreApplication::applicationDirPath();
} }
+3 -7
View File
@@ -6,16 +6,15 @@
#include <QString> #include <QString>
#include <optional>
namespace chatterino { namespace chatterino {
class Modes; class Modes;
class Args;
class Paths class Paths
{ {
public: public:
Paths(const Modes &modes); Paths(const Args &args, const Modes &modes);
// Root directory for the configuration files. %APPDATA%/chatterino or // Root directory for the configuration files. %APPDATA%/chatterino or
// ExecutablePath for portable mode // ExecutablePath for portable mode
@@ -64,12 +63,9 @@ public:
private: private:
void initAppFilePathHash(); void initAppFilePathHash();
void initCheckPortable(); void initRootDirectory(const Args &args, const Modes &modes);
void initRootDirectory(const Modes &modes);
void initSubDirectories(); void initSubDirectories();
std::optional<bool> portable_;
// Directory for cache files. Same as <appDataDirectory>/Misc // Directory for cache files. Same as <appDataDirectory>/Misc
QString cacheDirectory_; QString cacheDirectory_;
}; };
+3 -3
View File
@@ -206,7 +206,7 @@ void Updates::installUpdates()
file.flush(); file.flush();
file.close(); file.close();
auto updaterPath = Updates::portableUpdaterPath(); auto updaterPath = Updates::portableUpdaterPath(this->paths);
if (!QFile::exists(updaterPath)) if (!QFile::exists(updaterPath))
{ {
this->setStatus_(MissingPortableUpdater); this->setStatus_(MissingPortableUpdater);
@@ -410,9 +410,9 @@ Updates::Status Updates::getStatus() const
return this->status_; return this->status_;
} }
QString Updates::portableUpdaterPath() QString Updates::portableUpdaterPath(const Paths &paths)
{ {
return combinePath(QCoreApplication::applicationDirPath(), return combinePath(paths.rootAppDataDirectory,
"updater.1/ChatterinoUpdater.exe"); "updater.1/ChatterinoUpdater.exe");
} }
+1 -1
View File
@@ -55,7 +55,7 @@ public:
void installUpdates(); void installUpdates();
Status getStatus() const; Status getStatus() const;
static QString portableUpdaterPath(); static QString portableUpdaterPath(const Paths &paths);
bool shouldShowUpdateButton() const; bool shouldShowUpdateButton() const;
bool isError() const; bool isError() const;
+3 -2
View File
@@ -99,8 +99,9 @@ void UpdateDialog::updateStatusChanged(Updates::Status status)
break; break;
case Updates::MissingPortableUpdater: { case Updates::MissingPortableUpdater: {
this->ui_.label->setText("The portable updater (expected in " % this->ui_.label->setText(
Updates::portableUpdaterPath() % "The portable updater (expected in " %
Updates::portableUpdaterPath(getApp()->getPaths()) %
") was not found."); ") was not found.");
} }
break; break;
+1 -1
View File
@@ -43,7 +43,7 @@ int main(int argc, char **argv)
// Ensure settings are initialized before any tests are run // Ensure settings are initialized before any tests are run
QTemporaryDir settingsDir; QTemporaryDir settingsDir;
settingsDir.setAutoRemove(false); // we'll remove it manually settingsDir.setAutoRemove(false); // we'll remove it manually
chatterino::Settings settings(Modes(), args, settingsDir.path()); chatterino::Settings settings(Modes(args), args, settingsDir.path());
QTimer::singleShot(0, [&]() { QTimer::singleShot(0, [&]() {
auto res = RUN_ALL_TESTS(); auto res = RUN_ALL_TESTS();