Skip to content

Commit 5692464

Browse files
committed
cufetchpm: args: add update command
1 parent 2bb9468 commit 5692464

File tree

7 files changed

+132
-61
lines changed

7 files changed

+132
-61
lines changed

cufetchpm/include/manifest.hpp

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@
2626
#ifndef _MANIFEST_HPP_
2727
#define _MANIFEST_HPP_
2828

29+
#include <filesystem>
2930
#include <string>
3031
#include <string_view>
31-
#include <utility>
3232
#include <vector>
3333

3434
#include "platform.hpp"
@@ -85,6 +85,10 @@ struct manifest_t
8585
// The repository git url
8686
std::string url;
8787

88+
// NOTE: INTERNAL ONLY
89+
// The repository latest commit hash.
90+
std::string git_hash;
91+
8892
// An array of all the plugins that are declared in the manifest
8993
std::vector<plugin_t> plugins;
9094

@@ -108,9 +112,7 @@ std::vector<std::string> getStrArrayValue(const toml::table& tbl, const std::str
108112
class CManifest
109113
{
110114
public:
111-
CManifest(const std::string_view path);
112-
CManifest(toml::table&& tbl) : m_tbl(std::move(tbl)) { parse_manifest(); }
113-
CManifest(const toml::table& tbl) : m_tbl(tbl) { parse_manifest(); }
115+
CManifest(const std::filesystem::path& path);
114116

115117
plugin_t get_plugin(const std::string_view name);
116118

@@ -120,6 +122,9 @@ class CManifest
120122
const std::string& get_repo_url() const
121123
{ return m_repo.url; }
122124

125+
const std::string& get_repo_hash() const
126+
{ return m_repo.git_hash; }
127+
123128
const std::vector<plugin_t>& get_all_plugins() const
124129
{ return m_repo.plugins; }
125130

@@ -130,8 +135,7 @@ class CManifest
130135
toml::table m_tbl;
131136
manifest_t m_repo;
132137

133-
void parse_manifest();
134-
void parse_manifest_state();
138+
void parse_manifest(const std::filesystem::path& path);
135139

136140
std::string getStrValue(const std::string_view name, const std::string_view key) const
137141
{

cufetchpm/include/pluginManager.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ class PluginManager
7070
void add_source_repo_plugins(const std::string& repo);
7171
void build_plugins(const fs::path& working_dir);
7272
bool add_plugin(const std::string&);
73+
void update_repos();
7374
bool is_plugin_conflicting(const plugin_t& plugin);
7475
void remove_plugins_source(const std::string& source_name);
7576

cufetchpm/src/main.cpp

Lines changed: 40 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
#include <cstdlib>
2727
#include <filesystem>
28+
#include <unordered_map>
2829

2930
#include "fmt/compile.h"
3031
#include "fmt/os.h"
@@ -36,24 +37,43 @@
3637
#include "texts.hpp"
3738

3839
#if (!__has_include("version.h"))
39-
#error "version.h not found, please generate it with ./scripts/generateVersion.sh"
40+
#error "version.h not found, please generate it with ../scripts/generateVersion.sh"
4041
#else
4142
#include "version.h"
4243
#endif
4344

4445
#include "getopt_port/getopt.h"
4546

46-
enum Ops
47+
enum OPs
4748
{
4849
NONE,
4950
INSTALL,
51+
UPDATE,
5052
LIST,
5153
ENABLE,
5254
DISABLE,
5355
UNINSTALL,
5456
GEN_MANIFEST,
57+
HELP
5558
} op = NONE;
5659

60+
const std::unordered_map<std::string_view, OPs> map{
61+
{ "install", INSTALL },
62+
{ "update", UPDATE },
63+
{ "list", LIST },
64+
{ "help", HELP },
65+
{ "enable", ENABLE },
66+
{ "disable", UNINSTALL },
67+
{ "gen-manifest", GEN_MANIFEST },
68+
};
69+
70+
OPs str_to_enum(const std::string_view name)
71+
{
72+
if (auto it = map.find(name); it != map.end())
73+
return it->second;
74+
return NONE;
75+
}
76+
5777
void version()
5878
{
5979
fmt::print(
@@ -163,9 +183,6 @@ bool parse_general_command_args(int argc, char* argv[])
163183
for (int i = optind; i < argc; ++i)
164184
options.arguments.emplace_back(argv[i]);
165185

166-
if (options.arguments.empty())
167-
die("Please provide a source/plugin to enable/disable");
168-
169186
return true;
170187
}
171188

@@ -203,42 +220,17 @@ static bool parseargs(int argc, char* argv[])
203220
std::string_view cmd = argv[optind];
204221
int sub_argc = argc - optind - 1;
205222
char** sub_argv = argv + optind + 1;
206-
if (cmd == "install" || cmd == "i")
207-
{
208-
op = INSTALL;
209-
optind = 0;
210-
return parse_install_args(sub_argc, sub_argv);
211-
}
212-
else if (cmd == "list" || cmd == "l")
213-
{
214-
op = LIST;
215-
optind = 0;
216-
return parse_list_args(sub_argc, sub_argv);
217-
}
218-
else if (cmd == "gen-manifest")
219-
{
220-
op = GEN_MANIFEST;
221-
optind = 0;
222-
}
223-
else if (cmd == "enable")
224-
{
225-
op = ENABLE;
226-
optind = 0;
227-
return parse_general_command_args(sub_argc, sub_argv);
228-
}
229-
else if (cmd == "disable")
230-
{
231-
op = DISABLE;
232-
optind = 0;
233-
return parse_general_command_args(sub_argc, sub_argv);
234-
}
235-
else if (cmd == "uninstall")
223+
224+
op = str_to_enum(cmd);
225+
switch (op)
236226
{
237-
op = UNINSTALL;
238-
optind = 0;
239-
return parse_general_command_args(sub_argc, sub_argv);
227+
case INSTALL: optind = 0; return parse_install_args(sub_argc, sub_argv);
228+
case LIST: optind = 0; return parse_list_args(sub_argc, sub_argv);
229+
case HELP: break;
230+
default: optind = 0; return parse_general_command_args(sub_argc, sub_argv);
240231
}
241-
else if (cmd == "help")
232+
233+
if (op == HELP)
242234
{
243235
if (sub_argc >= 1)
244236
{
@@ -352,7 +344,8 @@ void list_all_plugins(StateManager&& state)
352344
fmt::println("\033[1;34m - {}\033[0m", plugin.name);
353345
fmt::println("\t\033[1;35mDescription:\033[0m {}", plugin.description);
354346
fmt::println("\t\033[1;36mAuthor(s):\033[0m {}", fmt::join(plugin.authors, ", "));
355-
fmt::println("\t\033[1;38;2;255;100;220mDisabled:\033[0m {}", is_plugin_disabled(manifest.name, plugin.name));
347+
fmt::println("\t\033[1;38;2;255;100;220mDisabled:\033[0m {}",
348+
is_plugin_disabled(manifest.name, plugin.name));
356349
fmt::println("\t\033[1;38;2;220;220;220mLicense(s):\033[0m {}", fmt::join(plugin.licenses, ", "));
357350
fmt::println("\t\033[1;38;2;144;238;144mPrefixe(s):\033[0m {}", fmt::join(plugin.prefixes, ", "));
358351
}
@@ -370,7 +363,7 @@ void list_all_plugins(StateManager&& state)
370363
fmt::print(" \033[1;34m{} - \033[1;35m{}", plugin.name, plugin.description);
371364
if (is_plugin_disabled(manifest.name, plugin.name))
372365
fmt::print(" \033[1;31m(DISABLED)");
373-
fmt::print("\n");
366+
fmt::print("\n");
374367
}
375368
fmt::print("\033[0m");
376369
}
@@ -423,6 +416,12 @@ int main(int argc, char* argv[])
423416
switch_plugin(std::move(state), false);
424417
break;
425418
}
419+
case UPDATE:
420+
{
421+
PluginManager plugin_manager(std::move(state));
422+
plugin_manager.update_repos();
423+
break;
424+
}
426425
case UNINSTALL:
427426
{
428427
if (options.arguments.size() < 1)

cufetchpm/src/manifest.cpp

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,15 @@
2727

2828
#include <algorithm>
2929
#include <cctype>
30+
#include <filesystem>
3031
#include <vector>
3132

3233
#include "libcufetch/common.hh"
34+
#include "tiny-process-library/process.hpp"
3335
#include "util.hpp"
3436

37+
namespace fs = std::filesystem;
38+
3539
static bool is_valid_name(const std::string_view n)
3640
{
3741
return std::ranges::all_of(n,
@@ -85,24 +89,24 @@ std::vector<std::string> ManifestSpace::getStrArrayValue(const toml::table& tbl,
8589
return {};
8690
}
8791

88-
CManifest::CManifest(const std::string_view path)
92+
CManifest::CManifest(const fs::path& path)
8993
{
9094
try
9195
{
92-
this->m_tbl = toml::parse_file(path);
96+
this->m_tbl = toml::parse_file(path.string());
9397
}
9498
catch (const toml::parse_error& err)
9599
{
96100
die(_("Failed to parse manifest file at '{}':\n"
97101
"{}\n"
98102
"\t(error occurred at line {} column {})"),
99-
path, err.description(), err.source().begin.line, err.source().begin.column);
103+
path.string(), err.description(), err.source().begin.line, err.source().begin.column);
100104
}
101105

102-
parse_manifest();
106+
parse_manifest(path);
103107
}
104108

105-
void CManifest::parse_manifest()
109+
void CManifest::parse_manifest(const fs::path& path)
106110
{
107111
m_repo.name = getStrValue("repository", "name");
108112
m_repo.url = getStrValue("repository", "url");
@@ -112,6 +116,12 @@ void CManifest::parse_manifest()
112116
die("Manifest repository name '{}' is invalid. Only alphanumeric and '-', '_', '=' are allowed in the name",
113117
m_repo.name);
114118

119+
TinyProcessLib::Process proc(fmt::format("git -C {} rev-parse HEAD", path.parent_path().string()), "",
120+
[&](const char* buf, size_t len) { m_repo.git_hash.assign(buf, len); });
121+
if (proc.get_exit_status() != 0)
122+
die("manifest: Failed to get repository hash");
123+
m_repo.git_hash.erase(std::remove(m_repo.git_hash.begin(), m_repo.git_hash.end(), '\n'), m_repo.git_hash.end());
124+
115125
if (auto* deps = m_tbl["dependencies"].as_table())
116126
{
117127
// Collect "all" dependencies

cufetchpm/src/pluginManager.cpp

Lines changed: 63 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ static bool find_plugin_prefix(const plugin_t& plugin, const plugin_t& pending_p
6868
return false;
6969
}
7070

71+
static bool is_update = false;
72+
7173
void PluginManager::add_source_repo_plugins(const std::string& repo)
7274
{
7375
if (!has_deps(core_dependencies))
@@ -101,26 +103,76 @@ bool PluginManager::is_plugin_conflicting(const plugin_t& pending_plugin)
101103
return false;
102104
}
103105

106+
void PluginManager::update_repos()
107+
{
108+
for (const manifest_t& repo : m_state_manager.get_all_repos())
109+
{
110+
std::string output;
111+
auto func = [&](const char *buf, size_t len){output.assign(buf, len);};
112+
// the user didn't remove the cache directory, right?
113+
if (fs::exists(m_cache_path / repo.name))
114+
{
115+
debug("Repo '{}' cache path exists", repo.name);
116+
fs::current_path(m_cache_path / repo.name);
117+
if (Process({ "git", "pull", "--rebase"}, "", func, func).get_exit_status() != 0)
118+
die("Failed to 'git pull --rebase' repository {}: {}", repo.name, output);
119+
debug("git output = {}", output);
120+
121+
std::string remote;
122+
if (Process({ "git", "rev-parse", "@{u}"}, "", [&](const char *buf, size_t len){remote.assign(buf, len);}, func).get_exit_status() != 0)
123+
die("Failed to retrieve upstream hash from repository {}: {}", repo.name, output);
124+
125+
debug("remote = {} && git_hash = {}", remote, repo.git_hash);
126+
// let's avoid any spaces or newlines
127+
if (hasStart(remote, repo.git_hash))
128+
{
129+
info("{} is already up-to-date.", repo.name);
130+
continue;
131+
}
132+
133+
status("Updating {}", repo.name);
134+
is_update = true;
135+
build_plugins(m_cache_path / repo.name);
136+
}
137+
// they did, dammit
138+
else
139+
{
140+
debug("Repo '{}' cache path got deleted/not found", repo.name);
141+
if (Process({ "git", "ls-remote", repo.url, "HEAD" }, "", func, func).get_exit_status() != 0)
142+
die("Failed to retrieve latest commit from url {}: {}", repo.url, output);
143+
144+
debug("git output = {}", output);
145+
// let's avoid any spaces or newlines
146+
if (hasStart(output, repo.git_hash))
147+
{
148+
info("{} is already up-to-date.", repo.name);
149+
continue;
150+
}
151+
status("Cloning and then updating {}", repo.name);
152+
is_update = true;
153+
add_source_repo_plugins(repo.url);
154+
}
155+
}
156+
}
157+
104158
void PluginManager::build_plugins(const fs::path& working_dir)
105159
{
106160
std::vector<std::string> non_supported_plugins;
107161

108162
// cd to the working directory and parse its manifest
109163
fs::current_path(working_dir);
110-
CManifest manifest(MANIFEST_NAME);
164+
CManifest manifest(working_dir / MANIFEST_NAME);
111165

112166
// though lets check if we have already installed the plugin in the cache
113167
const fs::path& repo_cache_path = (m_cache_path / manifest.get_repo_name());
114-
if (fs::exists(repo_cache_path))
168+
if (fs::exists(repo_cache_path) && !is_update)
115169
{
116170
if (!options.install_force)
117171
{
118172
warn("Repository '{}' already exists in '{}'", manifest.get_repo_name(), repo_cache_path.string());
119173
fs::remove_all(working_dir);
120174
return;
121175
}
122-
123-
fs::remove_all(repo_cache_path);
124176
}
125177

126178
// So we don't have any plugins in the manifest uh
@@ -132,7 +184,8 @@ void PluginManager::build_plugins(const fs::path& working_dir)
132184

133185
if (!manifest.get_dependencies().empty())
134186
{
135-
info("The repository {} requires the following dependencies, check if you have them installed: {}", manifest.get_repo_name(), fmt::join(manifest.get_dependencies(), ", "));
187+
info("The repository {} requires the following dependencies, check if you have them installed: {}",
188+
manifest.get_repo_name(), fmt::join(manifest.get_dependencies(), ", "));
136189
if (!askUserYorN(true, "Are the dependencies installed?"))
137190
die("Balling out, re-install the repository again after installing all dependencies.");
138191
}
@@ -157,11 +210,12 @@ void PluginManager::build_plugins(const fs::path& working_dir)
157210
}
158211
}
159212

160-
if (is_plugin_conflicting(plugin))
213+
if (is_plugin_conflicting(plugin) && !is_update)
161214
{
162215
fs::remove_all(working_dir);
163-
error("Plugin '{}' has conflicting prefixes with other plugins.");
164-
error("Check with 'cufetcpm list' the plugins that have one of the following prefixes: {}", plugin.prefixes);
216+
error("Plugin '{}' has conflicting prefixes with other plugins.", plugin.name);
217+
error("Check with 'cufetchpm list' the plugins that have one of the following prefixes: {}",
218+
plugin.prefixes);
165219
die("Balling out");
166220
}
167221

@@ -207,7 +261,7 @@ void PluginManager::build_plugins(const fs::path& working_dir)
207261
{
208262
// ~/.config/customfetch/plugins/<manifest-directory>/<plugin-filename>
209263
const fs::path& library_config_path = manifest_config_path / library.path().filename();
210-
if (fs::exists(library_config_path) && !options.install_force)
264+
if (fs::exists(library_config_path) && (!options.install_force || !is_update))
211265
{
212266
if (askUserYorN(false, "Plugin '{}' already exists. Replace it?", library_config_path.string()))
213267
fs::remove_all(library_config_path);

0 commit comments

Comments
 (0)