feat: discover recipe in the paths from environment variable GOOSE_RECIPE_PATH (#2561)

This commit is contained in:
Lifei Zhou
2025-05-19 16:57:45 +10:00
committed by GitHub
parent 68d166b8e8
commit 41c67eb2c3
3 changed files with 61 additions and 29 deletions

View File

@@ -8,27 +8,28 @@ use std::process::Command;
use std::process::Stdio;
use tar::Archive;
use crate::recipes::recipe::RECIPE_FILE_EXTENSIONS;
pub const GOOSE_RECIPE_GITHUB_REPO_CONFIG_KEY: &str = "GOOSE_RECIPE_GITHUB_REPO";
pub fn retrieve_recipe_from_github(
recipe_name: &str,
recipe_repo_full_name: &str,
) -> Result<(String, PathBuf)> {
println!(
"retrieving recipe from github repo {}",
recipe_repo_full_name
"📦 Looking for recipe \"{}\" in github repo: {}",
recipe_name, recipe_repo_full_name
);
ensure_gh_authenticated()?;
let local_repo_path = ensure_repo_cloned(recipe_repo_full_name)?;
fetch_origin(&local_repo_path)?;
let download_dir = get_folder_from_github(&local_repo_path, recipe_name)?;
let file_extensions = ["yaml", "json"];
for ext in file_extensions {
for ext in RECIPE_FILE_EXTENSIONS {
let candidate_file_path = download_dir.join(format!("recipe.{}", ext));
if candidate_file_path.exists() {
let content = std::fs::read_to_string(&candidate_file_path)?;
println!(
"retrieved recipe from github repo {}/{}",
"⬇️ Retrieved recipe from github repo {}/{}",
recipe_repo_full_name,
candidate_file_path
.strip_prefix(&download_dir)

View File

@@ -14,6 +14,7 @@ use std::collections::{HashMap, HashSet};
use std::path::PathBuf;
pub const BUILT_IN_RECIPE_DIR_PARAM: &str = "recipe_dir";
pub const RECIPE_FILE_EXTENSIONS: &[&str] = &["yaml", "json"];
/// Loads, validates a recipe from a YAML or JSON file, and renders it with the given parameters
///
/// # Arguments

View File

@@ -1,23 +1,24 @@
use anyhow::{anyhow, Result};
use goose::config::Config;
use std::fs;
use std::path::{Path, PathBuf};
use std::{env, fs};
use crate::recipes::recipe::RECIPE_FILE_EXTENSIONS;
use super::github_recipe::{retrieve_recipe_from_github, GOOSE_RECIPE_GITHUB_REPO_CONFIG_KEY};
const GOOSE_RECIPE_PATH_ENV_VAR: &str = "GOOSE_RECIPE_PATH";
pub fn retrieve_recipe_file(recipe_name: &str) -> Result<(String, PathBuf)> {
// If recipe_name ends with yaml or json, treat it as a direct path
if recipe_name.ends_with(".yaml") || recipe_name.ends_with(".json") {
// If recipe_name ends with yaml or json, treat it as a direct file path
if RECIPE_FILE_EXTENSIONS
.iter()
.any(|ext| recipe_name.ends_with(&format!(".{}", ext)))
{
let path = PathBuf::from(recipe_name);
return read_recipe_file(path);
}
// First check current directory
let current_dir = std::env::current_dir()?;
if let Ok((content, recipe_parent_dir)) = read_recipe_in_dir(&current_dir, recipe_name) {
return Ok((content, recipe_parent_dir));
}
read_recipe_in_dir(&current_dir, recipe_name).or_else(|e| {
retrieve_recipe_from_local_path(recipe_name).or_else(|e| {
if let Some(recipe_repo_full_name) = configured_github_recipe_repo() {
retrieve_recipe_from_github(recipe_name, &recipe_repo_full_name)
} else {
@@ -26,6 +27,49 @@ pub fn retrieve_recipe_file(recipe_name: &str) -> Result<(String, PathBuf)> {
})
}
fn read_recipe_in_dir(dir: &Path, recipe_name: &str) -> Result<(String, PathBuf)> {
for ext in RECIPE_FILE_EXTENSIONS {
let recipe_path = dir.join(format!("{}.{}", recipe_name, ext));
if let Ok(result) = read_recipe_file(recipe_path) {
return Ok(result);
}
}
Err(anyhow!(format!(
"No {}.yaml or {}.json recipe file found in directory: {}",
recipe_name,
recipe_name,
dir.display()
)))
}
fn retrieve_recipe_from_local_path(recipe_name: &str) -> Result<(String, PathBuf)> {
let mut search_dirs = vec![PathBuf::from(".")];
if let Ok(recipe_path_env) = env::var(GOOSE_RECIPE_PATH_ENV_VAR) {
let path_separator = if cfg!(windows) { ';' } else { ':' };
let recipe_path_env_dirs: Vec<PathBuf> = recipe_path_env
.split(path_separator)
.map(PathBuf::from)
.collect();
search_dirs.extend(recipe_path_env_dirs);
}
for dir in &search_dirs {
if let Ok(result) = read_recipe_in_dir(dir, recipe_name) {
return Ok(result);
}
}
let search_dirs_str = search_dirs
.iter()
.map(|p| p.to_string_lossy())
.collect::<Vec<_>>()
.join(":");
Err(anyhow!(
" Failed to retrieve {}.yaml or {}.json in {}",
recipe_name,
recipe_name,
search_dirs_str
))
}
fn configured_github_recipe_repo() -> Option<String> {
let config = Config::global();
match config.get_param(GOOSE_RECIPE_GITHUB_REPO_CONFIG_KEY) {
@@ -55,17 +99,3 @@ fn read_recipe_file<P: AsRef<Path>>(recipe_path: P) -> Result<(String, PathBuf)>
Ok((content, parent_dir))
}
fn read_recipe_in_dir(dir: &Path, recipe_name: &str) -> Result<(String, PathBuf)> {
for ext in &["yaml", "json"] {
let recipe_path = dir.join(format!("{}.{}", recipe_name, ext));
match read_recipe_file(recipe_path) {
Ok((content, recipe_parent_dir)) => return Ok((content, recipe_parent_dir)),
Err(_) => continue,
}
}
Err(anyhow!(format!(
"No {}.yaml or {}.json recipe file found in current directory.",
recipe_name, recipe_name
)))
}