Skip to content

Commit bbb9da9

Browse files
committed
UI 빌드를 PyPI wheel에서 로드하도록 변경
- GitHub ZIP 다운로드/추출 로직 제거 - wheel 내 dartlab/ui/build/ 존재 확인, 누락 시 --force-reinstall - DARTLAB_UI_DIR 환경변수를 서버 프로세스에 주입
1 parent 270b021 commit bbb9da9

2 files changed

Lines changed: 33 additions & 96 deletions

File tree

src/runner.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,13 @@ pub fn start_server(app_dir: &Path, use_ollama: bool) -> Result<PathBuf, String>
5555
.try_clone()
5656
.map_err(|e| format!("로그 파일 복제 실패: {e}"))?;
5757

58+
let ui_build = paths::dartlab_ui_dir(app_dir).join("build");
59+
5860
let mut command = Command::new(&dartlab);
5961
command
6062
.args(["ai", "--port", &PORT.to_string(), "--no-browser"])
6163
.env("DARTLAB_NO_BROWSER", "1")
64+
.env("DARTLAB_UI_DIR", &ui_build)
6265
.current_dir(app_dir)
6366
.stdout(Stdio::from(log_file))
6467
.stderr(Stdio::from(log_file_err))

src/setup.rs

Lines changed: 30 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -145,114 +145,48 @@ pub fn ensure_dartlab(app_dir: &Path) -> Result<(), String> {
145145
Ok(())
146146
}
147147

148+
/// UI 빌드 파일 확인.
149+
///
150+
/// dartlab PyPI wheel에 `dartlab/ui/build/`가 포함되어 있으므로
151+
/// `pip install dartlab`만으로 UI가 설치된다.
152+
/// 이 함수는 설치 후 파일 존재 여부만 확인한다.
148153
pub fn ensure_ui_build(app_dir: &Path) -> Result<(), String> {
149-
let ui_dir = paths::dartlab_ui_dir(app_dir);
150-
let build_dir = ui_dir.join("build");
151-
let version_file = build_dir.join(".dartlab_ui_version");
152-
153-
let current_ver = get_dartlab_version(app_dir);
154-
155-
if build_dir.exists() && build_dir.join("index.html").exists() {
156-
if let Some(ref ver) = current_ver {
157-
if let Ok(saved) = std::fs::read_to_string(&version_file) {
158-
if saved.trim() == ver.trim() {
159-
return Ok(());
160-
}
161-
logger::log(&format!(
162-
"dartlab 버전 변경 감지 ({} → {ver}) — UI 재다운로드",
163-
saved.trim()
164-
));
165-
} else {
166-
return Ok(());
167-
}
168-
} else {
169-
return Ok(());
170-
}
171-
}
172-
173-
logger::log("UI 빌드 파일 다운로드 중...");
154+
let build_dir = paths::dartlab_ui_dir(app_dir).join("build");
174155

175-
let zip_url = "https://github.com/eddmpython/dartlab/archive/refs/heads/master.zip";
176-
let zip_path = app_dir.join("dartlab-ui.zip");
177-
download_file(zip_url, &zip_path)?;
178-
179-
let extract_dir = app_dir.join("_dartlab_ui_tmp");
180-
if extract_dir.exists() {
181-
std::fs::remove_dir_all(&extract_dir).ok();
156+
if build_dir.join("index.html").exists() {
157+
logger::log("UI 빌드 파일 확인 완료");
158+
return Ok(());
182159
}
183-
std::fs::create_dir_all(&extract_dir).map_err(|e| e.to_string())?;
184-
extract_zip(&zip_path, &extract_dir)?;
185-
std::fs::remove_file(&zip_path).ok();
186160

187-
let src_build = find_build_dir(&extract_dir)
188-
.ok_or("다운로드한 레포에서 ui/build 디렉토리를 찾을 수 없습니다")?;
189-
190-
std::fs::create_dir_all(&ui_dir).map_err(|e| e.to_string())?;
191-
192-
if build_dir.exists() {
193-
std::fs::remove_dir_all(&build_dir).ok();
194-
}
195-
copy_dir_all(&src_build, &build_dir)?;
196-
197-
std::fs::remove_dir_all(&extract_dir).ok();
198-
199-
if !build_dir.join("index.html").exists() {
200-
return Err("UI 빌드 파일 복사 완료되었으나 index.html이 없습니다".into());
201-
}
202-
203-
if let Some(ref ver) = current_ver {
204-
std::fs::write(&version_file, ver).ok();
205-
}
206-
207-
logger::log("UI 빌드 파일 설치 완료");
208-
Ok(())
209-
}
210-
211-
fn get_dartlab_version(app_dir: &Path) -> Option<String> {
161+
// wheel에 포함되어야 하는데 없으면 dartlab 재설치 시도
162+
logger::log("UI 빌드 파일 누락 — dartlab 재설치 시도");
163+
let uv = paths::uv_bin(app_dir);
212164
let python = paths::python_bin(app_dir);
213-
let output = Command::new(&python)
214-
.args(["-c", "import dartlab; print(dartlab.__version__)"])
165+
let output = Command::new(&uv)
166+
.args([
167+
"pip",
168+
"install",
169+
"--force-reinstall",
170+
"dartlab[ai,llm]",
171+
"--python",
172+
python.to_str().unwrap(),
173+
])
215174
.current_dir(app_dir)
216175
.creation_flags(CREATE_NO_WINDOW)
217176
.output()
218-
.ok()?;
219-
if !output.status.success() {
220-
return None;
221-
}
222-
let ver = String::from_utf8_lossy(&output.stdout).trim().to_string();
223-
if ver.is_empty() { None } else { Some(ver) }
224-
}
177+
.map_err(|e| e.to_string())?;
225178

226-
fn find_build_dir(extract_dir: &Path) -> Option<std::path::PathBuf> {
227-
if let Ok(entries) = std::fs::read_dir(extract_dir) {
228-
for entry in entries.flatten() {
229-
let candidate = entry
230-
.path()
231-
.join("src")
232-
.join("dartlab")
233-
.join("ui")
234-
.join("build");
235-
if candidate.join("index.html").exists() {
236-
return Some(candidate);
237-
}
238-
}
179+
if !output.status.success() {
180+
let stderr = String::from_utf8_lossy(&output.stderr);
181+
return Err(format!("dartlab 재설치 실패: {stderr}"));
239182
}
240-
None
241-
}
242183

243-
fn copy_dir_all(src: &Path, dst: &Path) -> Result<(), String> {
244-
std::fs::create_dir_all(dst).map_err(|e| e.to_string())?;
245-
for entry in std::fs::read_dir(src).map_err(|e| e.to_string())? {
246-
let entry = entry.map_err(|e| e.to_string())?;
247-
let ty = entry.file_type().map_err(|e| e.to_string())?;
248-
let dst_path = dst.join(entry.file_name());
249-
if ty.is_dir() {
250-
copy_dir_all(&entry.path(), &dst_path)?;
251-
} else {
252-
std::fs::copy(entry.path(), &dst_path).map_err(|e| e.to_string())?;
253-
}
184+
if build_dir.join("index.html").exists() {
185+
logger::log("UI 빌드 파일 재설치 후 확인 완료");
186+
Ok(())
187+
} else {
188+
Err("dartlab 재설치 후에도 UI 빌드 파일(index.html)이 없습니다".into())
254189
}
255-
Ok(())
256190
}
257191

258192
fn cleanup_legacy(app_dir: &Path) {

0 commit comments

Comments
 (0)