hyprpm: added plugin author (#12594)

This commit is contained in:
Luke Barkess 2025-12-14 17:16:58 +00:00 committed by GitHub
parent 09e195d1f2
commit 05ccbb2f2d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 160 additions and 67 deletions

View file

@ -93,6 +93,7 @@ void DataState::addNewPluginRepo(const SPluginRepository& repo) {
auto DATA = toml::table{ auto DATA = toml::table{
{"repository", toml::table{ {"repository", toml::table{
{"name", repo.name}, {"name", repo.name},
{"author", repo.author},
{"hash", repo.hash}, {"hash", repo.hash},
{"url", repo.url}, {"url", repo.url},
{"rev", repo.rev} {"rev", repo.rev}
@ -122,31 +123,32 @@ void DataState::addNewPluginRepo(const SPluginRepository& repo) {
Debug::die("{}", failureString("Failed to write plugin state")); Debug::die("{}", failureString("Failed to write plugin state"));
} }
bool DataState::pluginRepoExists(const std::string& urlOrName) { bool DataState::pluginRepoExists(const SPluginRepoIdentifier identifier) {
ensureStateStoreExists(); ensureStateStoreExists();
for (const auto& stateFile : getPluginStates()) { for (const auto& stateFile : getPluginStates()) {
const auto STATE = toml::parse_file(stateFile.c_str()); const auto STATE = toml::parse_file(stateFile.c_str());
const auto NAME = STATE["repository"]["name"].value_or(""); const auto NAME = STATE["repository"]["name"].value_or("");
const auto AUTHOR = STATE["repository"]["author"].value_or("");
const auto URL = STATE["repository"]["url"].value_or(""); const auto URL = STATE["repository"]["url"].value_or("");
if (URL == urlOrName || NAME == urlOrName) if (identifier.matches(URL, NAME, AUTHOR))
return true; return true;
} }
return false; return false;
} }
void DataState::removePluginRepo(const std::string& urlOrName) { void DataState::removePluginRepo(const SPluginRepoIdentifier identifier) {
ensureStateStoreExists(); ensureStateStoreExists();
for (const auto& stateFile : getPluginStates()) { for (const auto& stateFile : getPluginStates()) {
const auto STATE = toml::parse_file(stateFile.c_str()); const auto STATE = toml::parse_file(stateFile.c_str());
const auto NAME = STATE["repository"]["name"].value_or(""); const auto NAME = STATE["repository"]["name"].value_or("");
const auto AUTHOR = STATE["repository"]["author"].value_or("");
const auto URL = STATE["repository"]["url"].value_or(""); const auto URL = STATE["repository"]["url"].value_or("");
if (URL == urlOrName || NAME == urlOrName) { if (identifier.matches(URL, NAME, AUTHOR)) {
// unload the plugins!! // unload the plugins!!
for (const auto& file : std::filesystem::directory_iterator(stateFile.parent_path())) { for (const auto& file : std::filesystem::directory_iterator(stateFile.parent_path())) {
if (!file.path().string().ends_with(".so")) if (!file.path().string().ends_with(".so"))
@ -220,6 +222,7 @@ std::vector<SPluginRepository> DataState::getAllRepositories() {
const auto STATE = toml::parse_file(stateFile.c_str()); const auto STATE = toml::parse_file(stateFile.c_str());
const auto NAME = STATE["repository"]["name"].value_or(""); const auto NAME = STATE["repository"]["name"].value_or("");
const auto AUTHOR = STATE["repository"]["author"].value_or("");
const auto URL = STATE["repository"]["url"].value_or(""); const auto URL = STATE["repository"]["url"].value_or("");
const auto REV = STATE["repository"]["rev"].value_or(""); const auto REV = STATE["repository"]["rev"].value_or("");
const auto HASH = STATE["repository"]["hash"].value_or(""); const auto HASH = STATE["repository"]["hash"].value_or("");
@ -227,6 +230,7 @@ std::vector<SPluginRepository> DataState::getAllRepositories() {
SPluginRepository repo; SPluginRepository repo;
repo.hash = HASH; repo.hash = HASH;
repo.name = NAME; repo.name = NAME;
repo.author = AUTHOR;
repo.url = URL; repo.url = URL;
repo.rev = REV; repo.rev = REV;
@ -247,7 +251,7 @@ std::vector<SPluginRepository> DataState::getAllRepositories() {
return repos; return repos;
} }
bool DataState::setPluginEnabled(const std::string& name, bool enabled) { bool DataState::setPluginEnabled(const SPluginRepoIdentifier identifier, bool enabled) {
ensureStateStoreExists(); ensureStateStoreExists();
for (const auto& stateFile : getPluginStates()) { for (const auto& stateFile : getPluginStates()) {
@ -256,8 +260,17 @@ bool DataState::setPluginEnabled(const std::string& name, bool enabled) {
if (key == "repository") if (key == "repository")
continue; continue;
if (key.str() != name) switch (identifier.type) {
case IDENTIFIER_NAME:
if (key.str() != identifier.name)
continue; continue;
break;
case IDENTIFIER_AUTHOR_NAME:
if (STATE["repository"]["author"] != identifier.author || key.str() != identifier.name)
continue;
break;
default: return false;
}
const auto FAILED = STATE[key]["failed"].value_or(false); const auto FAILED = STATE[key]["failed"].value_or(false);

View file

@ -15,11 +15,11 @@ namespace DataState {
std::vector<std::filesystem::path> getPluginStates(); std::vector<std::filesystem::path> getPluginStates();
void ensureStateStoreExists(); void ensureStateStoreExists();
void addNewPluginRepo(const SPluginRepository& repo); void addNewPluginRepo(const SPluginRepository& repo);
void removePluginRepo(const std::string& urlOrName); void removePluginRepo(const SPluginRepoIdentifier identifier);
bool pluginRepoExists(const std::string& urlOrName); bool pluginRepoExists(const SPluginRepoIdentifier identifier);
void updateGlobalState(const SGlobalState& state); void updateGlobalState(const SGlobalState& state);
void purgeAllCache(); void purgeAllCache();
SGlobalState getGlobalState(); SGlobalState getGlobalState();
bool setPluginEnabled(const std::string& name, bool enabled); bool setPluginEnabled(const SPluginRepoIdentifier identifier, bool enabled);
std::vector<SPluginRepository> getAllRepositories(); std::vector<SPluginRepository> getAllRepositories();
}; };

View file

@ -0,0 +1,48 @@
#include "Plugin.hpp"
SPluginRepoIdentifier SPluginRepoIdentifier::fromUrl(const std::string& url) {
return SPluginRepoIdentifier{.type = IDENTIFIER_URL, .url = url};
}
SPluginRepoIdentifier SPluginRepoIdentifier::fromName(const std::string& name) {
return SPluginRepoIdentifier{.type = IDENTIFIER_NAME, .name = name};
}
SPluginRepoIdentifier SPluginRepoIdentifier::fromAuthorName(const std::string& author, const std::string& name) {
return SPluginRepoIdentifier{.type = IDENTIFIER_AUTHOR_NAME, .name = name, .author = author};
}
SPluginRepoIdentifier SPluginRepoIdentifier::fromString(const std::string& string) {
if (string.find(':') != std::string::npos) {
return SPluginRepoIdentifier{.type = IDENTIFIER_URL, .url = string};
} else {
auto slashPos = string.find('/');
if (slashPos != std::string::npos) {
std::string author = string.substr(0, slashPos);
std::string name = string.substr(slashPos + 1, string.size() - slashPos - 1);
return SPluginRepoIdentifier{.type = IDENTIFIER_AUTHOR_NAME, .name = name, .author = author};
} else {
return SPluginRepoIdentifier{.type = IDENTIFIER_NAME, .name = string};
}
}
}
std::string SPluginRepoIdentifier::toString() const {
switch (type) {
case IDENTIFIER_NAME: return name;
case IDENTIFIER_AUTHOR_NAME: return author + '/' + name;
case IDENTIFIER_URL: return url;
}
return "";
}
bool SPluginRepoIdentifier::matches(const std::string& url, const std::string& name, const std::string& author) const {
switch (type) {
case IDENTIFIER_URL: return this->url == url;
case IDENTIFIER_NAME: return this->name == name;
case IDENTIFIER_AUTHOR_NAME: return this->author == author && this->name == name;
}
return false;
}

View file

@ -14,6 +14,27 @@ struct SPluginRepository {
std::string url; std::string url;
std::string rev; std::string rev;
std::string name; std::string name;
std::string author;
std::vector<SPlugin> plugins; std::vector<SPlugin> plugins;
std::string hash; std::string hash;
}; };
enum ePluginRepoIdentifierType {
IDENTIFIER_URL,
IDENTIFIER_NAME,
IDENTIFIER_AUTHOR_NAME
};
struct SPluginRepoIdentifier {
ePluginRepoIdentifierType type;
std::string url = "";
std::string name = "";
std::string author = "";
static SPluginRepoIdentifier fromString(const std::string& string);
static SPluginRepoIdentifier fromUrl(const std::string& Url);
static SPluginRepoIdentifier fromName(const std::string& name);
static SPluginRepoIdentifier fromAuthorName(const std::string& author, const std::string& name);
std::string toString() const;
bool matches(const std::string& url, const std::string& name, const std::string& author) const;
};

View file

@ -11,6 +11,7 @@
#include <cstdio> #include <cstdio>
#include <iostream> #include <iostream>
#include <filesystem> #include <filesystem>
#include <string>
#include <print> #include <print>
#include <fstream> #include <fstream>
#include <algorithm> #include <algorithm>
@ -136,7 +137,7 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
return false; return false;
} }
if (DataState::pluginRepoExists(url)) { if (DataState::pluginRepoExists(SPluginRepoIdentifier::fromUrl(url))) {
std::println(stderr, "\n{}", failureString("Could not clone the plugin repository. Repository already installed.")); std::println(stderr, "\n{}", failureString("Could not clone the plugin repository. Repository already installed."));
return false; return false;
} }
@ -333,7 +334,10 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
std::string repohash = execAndGet("cd " + m_szWorkingPluginDirectory + " && git rev-parse HEAD"); std::string repohash = execAndGet("cd " + m_szWorkingPluginDirectory + " && git rev-parse HEAD");
if (repohash.length() > 0) if (repohash.length() > 0)
repohash.pop_back(); repohash.pop_back();
repo.name = pManifest->m_repository.name.empty() ? url.substr(url.find_last_of('/') + 1) : pManifest->m_repository.name; auto lastSlash = url.find_last_of('/');
auto secondLastSlash = url.find_last_of('/', lastSlash - 1);
repo.name = pManifest->m_repository.name.empty() ? url.substr(lastSlash + 1) : pManifest->m_repository.name;
repo.author = url.substr(secondLastSlash + 1, lastSlash - secondLastSlash - 1);
repo.url = url; repo.url = url;
repo.rev = rev; repo.rev = rev;
repo.hash = repohash; repo.hash = repohash;
@ -356,13 +360,13 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
return true; return true;
} }
bool CPluginManager::removePluginRepo(const std::string& urlOrName) { bool CPluginManager::removePluginRepo(const SPluginRepoIdentifier identifier) {
if (!DataState::pluginRepoExists(urlOrName)) { if (!DataState::pluginRepoExists(identifier)) {
std::println(stderr, "\n{}", failureString("Could not remove the repository. Repository is not installed.")); std::println(stderr, "\n{}", failureString("Could not remove the repository. Repository is not installed."));
return false; return false;
} }
std::cout << Colors::YELLOW << "!" << Colors::RESET << Colors::RED << " removing a plugin repository: " << Colors::RESET << urlOrName << "\n " std::cout << Colors::YELLOW << "!" << Colors::RESET << Colors::RED << " removing a plugin repository: " << Colors::RESET << identifier.toString() << "\n "
<< "Are you sure? [Y/n] "; << "Are you sure? [Y/n] ";
std::fflush(stdout); std::fflush(stdout);
std::string input; std::string input;
@ -373,7 +377,7 @@ bool CPluginManager::removePluginRepo(const std::string& urlOrName) {
return false; return false;
} }
DataState::removePluginRepo(urlOrName); DataState::removePluginRepo(identifier);
return true; return true;
} }
@ -444,7 +448,6 @@ eHeadersErrors CPluginManager::headersValid() {
} }
bool CPluginManager::updateHeaders(bool force) { bool CPluginManager::updateHeaders(bool force) {
DataState::ensureStateStoreExists(); DataState::ensureStateStoreExists();
const auto HLVER = getHyprlandVersion(false); const auto HLVER = getHyprlandVersion(false);
@ -772,7 +775,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
const auto OLDPLUGINIT = std::find_if(repo.plugins.begin(), repo.plugins.end(), [&](const auto& other) { return other.name == p.name; }); const auto OLDPLUGINIT = std::find_if(repo.plugins.begin(), repo.plugins.end(), [&](const auto& other) { return other.name == p.name; });
newrepo.plugins.push_back(SPlugin{p.name, m_szWorkingPluginDirectory + "/" + p.output, OLDPLUGINIT != repo.plugins.end() ? OLDPLUGINIT->enabled : false}); newrepo.plugins.push_back(SPlugin{p.name, m_szWorkingPluginDirectory + "/" + p.output, OLDPLUGINIT != repo.plugins.end() ? OLDPLUGINIT->enabled : false});
} }
DataState::removePluginRepo(newrepo.name); DataState::removePluginRepo(SPluginRepoIdentifier::fromName(newrepo.name));
DataState::addNewPluginRepo(newrepo); DataState::addNewPluginRepo(newrepo);
std::filesystem::remove_all(m_szWorkingPluginDirectory); std::filesystem::remove_all(m_szWorkingPluginDirectory);
@ -797,17 +800,23 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
return true; return true;
} }
bool CPluginManager::enablePlugin(const std::string& name) { bool CPluginManager::enablePlugin(const SPluginRepoIdentifier identifier) {
bool ret = DataState::setPluginEnabled(name, true); bool ret = false;
switch (identifier.type) {
case IDENTIFIER_NAME:
case IDENTIFIER_AUTHOR_NAME: ret = DataState::setPluginEnabled(identifier, true); break;
default: return false;
}
if (ret) if (ret)
std::println("{}", successString("Enabled {}", name)); std::println("{}", successString("Enabled {}", identifier.name));
return ret; return ret;
} }
bool CPluginManager::disablePlugin(const std::string& name) { bool CPluginManager::disablePlugin(const SPluginRepoIdentifier identifier) {
bool ret = DataState::setPluginEnabled(name, false); bool ret = DataState::setPluginEnabled(identifier, false);
if (ret) if (ret)
std::println("{}", successString("Disabled {}", name)); std::println("{}", successString("Disabled {}", identifier.name));
return ret; return ret;
} }
@ -928,7 +937,7 @@ void CPluginManager::listAllPlugins() {
const auto REPOS = DataState::getAllRepositories(); const auto REPOS = DataState::getAllRepositories();
for (auto const& r : REPOS) { for (auto const& r : REPOS) {
std::println("{}", infoString("Repository {}:", r.name)); std::println("{}", infoString("Repository {} (by {}):", r.name, r.author));
for (auto const& p : r.plugins) { for (auto const& p : r.plugins) {
std::println(" │ Plugin {}", p.name); std::println(" │ Plugin {}", p.name);

View file

@ -2,8 +2,10 @@
#include <cstdint> #include <cstdint>
#include <memory> #include <memory>
#include <optional>
#include <string> #include <string>
#include <utility> #include <utility>
#include "Plugin.hpp"
enum eHeadersErrors { enum eHeadersErrors {
HEADERS_OK = 0, HEADERS_OK = 0,
@ -46,7 +48,7 @@ class CPluginManager {
CPluginManager(); CPluginManager();
bool addNewPluginRepo(const std::string& url, const std::string& rev); bool addNewPluginRepo(const std::string& url, const std::string& rev);
bool removePluginRepo(const std::string& urlOrName); bool removePluginRepo(const SPluginRepoIdentifier identifier);
eHeadersErrors headersValid(); eHeadersErrors headersValid();
bool updateHeaders(bool force = false); bool updateHeaders(bool force = false);
@ -54,8 +56,8 @@ class CPluginManager {
void listAllPlugins(); void listAllPlugins();
bool enablePlugin(const std::string& name); bool enablePlugin(const SPluginRepoIdentifier identifier);
bool disablePlugin(const std::string& name); bool disablePlugin(const SPluginRepoIdentifier identifier);
ePluginLoadStateReturn ensurePluginsLoadState(bool forceReload = false); ePluginLoadStateReturn ensurePluginsLoadState(bool forceReload = false);
bool loadUnloadPlugin(const std::string& path, bool load); bool loadUnloadPlugin(const std::string& path, bool load);

View file

@ -13,11 +13,11 @@ using namespace Hyprutils::Utils;
constexpr std::string_view HELP = R"#(┏ hyprpm, a Hyprland Plugin Manager constexpr std::string_view HELP = R"#(┏ hyprpm, a Hyprland Plugin Manager
add [url] [git rev] Install a new plugin repository from git. Git revision add <url> [git rev] Install a new plugin repository from git. Git revision
is optional, when set, commit locks are ignored. is optional, when set, commit locks are ignored.
remove [url/name] Remove an installed plugin repository. remove <url|name|author/name> Remove an installed plugin repository.
enable [name] Enable a plugin. enable <name|author/name> Enable a plugin.
disable [name] Disable a plugin. disable <name|author/name> Disable a plugin.
update Check and update all plugins if needed. update Check and update all plugins if needed.
reload Reload hyprpm state. Ensure all enabled plugins are loaded. reload Reload hyprpm state. Ensure all enabled plugins are loaded.
list List all installed plugins. list List all installed plugins.
@ -126,7 +126,7 @@ int main(int argc, char** argv, char** envp) {
NSys::root::cacheSudo(); NSys::root::cacheSudo();
CScopeGuard x([] { NSys::root::dropSudo(); }); CScopeGuard x([] { NSys::root::dropSudo(); });
return g_pPluginManager->removePluginRepo(command[1]) ? 0 : 1; return g_pPluginManager->removePluginRepo(SPluginRepoIdentifier::fromString(command[1])) ? 0 : 1;
} else if (command[0] == "update") { } else if (command[0] == "update") {
NSys::root::cacheSudo(); NSys::root::cacheSudo();
CScopeGuard x([] { NSys::root::dropSudo(); }); CScopeGuard x([] { NSys::root::dropSudo(); });
@ -160,7 +160,7 @@ int main(int argc, char** argv, char** envp) {
return 1; return 1;
} }
if (!g_pPluginManager->enablePlugin(command[1])) { if (!g_pPluginManager->enablePlugin(SPluginRepoIdentifier::fromString(command[1]))) {
std::println(stderr, "{}", failureString("Couldn't enable plugin (missing?)")); std::println(stderr, "{}", failureString("Couldn't enable plugin (missing?)"));
return 1; return 1;
} }
@ -181,7 +181,7 @@ int main(int argc, char** argv, char** envp) {
return 1; return 1;
} }
if (!g_pPluginManager->disablePlugin(command[1])) { if (!g_pPluginManager->disablePlugin(SPluginRepoIdentifier::fromString(command[1]))) {
std::println(stderr, "{}", failureString("Couldn't disable plugin (missing?)")); std::println(stderr, "{}", failureString("Couldn't disable plugin (missing?)"));
return 1; return 1;
} }