Skip to content

feat!: update command#131

Open
dinosath wants to merge 12 commits intoaliev:mainfrom
dinosath:feature/update-command
Open

feat!: update command#131
dinosath wants to merge 12 commits intoaliev:mainfrom
dinosath:feature/update-command

Conversation

@dinosath
Copy link
Copy Markdown
Collaborator

@dinosath dinosath commented Apr 9, 2026

Added breaking changes. With these changes in order to generate a project a generate command is needed. Also added an update command with pulling updates from local and git based project. If the content is not identical git conflict markers are added

@dinosath dinosath requested a review from aliev April 10, 2026 05:55
@aliev
Copy link
Copy Markdown
Owner

aliev commented Apr 11, 2026

Love this, thanks! We've had this on the list for a long time 🙂

Let me take a closer look. I think this might deserve to go into a major release, maybe v1?

Copy link
Copy Markdown
Owner

@aliev aliev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Architecture is clean, the generate/update split is well-scoped. Four blockers inline plus a few non-blocking notes and nits. Happy to see the non-blockers as follow-ups if you'd rather keep this PR tight.

Comment thread src/generated.rs
/// Information about the template source (local path + hash, or git URL + commit/tag).
pub template: TemplateSourceInfo,
/// The answers collected during generation, serialised as a JSON value.
pub answers: serde_json::Value,
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type: password answers end up here in plaintext, and .baker-generated.yaml usually gets committed. Probably worth dropping password keys before writing, and re-prompting for them on update.

Keychain-based round-tripping would be nicer but feels like a separate PR.

Comment thread src/cli/update.rs Outdated
let tmp = tempfile::TempDir::new()?;
let tmp_path = tmp.path().to_path_buf();
let loaded = clone_git_into_tmp(url, &tmp_path)?;
std::mem::forget(tmp);
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mem::forget(tmp) leaks the cloned repo into /tmp on every git-source update. The forget is there because LoadedTemplate.root points inside tmp, so a normal drop would yank the directory mid-run.

Probably worth returning the TempDir alongside LoadedTemplate and holding it on the stack until run() finishes. RAII handles cleanup on its own, including on unwind.

fn fetch_updated_template(...) -> Result<(LoadedTemplate, Option<TempDir>)> { ... }
// in run():
let (loaded, _tmp_guard) = self.fetch_updated_template(...)?;

Comment thread src/cli/update.rs Outdated

let original_dir = std::env::current_dir()?;
std::fs::create_dir_all(parent)?;
std::env::set_current_dir(parent)?;
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple of things. If GitLoader::load() panics, the trailing set_current_dir(&original_dir) doesn't run, and the process is left with CWD inside a temp dir that's about to be deleted. The CWD_LOCK in update_integration_tests.rs already hints at this. A small RAII guard that restores CWD on Drop covers both panics and any future early-return paths.

The deeper reason this dance exists is that GitLoader::load() clones into <cwd>/<repo_name> rather than an explicit target dir, so update.rs has to nudge CWD to control where the repo lands. Probably worth filing as tech debt: if GitLoader took a target path, this whole helper goes away and the test lock could too.

Comment thread src/cli/update.rs Outdated

if let Some(ref path) = self.args.answers_file {
let content = std::fs::read_to_string(path)?;
if let Ok(serde_json::Value::Object(extra)) =
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both if let Ok(serde_json::Value::Object(extra)) = ... arms here drop the parse error and the "valid JSON but not an object" case on the floor. A user passing --answers '{"name":}' or a malformed --answers-file gets no feedback, just a silently ignored override.

runner.rs propagates these parse errors via ?, so this is also a behavioral split between generate and update for the same flags. Probably worth using ? here too and returning a clear error when the parsed value isn't an object.

Comment thread src/generated.rs
return Err(crate::error::Error::GeneratedFileNotFound { path });
}
let content = std::fs::read_to_string(&path)?;
let data: BakerGenerated = serde_yaml::from_str(&content)?;
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

non-blocking: read() doesn't check the version field. Once v2 metadata exists, an old baker will quietly accept it (dropping unknown fields), and a new baker will accept v0.x the same way. Probably worth a if data.version != "1" { return Err(...) } so the mismatch is loud.

Comment thread src/error.rs Outdated
GeneratedFileNotFound { path: std::path::PathBuf },

#[error("Template has not changed since last generation (commit/hash unchanged)")]
TemplateUnchanged,
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this variant isn't constructed anywhere. The no-change path in update.rs just does println! + Ok(()). Either drop the variant or use it for the early-exit so tests can match on it.

Comment thread src/cli/update.rs Outdated
return Ok(None);
}

let execute = self.confirm_hook_execution(
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

non-blocking: runner.rs::confirm_hook_execution rolls pre and post hooks into a single combined prompt ("these hooks will run, ok?"). Here each hook gets its own confirm_hook_execution call, so a template with both pre and post triggers two prompts on update versus one on generate. Probably worth combining them the same way runner does.

Comment thread src/cli/update.rs Outdated
std::fs::create_dir_all(parent)?;
std::env::set_current_dir(parent)?;

let result = GitLoader::new(url.to_string(), true).load();
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: the true here is skip_overwrite_check, but we always clone into a fresh TempDir, so the prompt could never trigger anyway. The flag is functionally a no-op in this call. Maybe a constant or a short comment to make that obvious.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants