Skip to content

Commit bfeea4c

Browse files
authored
Do proper workspace scaffolding on pcb import (#526)
* Do proper workspace scaffolding on `pcb import` * Address PR comments
1 parent ca36d56 commit bfeea4c

File tree

5 files changed

+57
-64
lines changed

5 files changed

+57
-64
lines changed

.github/workflows/e2e.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,6 @@ jobs:
110110

111111
- name: Test pcb import (E2E)
112112
run: |
113-
pcb new --workspace test-import --repo github.com/test/test-import
114113
find kicad-test-fixtures -name '*.kicad_pro' | while IFS= read -r pro; do
115114
echo "=== Importing $pro ==="
116115
pcb import "$pro" ./test-import

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ and this project adheres to Semantic Versioning (https://semver.org/spec/v2.0.0.
88

99
## [Unreleased]
1010

11+
### Changed
12+
13+
- `pcb import` now scaffolds a full workspace (git init, README, .gitignore) when the output directory is new, matching `pcb new --workspace`.
14+
1115
## [0.3.39] - 2026-02-11
1216

1317
### Fixed

crates/pcb/src/import/paths.rs

Lines changed: 8 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ fn ensure_workspace_root(path: &Path) -> Result<PathBuf> {
3232
if path.exists() && !path.is_dir() {
3333
anyhow::bail!("Output directory is not a directory: {}", path.display());
3434
}
35-
if !path.exists() {
35+
let created = !path.exists();
36+
if created {
3637
fs::create_dir_all(path)
3738
.with_context(|| format!("Failed to create output directory: {}", path.display()))?;
3839
}
@@ -49,12 +50,6 @@ fn ensure_workspace_root(path: &Path) -> Result<PathBuf> {
4950
pcb_toml.display()
5051
);
5152
}
52-
if !config.is_v2() {
53-
anyhow::bail!(
54-
"Output directory contains a legacy (V1) workspace pcb.toml; run `pcb migrate`: {}",
55-
pcb_toml.display()
56-
);
57-
}
5853
return Ok(workspace_root);
5954
}
6055

@@ -74,23 +69,11 @@ fn ensure_workspace_root(path: &Path) -> Result<PathBuf> {
7469
);
7570
}
7671

77-
write_minimal_workspace_pcb_toml(&pcb_toml)?;
72+
if let Err(e) = crate::new::init_workspace(&workspace_root, "") {
73+
if created {
74+
let _ = fs::remove_dir_all(&workspace_root);
75+
}
76+
return Err(e);
77+
}
7878
Ok(workspace_root)
7979
}
80-
81-
fn write_minimal_workspace_pcb_toml(path: &Path) -> Result<()> {
82-
use pcb_zen_core::config::{default_members, PcbToml, WorkspaceConfig};
83-
84-
let config = PcbToml {
85-
workspace: Some(WorkspaceConfig {
86-
pcb_version: Some(crate::migrate::codemods::manifest_v2::pcb_version_from_cargo()),
87-
members: default_members(),
88-
..WorkspaceConfig::default()
89-
}),
90-
..PcbToml::default()
91-
};
92-
93-
let content = toml::to_string_pretty(&config)?;
94-
fs::write(path, content).with_context(|| format!("Failed to write {}", path.display()))?;
95-
Ok(())
96-
}

crates/pcb/src/new.rs

Lines changed: 43 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -231,36 +231,20 @@ fn prompt_new_package() -> Result<()> {
231231
execute_new_package(&path)
232232
}
233233

