@repokit/core 3.0.2 → 3.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +1056 -2
- package/Cargo.toml +4 -1
- package/dist/CommandParser.mjs +3 -3
- package/externals/CommandParser.ts +3 -3
- package/installation/install.sh +29 -11
- package/internals/configuration/configuration.rs +3 -2
- package/internals/configuration/mod.rs +2 -0
- package/internals/configuration/recovery.rs +42 -0
- package/internals/{internal_commands → configuration}/typescript_command.rs +20 -22
- package/internals/executables/internal_executable_definition.rs +1 -1
- package/internals/executables/mod.rs +1 -2
- package/internals/executor/executor.rs +13 -0
- package/internals/internal_commands/help.rs +5 -2
- package/internals/internal_commands/internal_registry.rs +6 -4
- package/internals/internal_commands/list_commands.rs +2 -2
- package/internals/internal_commands/list_owners.rs +2 -2
- package/internals/internal_commands/list_themes.rs +1 -1
- package/internals/internal_commands/list_version.rs +60 -0
- package/internals/internal_commands/locate_command.rs +5 -4
- package/internals/internal_commands/mod.rs +1 -1
- package/internals/internal_commands/onboarder.rs +1 -1
- package/internals/internal_commands/register_command.rs +3 -3
- package/internals/internal_commands/search_commands.rs +2 -2
- package/internals/internal_commands/upgrade_repokit.rs +37 -34
- package/internals/internal_filesystem/file_builder.rs +4 -0
- package/internals/internal_filesystem/internal_filesystem.rs +101 -9
- package/internals/logger/logger.rs +26 -14
- package/internals/main.rs +6 -3
- package/internals/post_processing/mod.rs +1 -0
- package/internals/post_processing/post_processor.rs +37 -0
- package/internals/repokit/command_definition.rs +11 -0
- package/internals/repokit/mod.rs +5 -1
- package/internals/repokit/repokit.rs +10 -14
- package/internals/repokit/repokit_command.rs +96 -0
- package/internals/repokit/repokit_config.rs +75 -0
- package/internals/repokit/repokit_construct_validator.rs +14 -0
- package/internals/repokit/runtime_compiler.rs +61 -0
- package/internals/themes/theme_inputs.rs +3 -2
- package/internals/validations/command_validations.rs +4 -5
- package/package.json +1 -1
- package/internals/executables/external_executable.rs +0 -4
- package/internals/repokit/interfaces.rs +0 -48
- /package/internals/executables/{intenal_executable.rs → internal_executable.rs} +0 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
use crate::post_processing::post_processor::PostProcessor;
|
|
1
2
|
use std::{
|
|
2
3
|
fs::{File, create_dir_all},
|
|
3
4
|
io::{Error, copy},
|
|
@@ -14,6 +15,7 @@ impl FileBuilder {
|
|
|
14
15
|
Ok(file) => file,
|
|
15
16
|
Err(error) => {
|
|
16
17
|
on_error(error);
|
|
18
|
+
PostProcessor::get().flush();
|
|
17
19
|
exit(0);
|
|
18
20
|
}
|
|
19
21
|
}
|
|
@@ -25,6 +27,7 @@ impl FileBuilder {
|
|
|
25
27
|
Ok(file) => file,
|
|
26
28
|
Err(error) => {
|
|
27
29
|
on_error(error);
|
|
30
|
+
PostProcessor::get().flush();
|
|
28
31
|
exit(0);
|
|
29
32
|
}
|
|
30
33
|
}
|
|
@@ -50,6 +53,7 @@ impl FileBuilder {
|
|
|
50
53
|
Ok(result) => result,
|
|
51
54
|
Err(error) => {
|
|
52
55
|
on_error(error);
|
|
56
|
+
PostProcessor::get().flush();
|
|
53
57
|
exit(0);
|
|
54
58
|
}
|
|
55
59
|
}
|
|
@@ -3,9 +3,11 @@ use normalize_path::NormalizePath;
|
|
|
3
3
|
use regex::Regex;
|
|
4
4
|
use shellexpand::tilde;
|
|
5
5
|
use std::{
|
|
6
|
+
collections::HashMap,
|
|
6
7
|
fs::{self, File},
|
|
7
8
|
io::{BufRead, BufReader, Lines},
|
|
8
9
|
path::{Path, PathBuf},
|
|
10
|
+
sync::LazyLock,
|
|
9
11
|
};
|
|
10
12
|
|
|
11
13
|
use crate::{
|
|
@@ -13,6 +15,9 @@ use crate::{
|
|
|
13
15
|
logger::logger::Logger,
|
|
14
16
|
};
|
|
15
17
|
|
|
18
|
+
pub static VERSION_REGEX: LazyLock<Regex> =
|
|
19
|
+
LazyLock::new(|| Regex::new(r#"\d*\.\d*.\d*"#).unwrap());
|
|
20
|
+
|
|
16
21
|
pub struct InternalFileSystem {
|
|
17
22
|
root: String,
|
|
18
23
|
}
|
|
@@ -114,6 +119,77 @@ impl InternalFileSystem {
|
|
|
114
119
|
None
|
|
115
120
|
}
|
|
116
121
|
|
|
122
|
+
pub fn get_package_manager(root: &str) -> &str {
|
|
123
|
+
let manager_map = HashMap::from([
|
|
124
|
+
("npm", "package-lock.json"),
|
|
125
|
+
("yarn", "yarn.lock"),
|
|
126
|
+
("pnpm", "pnpm-lock.yaml"),
|
|
127
|
+
("bun", "bun.lockb"),
|
|
128
|
+
]);
|
|
129
|
+
for (manager, lock_file) in manager_map {
|
|
130
|
+
let path = Path::new(&root).join(lock_file).normalize();
|
|
131
|
+
if path.exists() && path.is_file() {
|
|
132
|
+
return manager;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
"npm"
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
pub fn get_install_command(root: &str) -> &str {
|
|
139
|
+
let npm_install = "npm i -D";
|
|
140
|
+
let package_manager = InternalFileSystem::get_package_manager(root);
|
|
141
|
+
let manager_map = HashMap::from([
|
|
142
|
+
("npm", npm_install),
|
|
143
|
+
("yarn", "yarn add -D"),
|
|
144
|
+
("pnpm", "pnpm i -D"),
|
|
145
|
+
("bun", "bun add -D"),
|
|
146
|
+
]);
|
|
147
|
+
manager_map.get(package_manager).unwrap_or(&npm_install)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
pub fn get_node_executor(root: &str) -> &str {
|
|
151
|
+
let npx = "npx";
|
|
152
|
+
let package_manager = InternalFileSystem::get_package_manager(root);
|
|
153
|
+
let manager_map = HashMap::from([
|
|
154
|
+
("npm", "npx"),
|
|
155
|
+
("yarn", "yarn run -T"),
|
|
156
|
+
("pnpm", "pnpm run"),
|
|
157
|
+
("bun", "bunx"),
|
|
158
|
+
]);
|
|
159
|
+
manager_map.get(package_manager).unwrap_or(&npx)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
pub fn get_typescript_version(node_executor: &str) -> u32 {
|
|
163
|
+
let stdout = Executor::exec(format!("{} tsc --version", node_executor), |cmd| cmd);
|
|
164
|
+
let lines: Vec<&str> = stdout
|
|
165
|
+
.split("\n")
|
|
166
|
+
.filter_map(|s| {
|
|
167
|
+
let trimmed = s.trim();
|
|
168
|
+
if trimmed.is_empty() {
|
|
169
|
+
return None;
|
|
170
|
+
}
|
|
171
|
+
Some(trimmed)
|
|
172
|
+
})
|
|
173
|
+
.collect();
|
|
174
|
+
let fallback_version = "5.0.0";
|
|
175
|
+
let version = lines.last().unwrap_or(&fallback_version);
|
|
176
|
+
let captures: Vec<String> = VERSION_REGEX
|
|
177
|
+
.captures_iter(version)
|
|
178
|
+
.filter_map(|item| {
|
|
179
|
+
item.get(0)
|
|
180
|
+
.map(|match_text| match_text.as_str().to_string())
|
|
181
|
+
})
|
|
182
|
+
.collect();
|
|
183
|
+
let fallback_version_str = fallback_version.to_string();
|
|
184
|
+
let semver = captures.first().unwrap_or(&fallback_version_str);
|
|
185
|
+
semver
|
|
186
|
+
.chars()
|
|
187
|
+
.next()
|
|
188
|
+
.unwrap_or('5')
|
|
189
|
+
.to_digit(10)
|
|
190
|
+
.unwrap_or(5)
|
|
191
|
+
}
|
|
192
|
+
|
|
117
193
|
fn commands_directory(&self) -> PathBuf {
|
|
118
194
|
self.absolute(format!("{}/dist/commands", self.package_directory()).as_str())
|
|
119
195
|
}
|
|
@@ -122,7 +198,7 @@ impl InternalFileSystem {
|
|
|
122
198
|
self.absolute(format!("{}/externals/templates", self.package_directory()).as_str())
|
|
123
199
|
}
|
|
124
200
|
|
|
125
|
-
fn package_directory(&self) -> String {
|
|
201
|
+
pub fn package_directory(&self) -> String {
|
|
126
202
|
format!("./node_modules/{}", self.package_name())
|
|
127
203
|
}
|
|
128
204
|
|
|
@@ -142,7 +218,7 @@ impl InternalFileSystem {
|
|
|
142
218
|
Some(home) => {
|
|
143
219
|
let dot_file_path = home.join(".repokit");
|
|
144
220
|
if !&dot_file_path.exists() {
|
|
145
|
-
let found_version = self.
|
|
221
|
+
let found_version = self.installed_repokit_version();
|
|
146
222
|
found_version.as_ref()?;
|
|
147
223
|
let version_string = found_version.unwrap();
|
|
148
224
|
let result = fs::write(&dot_file_path, format!("{version_string}\n"));
|
|
@@ -171,7 +247,7 @@ impl InternalFileSystem {
|
|
|
171
247
|
}
|
|
172
248
|
}
|
|
173
249
|
|
|
174
|
-
pub fn
|
|
250
|
+
pub fn installed_repokit_version(&self) -> Option<String> {
|
|
175
251
|
let package_path = Path::new(&self.root)
|
|
176
252
|
.join(InternalFileSystem::package_directory(self))
|
|
177
253
|
.normalize();
|
|
@@ -182,7 +258,7 @@ impl InternalFileSystem {
|
|
|
182
258
|
}
|
|
183
259
|
let lines = BufReader::new(file.unwrap()).lines();
|
|
184
260
|
let version_matcher = Regex::new(r#""([^"]*)""#).unwrap();
|
|
185
|
-
for line in lines.
|
|
261
|
+
for line in lines.map_while(Result::ok) {
|
|
186
262
|
if line.contains("\"version\": ") {
|
|
187
263
|
let captures: Vec<String> = version_matcher
|
|
188
264
|
.captures_iter(&line)
|
|
@@ -191,12 +267,28 @@ impl InternalFileSystem {
|
|
|
191
267
|
.map(|match_text| match_text.as_str().to_string())
|
|
192
268
|
})
|
|
193
269
|
.collect();
|
|
194
|
-
let version = captures.get(1)
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
return None;
|
|
270
|
+
if let Some(version) = captures.get(1)
|
|
271
|
+
&& VERSION_REGEX.is_match(version)
|
|
272
|
+
{
|
|
273
|
+
return Some(version.to_string());
|
|
199
274
|
}
|
|
275
|
+
return None;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
None
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
pub fn runtime_repokit_version() -> Option<String> {
|
|
282
|
+
if let Some(home) = InternalFileSystem::home() {
|
|
283
|
+
let version = Executor::exec(
|
|
284
|
+
format!(
|
|
285
|
+
"head -n 1 {}",
|
|
286
|
+
home.join(".repokit").normalize().to_str().unwrap()
|
|
287
|
+
),
|
|
288
|
+
|cmd| cmd,
|
|
289
|
+
);
|
|
290
|
+
if VERSION_REGEX.is_match(&version) {
|
|
291
|
+
return Some(version);
|
|
200
292
|
}
|
|
201
293
|
}
|
|
202
294
|
None
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
use std::process::exit;
|
|
2
1
|
use std::sync::LazyLock;
|
|
3
2
|
use std::sync::Mutex;
|
|
4
3
|
use std::sync::MutexGuard;
|
|
5
4
|
|
|
6
5
|
use colored::{ColoredString, Colorize};
|
|
7
6
|
|
|
7
|
+
use crate::post_processing::post_processor::PostProcessor;
|
|
8
8
|
use crate::themes::theme::Theme;
|
|
9
9
|
use crate::themes::theme_registry::ThemeRegistry;
|
|
10
10
|
|
|
@@ -13,7 +13,7 @@ static REGISTERED_NAME: LazyLock<Mutex<String>> =
|
|
|
13
13
|
|
|
14
14
|
static THEMES: LazyLock<Mutex<ThemeRegistry>> = LazyLock::new(|| Mutex::new(ThemeRegistry::new()));
|
|
15
15
|
|
|
16
|
-
pub struct Logger
|
|
16
|
+
pub struct Logger;
|
|
17
17
|
|
|
18
18
|
impl Logger {
|
|
19
19
|
pub fn set_name(value: &str) {
|
|
@@ -30,20 +30,18 @@ impl Logger {
|
|
|
30
30
|
|
|
31
31
|
pub fn exit_with_info(message: &str) {
|
|
32
32
|
Logger::info(message);
|
|
33
|
-
|
|
33
|
+
PostProcessor::get().flush();
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
pub fn exit_with_error(message: &str) {
|
|
37
37
|
Logger::error(message);
|
|
38
|
-
|
|
38
|
+
PostProcessor::get().flush();
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
pub fn list(items: &[&str], indentation: Option<i32>) {
|
|
42
42
|
Logger::with_surrounding_space(|| {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
println!("{}{}. {}", Logger::indent(indentation), pointer + 1, item);
|
|
46
|
-
pointer += 1
|
|
43
|
+
for (index, item) in items.iter().enumerate() {
|
|
44
|
+
println!("{}{}. {}", Logger::indent(indentation), index + 1, item);
|
|
47
45
|
}
|
|
48
46
|
})
|
|
49
47
|
}
|
|
@@ -60,11 +58,25 @@ impl Logger {
|
|
|
60
58
|
}
|
|
61
59
|
|
|
62
60
|
pub fn log_file_path(path: &str) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
61
|
+
Logger::with_surrounding_space(|| {
|
|
62
|
+
println!(
|
|
63
|
+
"{}{}",
|
|
64
|
+
Logger::indent(None),
|
|
65
|
+
Logger::with_theme(|theme| theme.highlight(path))
|
|
66
|
+
);
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
pub fn list_file_paths(paths: &Vec<String>) {
|
|
71
|
+
Logger::with_surrounding_space(|| {
|
|
72
|
+
for path in paths {
|
|
73
|
+
println!(
|
|
74
|
+
"{}{}",
|
|
75
|
+
Logger::indent(None),
|
|
76
|
+
Logger::with_theme(|theme| theme.highlight(path))
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
})
|
|
68
80
|
}
|
|
69
81
|
|
|
70
82
|
pub fn indent(times: Option<i32>) -> String {
|
|
@@ -109,7 +121,7 @@ impl Logger {
|
|
|
109
121
|
Logger::info(format!("I was unable to {operation} in your repository").as_str());
|
|
110
122
|
Logger::error("Please verify the permissions on your working directory or file a bug here");
|
|
111
123
|
Logger::log_issue_link();
|
|
112
|
-
|
|
124
|
+
PostProcessor::get().flush();
|
|
113
125
|
}
|
|
114
126
|
|
|
115
127
|
fn info_prefix() -> String {
|
package/internals/main.rs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
use crate::{
|
|
2
|
-
|
|
3
|
-
internal_filesystem::internal_filesystem::InternalFileSystem,
|
|
2
|
+
configuration::typescript_command::TypescriptCommand,
|
|
3
|
+
internal_filesystem::internal_filesystem::InternalFileSystem,
|
|
4
|
+
repokit::{repokit::RepoKit, runtime_compiler::RuntimeCompiler},
|
|
4
5
|
};
|
|
5
6
|
|
|
6
7
|
mod argv;
|
|
@@ -11,13 +12,15 @@ mod file_walker;
|
|
|
11
12
|
mod internal_commands;
|
|
12
13
|
mod internal_filesystem;
|
|
13
14
|
mod logger;
|
|
15
|
+
mod post_processing;
|
|
14
16
|
mod repokit;
|
|
15
17
|
mod themes;
|
|
16
18
|
mod validations;
|
|
17
19
|
|
|
18
20
|
fn main() {
|
|
19
21
|
let root = InternalFileSystem::find_root();
|
|
22
|
+
RuntimeCompiler::hop_to_runtime_version(&root);
|
|
20
23
|
let config = TypescriptCommand::new(&root).parse_configuration();
|
|
21
|
-
let kit = RepoKit::new(root, config);
|
|
24
|
+
let kit = RepoKit::new(&root, config);
|
|
22
25
|
kit.invoke();
|
|
23
26
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pub mod post_processor;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
use std::{
|
|
2
|
+
process::exit,
|
|
3
|
+
sync::{LazyLock, Mutex, MutexGuard},
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
pub struct PostProcessor {
|
|
7
|
+
tasks: Vec<Box<dyn Fn() + Send + 'static>>,
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
impl PostProcessor {
|
|
11
|
+
pub fn new() -> Self {
|
|
12
|
+
PostProcessor { tasks: Vec::new() }
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
pub fn get() -> MutexGuard<'static, PostProcessor> {
|
|
16
|
+
REPOKIT_POST_PROCESSOR.lock().unwrap()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
pub fn register_task<F>(&mut self, task: F)
|
|
20
|
+
where
|
|
21
|
+
F: Fn() + Send + 'static,
|
|
22
|
+
{
|
|
23
|
+
self.tasks.push(Box::new(task));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
pub fn flush(&mut self) {
|
|
27
|
+
for task in self.tasks.iter() {
|
|
28
|
+
task();
|
|
29
|
+
}
|
|
30
|
+
self.tasks.clear();
|
|
31
|
+
self.tasks.shrink_to_fit();
|
|
32
|
+
exit(0);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
static REPOKIT_POST_PROCESSOR: LazyLock<Mutex<PostProcessor>> =
|
|
37
|
+
LazyLock::new(|| Mutex::new(PostProcessor::new()));
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
use std::collections::HashMap;
|
|
2
|
+
|
|
3
|
+
use schemars::JsonSchema;
|
|
4
|
+
use serde::Deserialize;
|
|
5
|
+
|
|
6
|
+
#[derive(Debug, Deserialize, Clone, JsonSchema)]
|
|
7
|
+
pub struct CommandDefinition {
|
|
8
|
+
pub command: String,
|
|
9
|
+
pub description: String,
|
|
10
|
+
pub args: Option<HashMap<String, String>>,
|
|
11
|
+
}
|
package/internals/repokit/mod.rs
CHANGED
|
@@ -1,19 +1,15 @@
|
|
|
1
|
-
use std::{
|
|
2
|
-
collections::HashMap,
|
|
3
|
-
env::args,
|
|
4
|
-
path::Path,
|
|
5
|
-
process::{self},
|
|
6
|
-
};
|
|
1
|
+
use std::{collections::HashMap, env::args, path::Path};
|
|
7
2
|
|
|
8
3
|
use crate::{
|
|
9
4
|
executables::{
|
|
10
|
-
|
|
5
|
+
internal_executable::InternalExecutable, internal_executable_definition::RepoKitScope,
|
|
11
6
|
},
|
|
12
7
|
executor::executor::Executor,
|
|
13
8
|
internal_commands::help::Help,
|
|
14
9
|
internal_filesystem::internal_filesystem::InternalFileSystem,
|
|
15
10
|
logger::logger::Logger,
|
|
16
|
-
|
|
11
|
+
post_processing::post_processor::PostProcessor,
|
|
12
|
+
repokit::{repokit_command::RepoKitCommand, repokit_config::RepoKitConfig},
|
|
17
13
|
validations::command_validations::CommandValidations,
|
|
18
14
|
};
|
|
19
15
|
|
|
@@ -22,17 +18,17 @@ pub struct RepoKit {
|
|
|
22
18
|
}
|
|
23
19
|
|
|
24
20
|
impl RepoKit {
|
|
25
|
-
pub fn new(root:
|
|
21
|
+
pub fn new(root: &str, configuration: RepoKitConfig) -> RepoKit {
|
|
26
22
|
Logger::set_name(&configuration.project);
|
|
27
23
|
for theme in &configuration.themes {
|
|
28
24
|
Logger::with_registry(|mut registry| registry.register_user_theme(theme))
|
|
29
25
|
}
|
|
30
|
-
let theme = InternalFileSystem::new(
|
|
31
|
-
Logger::with_registry(|mut registry| registry.set_theme(
|
|
26
|
+
let theme = InternalFileSystem::new(root).read_theme_preference();
|
|
27
|
+
Logger::with_registry(|mut registry| registry.set_theme(root, &theme));
|
|
32
28
|
RepoKit {
|
|
33
29
|
scope: RepoKitScope {
|
|
34
|
-
root,
|
|
35
30
|
configuration,
|
|
31
|
+
root: root.to_string(),
|
|
36
32
|
},
|
|
37
33
|
}
|
|
38
34
|
}
|
|
@@ -42,7 +38,7 @@ impl RepoKit {
|
|
|
42
38
|
let validator = CommandValidations::from(self);
|
|
43
39
|
let internals = validator.collect_and_validate_internals();
|
|
44
40
|
if internals.contains_key(&command) {
|
|
45
|
-
let interface = internals.get(&command).expect("
|
|
41
|
+
let interface = internals.get(&command).expect("known command");
|
|
46
42
|
return interface.run(args, &internals);
|
|
47
43
|
}
|
|
48
44
|
if self.scope.configuration.commands.contains_key(&command) {
|
|
@@ -87,7 +83,7 @@ impl RepoKit {
|
|
|
87
83
|
if argv.len() < 2 {
|
|
88
84
|
let (internals, externals) = self.collect_and_validate();
|
|
89
85
|
Help::list_all(&self.scope.configuration.commands, &internals, &externals);
|
|
90
|
-
|
|
86
|
+
PostProcessor::get().flush();
|
|
91
87
|
}
|
|
92
88
|
let command = &argv[1];
|
|
93
89
|
let args = &(&argv)[2..];
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
use std::{
|
|
2
|
+
collections::HashMap,
|
|
3
|
+
sync::LazyLock,
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
use jsonschema::Validator;
|
|
7
|
+
use schemars::JsonSchema;
|
|
8
|
+
use serde::Deserialize;
|
|
9
|
+
use serde_json::{Value, from_value, to_value};
|
|
10
|
+
|
|
11
|
+
use crate::{
|
|
12
|
+
configuration::recovery::Recovery,
|
|
13
|
+
logger::logger::Logger,
|
|
14
|
+
post_processing::post_processor::PostProcessor,
|
|
15
|
+
repokit::{
|
|
16
|
+
command_definition::CommandDefinition,
|
|
17
|
+
repokit_construct_validator::RepoKitConstructValidator,
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
#[derive(Debug, Deserialize, Clone, JsonSchema)]
|
|
22
|
+
pub struct RepoKitCommand {
|
|
23
|
+
pub name: String,
|
|
24
|
+
pub owner: String,
|
|
25
|
+
pub location: String,
|
|
26
|
+
pub description: String,
|
|
27
|
+
pub commands: HashMap<String, CommandDefinition>,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
static REPOKIT_COMMAND_VALIDATOR: LazyLock<Validator> = LazyLock::new(|| {
|
|
31
|
+
Validator::new(&to_value(schemars::schema_for!(RepoKitCommand)).unwrap()).unwrap()
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
impl RepoKitCommand {
|
|
35
|
+
fn register_encountered_errors(root: &str, failed_paths: Vec<String>) {
|
|
36
|
+
let root_clone = root.to_string();
|
|
37
|
+
PostProcessor::get().register_task(move || {
|
|
38
|
+
println!();
|
|
39
|
+
if !failed_paths.is_empty() {
|
|
40
|
+
let appendage = if failed_paths.len() != 1 { "s" } else { "" };
|
|
41
|
+
Logger::error(
|
|
42
|
+
format!(
|
|
43
|
+
"I encountered an error in the following command{}",
|
|
44
|
+
appendage
|
|
45
|
+
)
|
|
46
|
+
.as_str(),
|
|
47
|
+
);
|
|
48
|
+
Logger::list_file_paths(&failed_paths);
|
|
49
|
+
} else {
|
|
50
|
+
Logger::info("There was an error parsing one or more of your commands");
|
|
51
|
+
}
|
|
52
|
+
Logger::info("You can validate a command file's syntactical correctness by running");
|
|
53
|
+
Logger::log_file_path(
|
|
54
|
+
&Recovery::new(&root_clone).get_typecheck_command("<optional-path-to-file>"),
|
|
55
|
+
);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
impl RepoKitConstructValidator<Vec<Value>, Vec<RepoKitCommand>> for RepoKitCommand {
|
|
61
|
+
fn from_input(root: &str, input: Vec<Value>) -> Vec<RepoKitCommand> {
|
|
62
|
+
let mut result: Vec<RepoKitCommand> = Vec::new();
|
|
63
|
+
let mut failures = 0;
|
|
64
|
+
let mut failed_paths: Vec<String> = Vec::new();
|
|
65
|
+
for command in input {
|
|
66
|
+
let repokit_command: Result<RepoKitCommand, serde_json::Error> =
|
|
67
|
+
from_value(command.clone());
|
|
68
|
+
if !RepoKitCommand::is_valid(&REPOKIT_COMMAND_VALIDATOR, &command)
|
|
69
|
+
|| repokit_command.is_err()
|
|
70
|
+
{
|
|
71
|
+
failures += 1;
|
|
72
|
+
if let Some(path) = RepoKitCommand::on_parsing_error(root, command) {
|
|
73
|
+
failed_paths.push(path);
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
let mut valid_command = repokit_command.expect("assertion success");
|
|
77
|
+
valid_command.location = format!("{}/{}", root, valid_command.location);
|
|
78
|
+
result.push(valid_command);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if failures != 0 {
|
|
82
|
+
RepoKitCommand::register_encountered_errors(root, failed_paths);
|
|
83
|
+
}
|
|
84
|
+
result
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
fn on_parsing_error(root: &str, command: Value) -> Option<String> {
|
|
88
|
+
let location = command.get("location");
|
|
89
|
+
println!();
|
|
90
|
+
if location.is_some_and(|v| v.is_string()) {
|
|
91
|
+
let path = format!("{}/{}", &root, location.unwrap().as_str().unwrap());
|
|
92
|
+
return Some(path);
|
|
93
|
+
}
|
|
94
|
+
None
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
use std::{
|
|
2
|
+
collections::HashMap,
|
|
3
|
+
path::Path,
|
|
4
|
+
process::exit,
|
|
5
|
+
sync::LazyLock,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
use jsonschema::Validator;
|
|
9
|
+
use schemars::JsonSchema;
|
|
10
|
+
use serde::Deserialize;
|
|
11
|
+
use serde_json::{Value, from_value, to_value};
|
|
12
|
+
|
|
13
|
+
use crate::{
|
|
14
|
+
configuration::recovery::Recovery,
|
|
15
|
+
logger::logger::Logger,
|
|
16
|
+
post_processing::post_processor::PostProcessor,
|
|
17
|
+
repokit::{
|
|
18
|
+
command_definition::CommandDefinition, repokit_command::RepoKitCommand,
|
|
19
|
+
repokit_construct_validator::RepoKitConstructValidator,
|
|
20
|
+
},
|
|
21
|
+
themes::theme_inputs::RepoKitTheme,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
#[derive(Debug, Deserialize, Clone, JsonSchema)]
|
|
25
|
+
pub struct RootCommand {
|
|
26
|
+
pub name: String,
|
|
27
|
+
pub command: String,
|
|
28
|
+
pub description: String,
|
|
29
|
+
pub args: Option<HashMap<String, String>>,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
impl RootCommand {
|
|
33
|
+
pub fn from(name: &str, command: &CommandDefinition) -> RootCommand {
|
|
34
|
+
RootCommand {
|
|
35
|
+
name: name.to_string(),
|
|
36
|
+
args: command.args.clone(),
|
|
37
|
+
command: command.command.to_string(),
|
|
38
|
+
description: command.description.to_string(),
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
#[derive(Debug, Deserialize, Clone, JsonSchema)]
|
|
44
|
+
pub struct RepoKitConfig {
|
|
45
|
+
pub project: String,
|
|
46
|
+
pub thirdParty: Vec<RepoKitCommand>,
|
|
47
|
+
pub commands: HashMap<String, CommandDefinition>,
|
|
48
|
+
pub themes: Vec<RepoKitTheme>,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
static REPOKIT_CONFIG_VALIDATOR: LazyLock<Validator> = LazyLock::new(|| {
|
|
52
|
+
Validator::new(&to_value(schemars::schema_for!(RepoKitConfig)).unwrap()).unwrap()
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
impl RepoKitConstructValidator<Value, RepoKitConfig> for RepoKitConfig {
|
|
56
|
+
fn from_input(root: &str, input: Value) -> RepoKitConfig {
|
|
57
|
+
let repokit_config: Result<RepoKitConfig, serde_json::Error> = from_value(input.clone());
|
|
58
|
+
if !RepoKitConfig::is_valid(&REPOKIT_CONFIG_VALIDATOR, &input) || repokit_config.is_err() {
|
|
59
|
+
RepoKitConfig::on_parsing_error(root, Value::Null);
|
|
60
|
+
}
|
|
61
|
+
repokit_config.expect("assertions succeeded")
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
fn on_parsing_error(root: &str, _: Value) -> Option<String> {
|
|
65
|
+
let path_buf = Path::new(&root).join("repokit.ts");
|
|
66
|
+
let path = path_buf.to_str().expect("exists");
|
|
67
|
+
let mut recovery = Recovery::new(root);
|
|
68
|
+
recovery.run(path);
|
|
69
|
+
println!();
|
|
70
|
+
Logger::info("There was an error parsing your configuration");
|
|
71
|
+
recovery.prompt_to_fix_errors(path);
|
|
72
|
+
PostProcessor::get().flush();
|
|
73
|
+
exit(0);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
use jsonschema::Validator;
|
|
2
|
+
use serde_json::Value;
|
|
3
|
+
|
|
4
|
+
pub trait RepoKitConstructValidator<T, V> {
|
|
5
|
+
fn from_input(root: &str, input: T) -> V;
|
|
6
|
+
fn on_parsing_error(root: &str, value: Value) -> Option<String>;
|
|
7
|
+
|
|
8
|
+
fn is_valid(validator: &Validator, input: &Value) -> bool {
|
|
9
|
+
if validator.validate(input).is_err() {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
true
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
use std::{env::args, path::PathBuf, process::exit};
|
|
2
|
+
|
|
3
|
+
use normalize_path::NormalizePath;
|
|
4
|
+
use terminal_spinners::{BOUNCING_BALL, SpinnerBuilder};
|
|
5
|
+
|
|
6
|
+
use crate::{
|
|
7
|
+
executor::executor::Executor, internal_filesystem::internal_filesystem::InternalFileSystem,
|
|
8
|
+
logger::logger::Logger,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
pub struct RuntimeCompiler;
|
|
12
|
+
|
|
13
|
+
impl RuntimeCompiler {
|
|
14
|
+
pub fn hop_to_runtime_version(root: &str) {
|
|
15
|
+
RuntimeCompiler::with_version_mismatch(root, |installed_version, fs| {
|
|
16
|
+
let package_path = fs.absolute(&fs.package_directory());
|
|
17
|
+
let install_path = package_path.join("installation/install.sh").normalize();
|
|
18
|
+
if install_path.is_absolute() && install_path.exists() {
|
|
19
|
+
Logger::info(
|
|
20
|
+
format!(
|
|
21
|
+
"Switching to version {}",
|
|
22
|
+
Logger::with_theme(|theme| theme.highlight(installed_version))
|
|
23
|
+
)
|
|
24
|
+
.as_str(),
|
|
25
|
+
);
|
|
26
|
+
if let Some(errors) = RuntimeCompiler::run_post_install(&package_path) {
|
|
27
|
+
println!("{errors}");
|
|
28
|
+
} else {
|
|
29
|
+
RuntimeCompiler::re_run_command();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
fn with_version_mismatch(root: &str, func: impl Fn(&str, InternalFileSystem)) {
|
|
36
|
+
let scoped_fs = InternalFileSystem::new(root);
|
|
37
|
+
if let Some(installed_version) = scoped_fs.installed_repokit_version()
|
|
38
|
+
&& let Some(runtime_version) = InternalFileSystem::runtime_repokit_version()
|
|
39
|
+
&& runtime_version != installed_version
|
|
40
|
+
{
|
|
41
|
+
func(&installed_version, scoped_fs);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
fn run_post_install(cwd: &PathBuf) -> Option<String> {
|
|
46
|
+
let handle = SpinnerBuilder::new()
|
|
47
|
+
.spinner(&BOUNCING_BALL)
|
|
48
|
+
.text(" Installing")
|
|
49
|
+
.start();
|
|
50
|
+
let result =
|
|
51
|
+
Executor::exec_with_errors("./installation/install.sh", |cmd| cmd.current_dir(cwd));
|
|
52
|
+
handle.done();
|
|
53
|
+
result
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
fn re_run_command() {
|
|
57
|
+
let args: Vec<String> = args().collect();
|
|
58
|
+
Executor::with_stdio(args.join(" "), |cmd| cmd);
|
|
59
|
+
exit(0);
|
|
60
|
+
}
|
|
61
|
+
}
|