mirror of
https://github.com/CachyOS/New-Cli-Installer.git
synced 2025-01-23 22:42:31 +08:00
👷 update ui
This commit is contained in:
parent
c36ab03920
commit
ef6fd02314
24
.clang-tidy
Normal file
24
.clang-tidy
Normal file
@ -0,0 +1,24 @@
|
||||
Checks: '-android-cloexec-fopen,
|
||||
-cppcoreguidelines-*,
|
||||
-fuchsia-default-arguments-calls,
|
||||
-fuchsia-default-arguments-declarations,
|
||||
-fuchsia-overloaded-operator,
|
||||
-google-explicit-constructor,
|
||||
-google-readability-function-size,
|
||||
-google-runtime-int,
|
||||
-google-runtime-references,
|
||||
-hicpp-*,
|
||||
-llvm-header-guard,
|
||||
-llvm-include-order,
|
||||
-llvmlibc-*,
|
||||
-misc-*,
|
||||
-modernize-*,
|
||||
-readability-*,
|
||||
-performance-*'
|
||||
FormatStyle: 'file'
|
||||
|
||||
CheckOptions:
|
||||
- key: hicpp-special-member-functions.AllowSoleDefaultDtor
|
||||
value: 1
|
||||
|
||||
HeaderFilterRegex: '.*hpp$'
|
@ -80,6 +80,12 @@ add_executable(${PROJECT_NAME}
|
||||
src/main.cpp
|
||||
)
|
||||
|
||||
add_executable(test-exec-interactive
|
||||
src/config.cpp
|
||||
src/utils.cpp
|
||||
src/main_test.cpp
|
||||
)
|
||||
|
||||
# Link this 'library' to use the warnings specified in CompilerWarnings.cmake
|
||||
add_library(project_warnings INTERFACE)
|
||||
set_project_warnings(project_warnings)
|
||||
@ -93,6 +99,7 @@ enable_sanitizers(project_options)
|
||||
include_directories(${CMAKE_SOURCE_DIR}/src)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE project_warnings project_options fmt::fmt ftxui::screen ftxui::dom ftxui::component nlohmann_json::nlohmann_json cpr::cpr)
|
||||
target_link_libraries(test-exec-interactive PRIVATE project_warnings project_options fmt::fmt)
|
||||
|
||||
option(ENABLE_UNITY "Enable Unity builds of projects" OFF)
|
||||
if(ENABLE_UNITY)
|
||||
|
@ -103,6 +103,13 @@ executable(
|
||||
include_directories: [include_directories('src')],
|
||||
install: true)
|
||||
|
||||
executable(
|
||||
'test-exec-interactive',
|
||||
files('src/config.cpp', 'src/utils.cpp', 'src/main_test.cpp'),
|
||||
dependencies: [fmt],
|
||||
include_directories: [include_directories('src')],
|
||||
install: false)
|
||||
|
||||
summary(
|
||||
{
|
||||
'Build type': get_option('buildtype'),
|
||||
|
8
src/main_test.cpp
Normal file
8
src/main_test.cpp
Normal file
@ -0,0 +1,8 @@
|
||||
#include "definitions.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
int main() {
|
||||
output("\n\n------ TEST BASH LAUNCH BEGIN ------\n\n");
|
||||
utils::exec("bash", true);
|
||||
output("\n\n------ TEST BASH LAUNCH END ------\n\n");
|
||||
}
|
133
src/tui.cpp
133
src/tui.cpp
@ -38,12 +38,10 @@ ftxui::Element centered_widget(ftxui::Component& container, const std::string_vi
|
||||
});
|
||||
}
|
||||
|
||||
ftxui::Component controls_widget(const std::array<std::string_view, 2>&& titles, const std::array<std::function<void()>, 2>&& callbacks) {
|
||||
ftxui::Component controls_widget(const std::array<std::string_view, 2>&& titles, const std::array<std::function<void()>, 2>&& callbacks, ftxui::ButtonOption* button_option) {
|
||||
/* clang-format off */
|
||||
auto button_option = ButtonOption();
|
||||
button_option.border = false;
|
||||
auto button_ok = Button(titles[0].data(), callbacks[0], &button_option);
|
||||
auto button_quit = Button(titles[1].data(), callbacks[1], &button_option);
|
||||
auto button_ok = Button(titles[0].data(), callbacks[0], button_option);
|
||||
auto button_quit = Button(titles[1].data(), callbacks[1], button_option);
|
||||
/* clang-format on */
|
||||
|
||||
auto container = Container::Horizontal({
|
||||
@ -80,6 +78,63 @@ ftxui::Element multiline_text(const std::vector<std::string>& lines) {
|
||||
return vbox(std::move(multiline)) | frame;
|
||||
}
|
||||
|
||||
// BIOS and UEFI
|
||||
void auto_partition() noexcept {
|
||||
auto* config_instance = Config::instance();
|
||||
auto& config_data = config_instance->data();
|
||||
|
||||
// Find existing partitions (if any) to remove
|
||||
auto parts = utils::exec(fmt::format("parted -s {} print | {}", config_data["DEVICE"], "awk \'/^ / {print $1}\'"));
|
||||
const auto& del_parts = utils::make_multiline(parts);
|
||||
for (const auto& del_part : del_parts) {
|
||||
#ifdef NDEVENV
|
||||
utils::exec(fmt::format("parted -s {} rm {}", config_data["DEVICE"], del_part));
|
||||
#else
|
||||
output("{}\n", del_part);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef NDEVENV
|
||||
// Identify the partition table
|
||||
const auto& part_table = utils::exec(fmt::format("parted -s {} print | grep -i \'partition table\' | {}", config_data["DEVICE"], "awk \'{print $3}\'"));
|
||||
|
||||
// Create partition table if one does not already exist
|
||||
if ((config_data["SYSTEM"] == "BIOS") && (part_table != "msdos"))
|
||||
utils::exec(fmt::format("parted -s {} mklabel msdos", config_data["DEVICE"]));
|
||||
if ((config_data["SYSTEM"] == "UEFI") && (part_table != "gpt"))
|
||||
utils::exec(fmt::format("parted -s {} mklabel gpt", config_data["DEVICE"]));
|
||||
|
||||
// Create partitions (same basic partitioning scheme for BIOS and UEFI)
|
||||
if (config_data["SYSTEM"] == "BIOS")
|
||||
utils::exec(fmt::format("parted -s {} mkpart primary ext3 1MiB 513MiB", config_data["DEVICE"]));
|
||||
else
|
||||
utils::exec(fmt::format("parted -s {} mkpart ESP fat32 1MiB 513MiB", config_data["DEVICE"]));
|
||||
|
||||
utils::exec(fmt::format("parted -s {} set 1 boot on", config_data["DEVICE"]));
|
||||
utils::exec(fmt::format("parted -s {} mkpart primary ext3 513MiB 100%", config_data["DEVICE"]));
|
||||
#endif
|
||||
|
||||
// Show created partitions
|
||||
auto disklist = utils::exec(fmt::format("lsblk {} -o NAME,TYPE,FSTYPE,SIZE", config_data["DEVICE"]));
|
||||
|
||||
auto& screen = tui::screen_service::instance()->data();
|
||||
/* clang-format off */
|
||||
auto button_option = ButtonOption();
|
||||
button_option.border = false;
|
||||
auto button_back = Button("Back", screen.ExitLoopClosure(), &button_option);
|
||||
/* clang-format on */
|
||||
|
||||
auto container = Container::Horizontal({
|
||||
button_back,
|
||||
});
|
||||
|
||||
auto renderer = Renderer(container, [&] {
|
||||
return tui::centered_widget(container, "New CLI Installer", multiline_text(utils::make_multiline(disklist)) | size(HEIGHT, GREATER_THAN, 5));
|
||||
});
|
||||
|
||||
screen.Loop(renderer);
|
||||
}
|
||||
|
||||
// Simple code to show devices / partitions.
|
||||
void show_devices() noexcept {
|
||||
auto& screen = tui::screen_service::instance()->data();
|
||||
@ -122,7 +177,70 @@ void select_device() noexcept {
|
||||
const auto& lines = utils::make_multiline(src, " ");
|
||||
config_data["DEVICE"] = lines[0];
|
||||
};
|
||||
auto controls_container = controls_widget({"OK", "Cancel"}, {ok_callback, screen.ExitLoopClosure()});
|
||||
|
||||
ButtonOption button_option{.border = false};
|
||||
auto controls_container = controls_widget({"OK", "Cancel"}, {ok_callback, screen.ExitLoopClosure()}, &button_option);
|
||||
|
||||
auto controls = Renderer(controls_container, [&] {
|
||||
return controls_container->Render() | hcenter | size(HEIGHT, LESS_THAN, 3) | size(WIDTH, GREATER_THAN, 25);
|
||||
});
|
||||
|
||||
auto global = Container::Vertical({
|
||||
content,
|
||||
Renderer([] { return separator(); }),
|
||||
controls,
|
||||
});
|
||||
|
||||
auto renderer = Renderer(global, [&] {
|
||||
return tui::centered_interative_multi("New CLI Installer", global);
|
||||
});
|
||||
|
||||
screen.Loop(renderer);
|
||||
}
|
||||
|
||||
void create_partitions() noexcept {
|
||||
static constexpr std::string_view optwipe = "Securely Wipe Device (optional)";
|
||||
static constexpr std::string_view optauto = "Automatic Partitioning";
|
||||
|
||||
auto* config_instance = Config::instance();
|
||||
auto& config_data = config_instance->data();
|
||||
|
||||
std::vector<std::string> menu_entries = {
|
||||
optwipe.data(),
|
||||
optauto.data(),
|
||||
"cfdisk",
|
||||
"cgdisk",
|
||||
"fdisk",
|
||||
"gdisk",
|
||||
"parted",
|
||||
};
|
||||
|
||||
auto& screen = tui::screen_service::instance()->data();
|
||||
std::int32_t selected{};
|
||||
auto menu = Menu(&menu_entries, &selected);
|
||||
auto content = Renderer(menu, [&] {
|
||||
return menu->Render() | center | size(HEIGHT, GREATER_THAN, 10) | size(WIDTH, GREATER_THAN, 40);
|
||||
});
|
||||
|
||||
auto ok_callback = [&] {
|
||||
const auto& answer = menu_entries[static_cast<std::size_t>(selected)];
|
||||
if (answer != optwipe && answer != optauto) {
|
||||
utils::exec(fmt::format("{} {}", answer, config_data["DEVICE"]), true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (answer == optwipe) {
|
||||
utils::secure_wipe();
|
||||
return;
|
||||
}
|
||||
if (answer == optauto) {
|
||||
auto_partition();
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
ButtonOption button_option{.border = false};
|
||||
auto controls_container = controls_widget({"OK", "Cancel"}, {ok_callback, screen.ExitLoopClosure()}, &button_option);
|
||||
|
||||
auto controls = Renderer(controls_container, [&] {
|
||||
return controls_container->Render() | hcenter | size(HEIGHT, LESS_THAN, 3) | size(WIDTH, GREATER_THAN, 25);
|
||||
@ -144,7 +262,8 @@ void select_device() noexcept {
|
||||
void init() noexcept {
|
||||
auto& screen = tui::screen_service::instance()->data();
|
||||
auto ok_callback = [=] { info("ok\n"); };
|
||||
auto container = controls_widget({"OK", "Quit"}, {ok_callback, screen.ExitLoopClosure()});
|
||||
ButtonOption button_option{.border = false};
|
||||
auto container = controls_widget({"OK", "Quit"}, {ok_callback, screen.ExitLoopClosure()}, &button_option);
|
||||
|
||||
auto renderer = Renderer(container, [&] {
|
||||
return tui::centered_widget(container, "New CLI Installer", text("TODO!!") | size(HEIGHT, GREATER_THAN, 5));
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include "config.hpp"
|
||||
#include "definitions.hpp"
|
||||
|
||||
#include <algorithm> // for transform
|
||||
#include <array> // for array
|
||||
#include <chrono> // for filesystem, seconds
|
||||
#include <cstdint> // for int32_t
|
||||
@ -9,6 +10,7 @@
|
||||
#include <cstdlib> // for exit, WIFEXITED, WIFSIGNALED
|
||||
#include <filesystem> // for exists, is_directory
|
||||
#include <iostream> // for basic_istream, cin
|
||||
#include <regex> // for regex_search, match_results<>::_Base_type
|
||||
#include <string> // for operator==, string, basic_string, allocator
|
||||
#include <sys/mount.h> // for mount
|
||||
#include <sys/wait.h> // for waitpid
|
||||
@ -16,10 +18,12 @@
|
||||
#include <unistd.h> // for execvp, fork
|
||||
#include <unordered_map> // for unordered_map
|
||||
|
||||
#ifdef NDEVENV
|
||||
#include <cpr/api.h>
|
||||
#include <cpr/response.h>
|
||||
#include <cpr/status_codes.h>
|
||||
#include <cpr/timeout.h>
|
||||
#endif
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
@ -51,22 +55,35 @@ void clear_screen() noexcept {
|
||||
output("{}", CLEAR_SCREEN_ANSI);
|
||||
}
|
||||
|
||||
std::string exec(const std::string_view& command, bool capture_output) noexcept {
|
||||
if (!capture_output) {
|
||||
void exec(const std::vector<std::string>& vec) noexcept {
|
||||
std::int32_t status{};
|
||||
auto pid = fork();
|
||||
if (pid == 0) {
|
||||
/* clang-format off */
|
||||
char* args[2] = { const_cast<char*>(command.data()), nullptr };
|
||||
/* clang-format on */
|
||||
execvp(args[0], args);
|
||||
std::vector<char*> args;
|
||||
std::transform(vec.cbegin(), vec.cend(), std::back_inserter(args),
|
||||
[=](const std::string& arg) -> char* { return const_cast<char*>(arg.data()); });
|
||||
args.push_back(nullptr);
|
||||
|
||||
char** command = args.data();
|
||||
execvp(command[0], command);
|
||||
} else {
|
||||
do {
|
||||
waitpid(pid, &status, 0);
|
||||
} while ((!WIFEXITED(status)) && (!WIFSIGNALED(status)));
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/sheredom/subprocess.h
|
||||
// https://gist.github.com/konstantint/d49ab683b978b3d74172
|
||||
// https://github.com/arun11299/cpp-subprocess/blob/master/subprocess.hpp#L1218
|
||||
// https://stackoverflow.com/questions/11342868/c-interface-for-interactive-bash
|
||||
// https://github.com/hniksic/rust-subprocess
|
||||
std::string exec(const std::string_view& command, const bool& interactive) noexcept {
|
||||
if (interactive) {
|
||||
system(command.data());
|
||||
return {};
|
||||
}
|
||||
|
||||
auto* pipe = popen(command.data(), "r");
|
||||
if (!pipe) {
|
||||
return "popen failed!";
|
||||
@ -125,6 +142,17 @@ auto make_multiline(std::string& str, const std::string_view&& delim) noexcept -
|
||||
return lines;
|
||||
}
|
||||
|
||||
// install a pkg in the live session if not installed
|
||||
void inst_needed(const std::string_view& pkg) {
|
||||
const auto& pkg_info = utils::exec(fmt::format("pacman -Q {}", pkg));
|
||||
const std::regex pkg_regex("/error/");
|
||||
if (!std::regex_search(pkg_info, pkg_regex)) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||
utils::clear_screen();
|
||||
utils::exec(fmt::format("pacman -Sy --noconfirm {}", pkg));
|
||||
}
|
||||
}
|
||||
|
||||
// Unmount partitions.
|
||||
void umount_partitions() noexcept {
|
||||
auto* config_instance = Config::instance();
|
||||
@ -144,6 +172,20 @@ void umount_partitions() noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
// Securely destroy all data on a given device.
|
||||
void secure_wipe() noexcept {
|
||||
auto* config_instance = Config::instance();
|
||||
auto& config_data = config_instance->data();
|
||||
|
||||
#ifdef NDEVENV
|
||||
utils::inst_needed("wipe");
|
||||
utils::exec(fmt::format("wipe -Ifre {}", config_data["DEVICE"]));
|
||||
#else
|
||||
utils::inst_needed("bash");
|
||||
output("{}\n", config_data["DEVICE"]);
|
||||
#endif
|
||||
}
|
||||
|
||||
void id_system() noexcept {
|
||||
auto* config_instance = Config::instance();
|
||||
auto& config_data = config_instance->data();
|
||||
@ -223,7 +265,7 @@ void show_iwctl() noexcept {
|
||||
info("6 - type `exit`\n");
|
||||
|
||||
while (utils::prompt_char("Press a key to continue...", CYAN)) {
|
||||
utils::exec("iwctl", false);
|
||||
utils::exec("iwctl", true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -13,8 +13,10 @@ void print_banner() noexcept;
|
||||
bool prompt_char(const char* prompt, const char* color = RESET, char* read = nullptr) noexcept;
|
||||
void clear_screen() noexcept;
|
||||
[[nodiscard]] auto make_multiline(std::string& str, const std::string_view&& delim = "\n") noexcept -> std::vector<std::string>;
|
||||
void secure_wipe() noexcept;
|
||||
|
||||
auto exec(const std::string_view& command, bool capture_output = true) noexcept -> std::string;
|
||||
void exec(const std::vector<std::string>& vec) noexcept;
|
||||
auto exec(const std::string_view& command, const bool& interactive = false) noexcept -> std::string;
|
||||
[[nodiscard]] bool check_root() noexcept;
|
||||
void id_system() noexcept;
|
||||
[[nodiscard]] bool handle_connection() noexcept;
|
||||
|
Loading…
Reference in New Issue
Block a user