234-
fn execute_new_workspace(workspace: &str, repo: Option<&str>) -> Result<()> {
235-
if get_workspace().is_some() {
236-
bail!("Cannot create a workspace inside an existing workspace");
237-
}
238-
239-
validate_name(workspace, "Workspace")?;
240-
241-
let repo =
242-
repo.ok_or_else(|| anyhow::anyhow!("--repo is required when creating a workspace"))?;
243-
let repository = clean_repo_url(repo)?;
244-
245-
let workspace_path = Path::new(workspace);
246-
247-
if workspace_path.exists() {
248-
bail!("Directory '{}' already exists", workspace);
249-
}
250-
251-
std::fs::create_dir_all(workspace_path)
252-
.with_context(|| format!("Failed to create directory '{}'", workspace))?;
253-
254-
let status = Command::new("git")
255-
.args(["init", "-b", "main"])
256-
.current_dir(workspace_path)
257-
.stdout(Stdio::null())
258-
.stderr(Stdio::null())
259-
.status()
260-
.context("Failed to run 'git init'")?;
261-
262-
if !status.success() {
263-
bail!("'git init' failed with exit code: {:?}", status.code());
234+
/// Initialize workspace scaffolding in an existing directory: pcb.toml, README,
235+
/// .gitignore, git init, and skill path. `repository` may be empty.
236+
pub(crate) fn init_workspace(dir: &Path, repository: &str) -> Result<()> {
237+
if !dir.join(".git").exists() {
238+
let status = Command::new("git")
239+
.args(["init", "-b", "main"])
240+
.current_dir(dir)
241+
.stdout(Stdio::null())
242+
.stderr(Stdio::null())
243+
.status()
244+
.context("Failed to run 'git init'")?;
245+
if !status.success() {
246+
bail!("'git init' failed with exit code: {:?}", status.code());
247+
}
264248
}
265249

266250
let env = create_template_env();
@@ -274,21 +258,44 @@ fn execute_new_workspace(workspace: &str, repo: Option<&str>) -> Result<()> {
274258
.unwrap()
275259
.render(&ctx)
276260
.context("Failed to render pcb.toml template")?;
277-
std::fs::write(workspace_path.join("pcb.toml"), pcb_toml_content)
278-
.context("Failed to write pcb.toml")?;
261+
std::fs::write(dir.join("pcb.toml"), pcb_toml_content).context("Failed to write pcb.toml")?;
279262

280263
let readme_content = env
281264
.get_template("workspace_readme")
282265
.unwrap()
283266
.render(&ctx)
284267
.context("Failed to render README.md template")?;
285-
std::fs::write(workspace_path.join("README.md"), readme_content)
286-
.context("Failed to write README.md")?;
268+
std::fs::write(dir.join("README.md"), readme_content).context("Failed to write README.md")?;
287269

288-
std::fs::write(workspace_path.join(".gitignore"), GITIGNORE_TEMPLATE)
270+
std::fs::write(dir.join(".gitignore"), GITIGNORE_TEMPLATE)
289271
.context("Failed to write .gitignore")?;
290272

291-
add_skill_to_path(workspace_path)?;
273+
add_skill_to_path(dir)?;
274+
275+
Ok(())
276+
}
277+
278+
fn execute_new_workspace(workspace: &str, repo: Option<&str>) -> Result<()> {
279+
if get_workspace().is_some() {
280+
bail!("Cannot create a workspace inside an existing workspace");
281+
}
282+
283+
validate_name(workspace, "Workspace")?;
284+
285+
let repo =
286+
repo.ok_or_else(|| anyhow::anyhow!("--repo is required when creating a workspace"))?;
287+
let repository = clean_repo_url(repo)?;
288+
289+
let workspace_path = Path::new(workspace);
290+
291+
if workspace_path.exists() {
292+
bail!("Directory '{}' already exists", workspace);
293+
}
294+
295+
std::fs::create_dir_all(workspace_path)
296+
.with_context(|| format!("Failed to create directory '{}'", workspace))?;
297+
298+
init_workspace(workspace_path, &repository)?;
292299

293300
eprintln!(
294301
"{} {} ({})",

crates/pcb/src/templates/workspace_pcb_toml.jinja

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[workspace]
2-
repository = "{{ repository }}"
3-
pcb-version = "{{ pcb_version }}"
2+
{% if repository %}repository = "{{ repository }}"
3+
{% endif %}pcb-version = "{{ pcb_version }}"
44
members = [
55
"components/*",
66
"modules/*",

0 commit comments

Comments
 (0)