Skip to content

Commit 03fa940

Browse files
authored
Feature/eirikb/config (#204)
* Ensure piggybacking tools get version from parent - in config! * Multi Command! This is not a task runner - I promise! * Add config version check for tool compatibility in the checker!
1 parent 763c266 commit 03fa940

File tree

5 files changed

+106
-1
lines changed

5 files changed

+106
-1
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,13 @@ Once defined, aliases can be used directly:
117117
./gg.cmd test --coverage # Expands to: ./gg.cmd npm test --coverage
118118
```
119119

120+
Aliases support `&&` for sequential execution:
121+
122+
```toml
123+
[aliases]
124+
build-and-test = "gradle clean build && npm test"
125+
```
126+
120127
### Viewing Configuration
121128

122129
View your current configuration:

src/stage4/src/checker.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,12 +297,28 @@ pub async fn check_or_update_tool(
297297
should_update: bool,
298298
allow_major: bool,
299299
force: bool,
300+
config: &crate::config::GgConfig,
300301
) {
301302
let metas = get_all_tool_metas().await;
302303

304+
let config_version = config.dependencies.get(tool_name);
305+
303306
let matching_meta = metas.into_iter().find(|(meta, _)| {
304307
if let Some(executor) = <dyn Executor>::new(meta.cmd.clone()) {
305-
executor.get_name() == tool_name
308+
let name_matches = executor.get_name() == tool_name;
309+
310+
if let Some(config_ver) = config_version {
311+
if let Some(version_req) = crate::executor::GgVersionReq::new(config_ver) {
312+
if let Some(meta_version) = &meta.download.version {
313+
return name_matches
314+
&& version_req
315+
.to_version_req()
316+
.matches(&meta_version.to_version());
317+
}
318+
}
319+
}
320+
321+
name_matches
306322
} else {
307323
false
308324
}

src/stage4/src/cli.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,21 @@ impl Cli {
165165
}
166166
} else {
167167
if let Some(first_arg) = self.args.first() {
168+
if let Some(alias_commands) = config.resolve_alias_with_and(first_arg) {
169+
if alias_commands.len() > 1 {
170+
return (
171+
vec![ClapCmd {
172+
cmd: format!("__multi_alias__{}", first_arg),
173+
version: None,
174+
distribution: None,
175+
include_tags: HashSet::new(),
176+
exclude_tags: HashSet::new(),
177+
}],
178+
self.args[1..].to_vec(),
179+
);
180+
}
181+
}
182+
168183
if let Some(alias_args) = config.resolve_alias(first_arg) {
169184
let mut expanded_args = alias_args;
170185
expanded_args.extend_from_slice(&self.args[1..]);
@@ -314,6 +329,16 @@ fn parse_command_string(cmd_string: &str, config: &GgConfig) -> Vec<ClapCmd> {
314329
if version.is_none() {
315330
if let Some(dep_version) = config.dependencies.get(&base_cmd) {
316331
version = Some(dep_version.clone());
332+
} else {
333+
// Piggybacking!
334+
let underlying_tool = match base_cmd.as_str() {
335+
"npm" | "npx" => "node",
336+
"dart" => "flutter",
337+
_ => &base_cmd,
338+
};
339+
if let Some(dep_version) = config.dependencies.get(underlying_tool) {
340+
version = Some(dep_version.clone());
341+
}
317342
}
318343
}
319344

src/stage4/src/config.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,16 @@ impl GgConfig {
7171
.map(|alias_cmd| shlex::split(alias_cmd).unwrap_or_else(|| vec![alias_cmd.clone()]))
7272
}
7373

74+
pub fn resolve_alias_with_and(&self, command: &str) -> Option<Vec<Vec<String>>> {
75+
self.aliases.get(command).map(|alias_cmd| {
76+
alias_cmd
77+
.split("&&")
78+
.map(|cmd| cmd.trim())
79+
.map(|cmd| shlex::split(cmd).unwrap_or_else(|| vec![cmd.to_string()]))
80+
.collect()
81+
})
82+
}
83+
7484
pub fn init_config() -> Result<(), String> {
7585
let config_path = Path::new("gg.toml");
7686

@@ -196,4 +206,19 @@ test = "npm test"
196206

197207
assert!(config.resolve_alias("nonexistent").is_none());
198208
}
209+
210+
#[test]
211+
fn test_alias_with_and_operator() {
212+
let mut config = GgConfig::default();
213+
config.aliases.insert(
214+
"build-and-test".to_string(),
215+
"gradle clean build && npm test".to_string(),
216+
);
217+
218+
let resolved = config.resolve_alias_with_and("build-and-test").unwrap();
219+
assert_eq!(
220+
resolved,
221+
vec![vec!["gradle", "clean", "build"], vec!["npm", "test"]]
222+
);
223+
}
199224
}

src/stage4/src/main.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,37 @@ async fn main() -> ExitCode {
249249
debug!(target: "main", "{:?}", &cli);
250250

251251
if let Some(cmd) = cmds.first() {
252+
if cmd.cmd.starts_with("__multi_alias__") {
253+
let alias_name = cmd.cmd.strip_prefix("__multi_alias__").unwrap();
254+
if let Some(alias_commands) = config.resolve_alias_with_and(alias_name) {
255+
for command_args in alias_commands {
256+
println!("Executing: {}", command_args.join(" "));
257+
258+
let mut gg_args =
259+
vec![env::current_exe().unwrap().to_string_lossy().to_string()];
260+
gg_args.extend(command_args.iter().map(|s| s.to_string()));
261+
262+
let status = std::process::Command::new(&gg_args[0])
263+
.args(&gg_args[1..])
264+
.status();
265+
266+
match status {
267+
Ok(exit_status) => {
268+
if !exit_status.success() {
269+
println!("Command failed: {}", command_args.join(" "));
270+
return ExitCode::from(exit_status.code().unwrap_or(1) as u8);
271+
}
272+
}
273+
Err(e) => {
274+
println!("Failed to execute command: {}", e);
275+
return ExitCode::from(1);
276+
}
277+
}
278+
}
279+
return ExitCode::from(0);
280+
}
281+
}
282+
252283
match cmd.cmd.as_str() {
253284
"update" => {
254285
let tool_name = app_args.first().cloned();
@@ -281,6 +312,7 @@ async fn main() -> ExitCode {
281312
should_update,
282313
allow_major,
283314
force,
315+
&config,
284316
)
285317
.await;
286318
}

0 commit comments

Comments
 (0)