Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/publish-npm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ jobs:
console.error(`Run: npm install -g ${pkg}`);
process.exit(1);
} else {
throw e;
process.exit(e.status || 1);
}
}
EOF
Expand Down
23 changes: 17 additions & 6 deletions src/commands/gitignore/preview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::utils::pretty_print;
use crate::utils::progress;
use crate::utils::remote::Fetcher;

use super::{GITHUB_RAW_BASE, ensure_gitignore_cache, find_template_in_cache};
use super::{ensure_gitignore_cache, find_template_in_cache, GITHUB_RAW_BASE};

#[derive(clap::Args, Debug, Clone)]
pub struct PreviewArgs {
Expand All @@ -14,6 +14,10 @@ pub struct PreviewArgs {
/// Update the gitignore cache
#[arg(long = "update-cache")]
pub update_cache: bool,

/// Disable colored output
#[arg(long = "no-color")]
pub no_color: bool,
}

impl super::Runnable for PreviewArgs {
Expand All @@ -28,17 +32,21 @@ impl super::Runnable for PreviewArgs {
let cache = ensure_gitignore_cache(&mut cache_manager, self.update_cache)?;

for template_name in &self.args {
preview_single_template(template_name, &cache)?;
preview_single_template(template_name, &cache, self.no_color)?;
}

Ok(())
}
}

fn preview_single_template(template: &str, cache: &super::Cache<String>) -> anyhow::Result<()> {
fn preview_single_template(
template: &str,
cache: &super::Cache<String>,
no_color: bool,
) -> anyhow::Result<()> {
// normalize template if it has the .gitignore ext
let template = template.strip_suffix(".gitignore").unwrap_or(template);

// Find the template path in cache
let template_path = find_template_in_cache(template, cache)?;

Expand All @@ -51,7 +59,10 @@ fn preview_single_template(template: &str, cache: &super::Cache<String>) -> anyh
pb.set_message(msg);
pb.finish_and_clear();

println!("\n === Preview: {} === \n", template);
pretty_print::print_highlighted("gitignore", &content);
if no_color {
println!("{}", content);
} else {
pretty_print::print_highlighted("gitignore", &content);
}
Ok(())
}
3 changes: 1 addition & 2 deletions src/commands/issue/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ pub mod list;
pub mod preview;

// Global constants - these can stay in the main module file
const GITHUB_RAW_BASE: &str =
"https://raw.githubusercontent.com/rafaeljohn9/gitcraft/main/templates";
const GITHUB_RAW_BASE: &str = "https://raw.githubusercontent.com/Byte-Barn/gitcraft/main/templates";

#[derive(Subcommand)]
pub enum Command {
Expand Down
14 changes: 11 additions & 3 deletions src/commands/issue/preview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ use super::GITHUB_RAW_BASE;
pub struct PreviewArgs {
#[arg(allow_hyphen_values = true)]
pub templates: Vec<String>,

/// Disable colored output
#[arg(long = "no-color")]
pub no_color: bool,
}

impl super::Runnable for PreviewArgs {
Expand All @@ -19,14 +23,14 @@ impl super::Runnable for PreviewArgs {
}

for template_name in &self.templates {
preview_single_template(template_name)?;
preview_single_template(template_name, self.no_color)?;
}

Ok(())
}
}

fn preview_single_template(template: &str) -> anyhow::Result<()> {
fn preview_single_template(template: &str, no_color: bool) -> anyhow::Result<()> {
let fetcher = Fetcher::new();
let url = format!("{}/issue-templates/{}.yml", GITHUB_RAW_BASE, template);

Expand All @@ -36,6 +40,10 @@ fn preview_single_template(template: &str) -> anyhow::Result<()> {
pb.set_message(msg);
pb.finish_and_clear();

pretty_print::print_highlighted("yml", &content);
if no_color {
println!("{}", content);
} else {
pretty_print::print_highlighted("yml", &content);
}
Ok(())
}
4 changes: 2 additions & 2 deletions src/commands/license/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ fn ensure_spdx_license_cache(
let should_update = cache_manager
.should_update_cache::<serde_json::Value>(SPDX_CACHE_NAME, CACHE_MAX_AGE_SECONDS)?;

if !should_update || !update_cache {
if !should_update && !update_cache {
let cache = cache_manager.load_cache(SPDX_CACHE_NAME)?;
// Only print if running in verbose/debug mode (not implemented here)
// e.g., println!("Loaded license template cache ({} templates)", cache.entries.len());
Expand Down Expand Up @@ -94,7 +94,7 @@ fn ensure_github_api_license_cache(
CACHE_MAX_AGE_SECONDS,
)?;

if !should_update || update_cache {
if !should_update && !update_cache {
let cache = cache_manager.load_cache(GITHUB_LICENSES_CACHE_NAME)?;
// Only print if running in verbose/debug mode (not implemented here)
// e.g., println!("Loaded GitHub licenses cache ({} licenses)", cache.entries.len());
Expand Down
4 changes: 2 additions & 2 deletions src/commands/license/preview.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use colored::*;

use super::{
CHOOSEALICENSE_RAW_BASE_URL, SPDX_LICENSE_DETAILS_BASE_URL, SPDX_LICENSE_LIST_URL,
ensure_spdx_license_cache,
ensure_spdx_license_cache, CHOOSEALICENSE_RAW_BASE_URL, SPDX_LICENSE_DETAILS_BASE_URL,
SPDX_LICENSE_LIST_URL,
};

use crate::utils::cache::{Cache, CacheManager};
Expand Down
3 changes: 1 addition & 2 deletions src/commands/pr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ pub mod list;
pub mod preview;

// Global constants - these can stay in the main module file
const GITHUB_RAW_BASE: &str =
"https://raw.githubusercontent.com/rafaeljohn9/gitcraft/main/templates";
const GITHUB_RAW_BASE: &str = "https://raw.githubusercontent.com/Byte-Barn/gitcraft/main/templates";

#[derive(Subcommand)]
pub enum Command {
Expand Down
15 changes: 12 additions & 3 deletions src/commands/pr/preview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ use super::GITHUB_RAW_BASE;
pub struct PreviewArgs {
#[arg(help = "PR template names to preview")]
pub args: Vec<String>,

/// Disable colored output
#[arg(long = "no-color")]
pub no_color: bool,
}

impl super::Runnable for PreviewArgs {
Expand All @@ -19,14 +23,14 @@ impl super::Runnable for PreviewArgs {
}

for template_name in &self.args {
preview_single_template(template_name)?;
preview_single_template(template_name, self.no_color)?;
}

Ok(())
}
}

fn preview_single_template(template: &str) -> anyhow::Result<()> {
fn preview_single_template(template: &str, no_color: bool) -> anyhow::Result<()> {
let fetcher = Fetcher::new();
let url = format!("{}/pr-templates/{}.md", GITHUB_RAW_BASE, template);

Expand All @@ -36,6 +40,11 @@ fn preview_single_template(template: &str) -> anyhow::Result<()> {
pb.set_message(msg);
pb.finish_and_clear();

pretty_print::print_highlighted("md", &content);
println!("\n === Preview: {} === \n", template);
if no_color {
println!("{}", content);
} else {
pretty_print::print_highlighted("md", &content);
}
Ok(())
}
87 changes: 53 additions & 34 deletions src/utils/remote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,45 +18,64 @@ impl Fetcher {
}
}

/// Fetch raw content from a URL
/// Fetch raw content from a URL with retry logic
pub fn fetch_content(&self, url: &str) -> anyhow::Result<String> {
let response = self
.client
.get(url)
.send()
.map_err(|e| anyhow!("Failed to fetch from {}: {}", url, e))?;

if !response.status().is_success() {
return Err(anyhow!(
"Request failed with status {}: {}",
response.status(),
url
));
}

response
.text()
.map_err(|e| anyhow!("Failed to read response: {}", e))
self.retry(|| self.client.get(url).send())
.and_then(|response| {
if !response.status().is_success() {
return Err(anyhow!(
"Failed to fetch from {}: HTTP {} ({})",
url,
response.status().as_u16(),
response.status().canonical_reason().unwrap_or("Unknown")
));
}
Ok(response)
})
.and_then(|response| {
response
.text()
.map_err(|e| anyhow!("Failed to read response: {}", e))
})
}

/// Fetch and parse JSON from a URL
/// Fetch and parse JSON from a URL with retry logic
pub fn fetch_json(&self, url: &str) -> anyhow::Result<serde_json::Value> {
let response = self
.client
.get(url)
.send()
.map_err(|e| anyhow!("Failed to fetch JSON from {}: {}", url, e))?;
self.retry(|| self.client.get(url).send())
.and_then(|response| {
if !response.status().is_success() {
return Err(anyhow!(
"JSON request failed with status {}: {}",
response.status(),
url
));
}
Ok(response)
})
.and_then(|response| {
response
.json()
.map_err(|e| anyhow!("Failed to parse JSON: {}", e))
})
}

if !response.status().is_success() {
return Err(anyhow!(
"JSON request failed with status {}: {}",
response.status(),
url
));
/// Retry logic with exponential backoff (max 3 attempts)
fn retry<F>(&self, mut f: F) -> anyhow::Result<reqwest::blocking::Response>
where
F: FnMut() -> Result<reqwest::blocking::Response, reqwest::Error>,
{
let mut attempts = 0;
loop {
match f() {
Ok(response) => return Ok(response),
Err(e) => {
attempts += 1;
if attempts >= 3 {
return Err(anyhow!("Request failed after 3 attempts: {}", e));
}
std::thread::sleep(Duration::from_millis(100 * (2_u64.pow(attempts - 1))));
}
}
}

response
.json()
.map_err(|e| anyhow!("Failed to parse JSON: {}", e))
}
}
12 changes: 5 additions & 7 deletions tests/integration/issue_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,7 @@ fn test_issue_add_invalid_type() {
cmd.args(&["add", "issue", "invalid-template"])
.assert()
.failure()
.stderr(
predicate::str::contains("Request failed").or(predicate::str::contains("not found")),
);
.stderr(predicate::str::contains("Failed").or(predicate::str::contains("not found")));
}

#[test]
Expand Down Expand Up @@ -301,9 +299,7 @@ fn test_issue_preview_invalid_id() {
cmd.args(&["preview", "issue", "not-a-template"])
.assert()
.failure()
.stderr(
predicate::str::contains("Request failed").or(predicate::str::contains("not found")),
);
.stderr(predicate::str::contains("Failed").or(predicate::str::contains("not found")));
}

// -------- HELP COMMAND TEST --------
Expand All @@ -318,7 +314,9 @@ fn test_issue_help_command() {
.assert()
.success()
.stdout(predicate::str::contains("Add an issue template"))
.stdout(predicate::str::contains("Usage: gitcraft add issue-template"))
.stdout(predicate::str::contains(
"Usage: gitcraft add issue-template",
))
.stdout(predicate::str::contains("--dir"))
.stdout(predicate::str::contains("--force"))
.stdout(predicate::str::contains("-o, --output"));
Expand Down
Loading