feat(crates/tuono): add --head flag to download most recent template (#408)

Co-authored-by: Valerio Ageno <51341197+Valerioageno@users.noreply.github.com>
This commit is contained in:
Jacob Marshall
2025-01-25 12:42:29 +00:00
committed by GitHub
parent 3cb150b090
commit d5cc65983e
2 changed files with 80 additions and 18 deletions
+5 -1
View File
@@ -33,6 +33,9 @@ enum Actions {
/// examples /// examples
#[arg(short, long)] #[arg(short, long)]
template: Option<String>, template: Option<String>,
/// Load the latest commit available on the main branch
#[arg(long)]
head: Option<bool>,
}, },
} }
@@ -142,8 +145,9 @@ pub fn app() -> std::io::Result<()> {
Actions::New { Actions::New {
folder_name, folder_name,
template, template,
head,
} => { } => {
scaffold_project::create_new_project(folder_name, template); scaffold_project::create_new_project(folder_name, template, head);
} }
} }
+75 -17
View File
@@ -1,5 +1,6 @@
use clap::crate_version; use clap::crate_version;
use reqwest::blocking; use reqwest::blocking;
use reqwest::blocking::Client;
use serde::Deserialize; use serde::Deserialize;
use std::env; use std::env;
use std::fs::{self, create_dir, File, OpenOptions}; use std::fs::{self, create_dir, File, OpenOptions};
@@ -61,7 +62,11 @@ fn create_file(path: PathBuf, content: String) -> std::io::Result<()> {
Ok(()) Ok(())
} }
pub fn create_new_project(folder_name: Option<String>, template: Option<String>) { pub fn create_new_project(
folder_name: Option<String>,
template: Option<String>,
select_head: Option<bool>,
) {
let folder = folder_name.unwrap_or(".".to_string()); let folder = folder_name.unwrap_or(".".to_string());
// In case of missing select the tuono example // In case of missing select the tuono example
@@ -74,23 +79,10 @@ pub fn create_new_project(folder_name: Option<String>, template: Option<String>)
// This string does not include the "v" version prefix // This string does not include the "v" version prefix
let cli_version: &str = crate_version!(); let cli_version: &str = crate_version!();
let res_tag: GithubTagResponse = client let tree_url: String = generate_tree_url(select_head, &client, cli_version);
.get(format!("{}v{}", GITHUB_TUONO_TAGS_URL, cli_version))
.send()
.and_then(|response| response.json::<GithubTagResponse>())
.unwrap_or_else(|_| {
exit_with_error(&format!(
"Error: Failed to call or parse the tag github API for v{cli_version}"
))
});
let sha_tagged_commit = res_tag.object.sha;
let res_tree = client let res_tree = client
.get(format!( .get(tree_url)
"{}{}?recursive=1",
GITHUB_TUONO_TAG_COMMIT_TREES_URL, sha_tagged_commit
))
.send() .send()
.unwrap_or_else(|_| { .unwrap_or_else(|_| {
exit_with_error(&format!( exit_with_error(&format!(
@@ -134,8 +126,10 @@ pub fn create_new_project(folder_name: Option<String>, template: Option<String>)
} in new_project_files.iter() } in new_project_files.iter()
{ {
if let GithubFileType::Blob = element_type { if let GithubFileType::Blob = element_type {
let url = generate_raw_content_url(select_head, cli_version, path);
let file_content = client let file_content = client
.get(format!("{GITHUB_RAW_CONTENT_URL}/v{cli_version}/{path}")) .get(url)
.send() .send()
.map_err(|_| exit_with_error("Failed to call the folder github API")) .map_err(|_| exit_with_error("Failed to call the folder github API"))
.and_then(|response| { .and_then(|response| {
@@ -160,6 +154,36 @@ pub fn create_new_project(folder_name: Option<String>, template: Option<String>)
outro(folder); outro(folder);
} }
fn generate_raw_content_url(select_head: Option<bool>, cli_version: &str, path: &String) -> String {
let tag = if select_head.unwrap_or(false) {
"main"
} else {
&format!("v{cli_version}")
};
format!("{}/{}/{}", GITHUB_RAW_CONTENT_URL, tag, path)
}
fn generate_tree_url(select_head: Option<bool>, client: &Client, cli_version: &str) -> String {
if select_head.unwrap_or(false) {
format!("{}main?recursive=1", GITHUB_TUONO_TAG_COMMIT_TREES_URL)
} else {
// This string does not include the "v" version prefix
let res_tag = client
.get(format!("{}v{}", GITHUB_TUONO_TAGS_URL, cli_version))
.send()
.unwrap_or_else(|_| {
exit_with_error("Failed to call the tag github API for v{cli_version}")
})
.json::<GithubTagResponse>()
.unwrap_or_else(|_| exit_with_error("Failed to parse the tag response"));
format!(
"{}{}?recursive=1",
GITHUB_TUONO_TAG_COMMIT_TREES_URL, res_tag.object.sha
)
}
}
fn create_directories( fn create_directories(
new_project_files: &[&GithubFile], new_project_files: &[&GithubFile],
folder_path: &Path, folder_path: &Path,
@@ -233,3 +257,37 @@ fn outro(folder_name: String) {
println!("\nRun the local environment:"); println!("\nRun the local environment:");
println!("tuono dev"); println!("tuono dev");
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn generate_valid_content_url_from_head() {
let expected = format!(
"{}/{}/{}",
GITHUB_RAW_CONTENT_URL, "main", "examples/tuono-app"
);
let generated = generate_raw_content_url(
Some(true),
crate_version!(),
&String::from("examples/tuono-app"),
);
assert_eq!(expected, generated)
}
#[test]
fn generate_valid_content_url_from_cli_version() {
let expected = format!(
"{}/{}/{}",
GITHUB_RAW_CONTENT_URL,
&format!("v{}", crate_version!()),
"examples/tuono-app"
);
let generated = generate_raw_content_url(
Some(false),
crate_version!(),
&String::from("examples/tuono-app"),
);
assert_eq!(expected, generated)
}
}