fix(launcher): junction riêng cho từng thư mục mã nguồn

run.bat chỉ hỏi junction "có tồn tại không", không hỏi nó trỏ vào đâu. Một
junction còn lại từ checkout KHÁC vẫn được dùng lại im lặng: run.bat nằm
trong thư mục này nhưng ứng dụng chạy từ thư mục kia. Triệu chứng là "sửa
code xong chạy vẫn y nguyên", và không có gì báo lỗi.

- junction được tạo lại mỗi lần chạy, không chỉ khi thiếu;
- mỗi thư mục mã nguồn có junction RIÊNG (khoá SHA1 từ đường dẫn tuyệt đối).
  Bản trước dùng chung một đường dẫn cho cả máy, nên hai checkout tranh nhau:
  cái chạy sau trỏ junction về mình, và tiến trình con của cái chạy trước
  (máy chủ MCP MS365, sinh ra sau khi app đã mở) import mã nguồn của cái kia;
- install.bat dọn junction dùng chung của bản cũ — để lại là một cái bẫy;
- venv cũ phải CHẠY ĐƯỢC, không chỉ tồn tại file python.exe: venv dựng bằng
  bản Python đã bị nâng cấp hoặc xoá vẫn còn nguyên file đó;
- smoke test kiểm DANH TÍNH, không chỉ kiểm import được — có một "cowork_local"
  khác chen trên sys.path thì lệnh import vẫn chạy tốt và bước kiểm vẫn xanh
  trong khi ứng dụng đọc mã nguồn khác;
- run.bat chốt cowork_local/__main__.py thật sự nhìn thấy được trước khi khởi
  động, thay cho lỗi Python khó hiểu "'cowork_local' is a package and cannot
  be directly executed";
- thông báo lỗi rõ cho ổ mạng và ổ không phải NTFS — junction không trỏ sang
  được, ca hay gặp nhất khi mang sang máy khác.

Đã kiểm bằng cách chạy thật cả hai script.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-07 19:55:02 +09:00
co-authored by Claude Opus 5
parent 39df2c6ed1
commit 257cc91a3c
2 changed files with 101 additions and 21 deletions
+49 -7
View File
@@ -9,7 +9,8 @@ rem install.bat --force dung lai moi truong ao tu dau
rem rem
rem Cai gi va cai o dau: rem Cai gi va cai o dau:
rem %LOCALAPPDATA%\CoworkLocal\venv moi truong ao rem %LOCALAPPDATA%\CoworkLocal\venv moi truong ao
rem %LOCALAPPDATA%\CoworkLocal\launcher lien ket de import duoc goi rem %LOCALAPPDATA%\CoworkLocal\launcher\<khoa> lien ket de import duoc goi
rem (mot khoa cho moi thu muc ma nguon)
rem rem
rem Vi sao KHONG dat venv trong repo: cac cong chat luong rem Vi sao KHONG dat venv trong repo: cac cong chat luong
rem (scripts/check_orphan_modules.py, check_imports.py) quet TOAN BO cay thu rem (scripts/check_orphan_modules.py, check_imports.py) quet TOAN BO cay thu
@@ -88,10 +89,24 @@ if "%FORCE%"=="1" if exist "%VENV%" (
rmdir /s /q "%VENV%" 2>nul rmdir /s /q "%VENV%" 2>nul
) )
rem Chi kiem file python.exe co ton tai la khong du: mot venv dung lai tu ban
rem Python da bi nang cap hoac xoa van con nguyen file do, nhung chay vao la loi
rem ngay. Goi thu mot lenh that de biet no con song.
set "VENV_OK=0"
if exist "%VENV%\Scripts\python.exe" ( if exist "%VENV%\Scripts\python.exe" (
"%VENV%\Scripts\python.exe" -c "import sys" >nul 2>&1
if not errorlevel 1 set "VENV_OK=1"
)
if "!VENV_OK!"=="1" (
echo [2/5] Môi trường ảo đã có — dùng lại echo [2/5] Môi trường ảo đã có — dùng lại
) else ( ) else (
echo [2/5] Tạo môi trường ảo... if exist "%VENV%" (
echo [2/5] Môi trường ảo cũ không chạy được — dựng lại từ đầu...
rmdir /s /q "%VENV%" 2>nul
) else (
echo [2/5] Tạo môi trường ảo...
)
%PY% -m venv "%VENV%" %PY% -m venv "%VENV%"
if errorlevel 1 ( if errorlevel 1 (
echo [LỖI] Không tạo được môi trường ảo. echo [LỖI] Không tạo được môi trường ảo.
@@ -135,9 +150,23 @@ if /I "%REPO_NAME%"=="cowork_local" (
goto smoke goto smoke
) )
if not exist "%LAUNCHER%" mkdir "%LAUNCHER%" >nul 2>&1 rem Junction rieng cho TUNG thu muc ma nguon. Ban truoc dung dung mot duong
rem dan cho ca may, nen hai ban checkout tranh nhau: cai chay sau tro junction
rem ve minh, va tien trinh con cua cai chay truoc (may chu MCP MS365) se import
rem ma nguon cua cai kia.
set "REPO_KEY="
for /f "delims=" %%K in ('%PY% -c "import hashlib,os,sys;print(hashlib.sha1(os.path.normcase(os.path.abspath(sys.argv[1])).encode()).hexdigest()[:10])" "%REPO%" 2^>nul') do set "REPO_KEY=%%K"
if not defined REPO_KEY set "REPO_KEY=default"
set "PKGROOT=%LAUNCHER%\!REPO_KEY!"
rem Don junction dung chung cua ban cu: de lai la mot cai bay — mot run.bat cu
rem o thu muc khac se dung lai no va chay nham ma nguon. rmdir KHONG co /s: voi
rem junction thi no xoa lien ket, khong xoa noi dung dich.
if exist "%LAUNCHER%\cowork_local" rmdir "%LAUNCHER%\cowork_local" >nul 2>&1 if exist "%LAUNCHER%\cowork_local" rmdir "%LAUNCHER%\cowork_local" >nul 2>&1
mklink /J "%LAUNCHER%\cowork_local" "%REPO%" >nul
if not exist "!PKGROOT!" mkdir "!PKGROOT!" >nul 2>&1
if exist "!PKGROOT!\cowork_local" rmdir "!PKGROOT!\cowork_local" >nul 2>&1
mklink /J "!PKGROOT!\cowork_local" "%REPO%" >nul
if errorlevel 1 ( if errorlevel 1 (
echo [LỖI] Không tạo được liên kết thư mục. echo [LỖI] Không tạo được liên kết thư mục.
echo Thư mục "%REPO_NAME%" không phải tên gói Python hợp lệ nên echo Thư mục "%REPO_NAME%" không phải tên gói Python hợp lệ nên
@@ -145,7 +174,7 @@ if errorlevel 1 (
echo mã nguồn thành "cowork_local". echo mã nguồn thành "cowork_local".
goto fail goto fail
) )
echo [4/5] Đã tạo liên kết: %LAUNCHER%\cowork_local echo [4/5] Đã tạo liên kết: !PKGROOT!\cowork_local
rem -------------------------------------------------------------------------- rem --------------------------------------------------------------------------
rem 5. Chay thu mot lan rem 5. Chay thu mot lan
@@ -155,12 +184,25 @@ if "%USE_SYSTEM%"=="1" (set "RUNPY=%PY%") else (set "RUNPY="%VENV%\Scripts\pytho
if /I "%REPO_NAME%"=="cowork_local" ( if /I "%REPO_NAME%"=="cowork_local" (
for %%I in ("%REPO%\..") do set "PKGPATH=%%~fI" for %%I in ("%REPO%\..") do set "PKGPATH=%%~fI"
) else ( ) else (
set "PKGPATH=%LAUNCHER%" set "PKGPATH=!PKGROOT!"
) )
echo [5/5] Kiểm tra lại... echo [5/5] Kiểm tra lại...
set "PYTHONPATH=!PKGPATH!" set "PYTHONPATH=!PKGPATH!"
%RUNPY% -c "import cowork_local, PySide6; print(' cowork_local + PySide6 nạp được')" rem Kiem ca DANH TINH, khong chi kiem import duoc: neu tren sys.path con mot thu
rem muc khac cung ten "cowork_local" (mot ban checkout cu chang han) thi lenh
rem import van chay tot, va ca buoc kiem tra nay se xanh trong khi ung dung
rem dang chay tu ma nguon KHAC. Duong dan truyen qua bien moi truong de khoi
rem phai boc dau nhay long nhau trong chuoi -c.
set "EXPECT_REPO=%REPO%"
%RUNPY% -c "import os,sys,cowork_local,PySide6; p=os.path.realpath(os.path.dirname(cowork_local.__file__)); e=os.path.realpath(os.environ['EXPECT_REPO']); print(' cowork_local + PySide6 nap duoc'); print(' goi doc tu: '+p); sys.exit(0 if p==e else 3)"
if errorlevel 3 (
echo [LỖI] Gói import được, nhưng KHÔNG phải từ thư mục mã nguồn này:
echo mong đợi: %REPO%
echo Trên PYTHONPATH hoặc site-packages đang có một "cowork_local" khác
echo chen lên trước. Gỡ nó đi rồi chạy lại install.bat.
goto fail
)
if errorlevel 1 ( if errorlevel 1 (
echo [LỖI] Cài xong nhưng vẫn chưa import được gói. echo [LỖI] Cài xong nhưng vẫn chưa import được gói.
goto fail goto fail
+52 -14
View File
@@ -63,26 +63,64 @@ rem --------------------------------------------------------------------------
rem 2. Duong dan de import duoc goi "cowork_local" rem 2. Duong dan de import duoc goi "cowork_local"
rem rem
rem Thu muc ma nguon phai mang dung ten "cowork_local" thi Python moi import rem Thu muc ma nguon phai mang dung ten "cowork_local" thi Python moi import
rem duoc no. Neu khong, install.bat da tao mot junction; o day chi kiem tra va rem duoc no. Neu khong, ta tro mot junction toi no.
rem tu dung lai neu no bi xoa — de nguoi dung khong phai chay lai install.bat rem
rem chi vi mot thu muc tam bi don. rem Junction duoc TAO LAI moi lan chay, khong phai chi khi thieu. Ban truoc chi
rem hoi "co ton tai khong", nen mot junction con lai tu checkout KHAC van duoc
rem dung lai im lang: run.bat nam trong thu muc nay nhung ung dung chay tu thu
rem muc kia. Trieu chung la "sua code xong chay van y nguyen" — mat rat lau moi
rem nghi ra, vi khong co gi bao loi ca. Tao lai ton vai mili giay va bo han ca
rem lop loi do. install.bat cung lam dung nhu vay.
rem -------------------------------------------------------------------------- rem --------------------------------------------------------------------------
rem Junction rieng cho TUNG thu muc ma nguon, khong dung chung mot cai.
rem
rem Ban truoc dung dung mot duong dan %LAUNCHER%\cowork_local cho ca may. Hai
rem ban checkout thi tranh nhau no: cai chay sau tro junction ve minh, va tien
rem trinh con cua cai chay truoc (may chu MCP MS365, sinh ra sau khi app da mo)
rem se import ma nguon cua cai kia. Rat kho lan ra, vi khong co gi bao loi.
rem
rem Khoa lay tu bam SHA1 cua duong dan tuyet doi (normcase, nen khong phan biet
rem hoa thuong nhu Windows). Moi thu muc ma nguon co mot khoa on dinh, khong
rem bao gio dung chung.
for %%I in ("%REPO%") do set "REPO_NAME=%%~nxI" for %%I in ("%REPO%") do set "REPO_NAME=%%~nxI"
if /I "%REPO_NAME%"=="cowork_local" ( if /I "%REPO_NAME%"=="cowork_local" (
for %%I in ("%REPO%\..") do set "PKGPATH=%%~fI" for %%I in ("%REPO%\..") do set "PKGPATH=%%~fI"
) else ( ) else (
if not exist "%LAUNCHER%\cowork_local" ( set "REPO_KEY="
if not exist "%LAUNCHER%" mkdir "%LAUNCHER%" >nul 2>&1 for /f "delims=" %%K in ('!RUNPY! -c "import hashlib,os,sys;print(hashlib.sha1(os.path.normcase(os.path.abspath(sys.argv[1])).encode()).hexdigest()[:10])" "%REPO%" 2^>nul') do set "REPO_KEY=%%K"
mklink /J "%LAUNCHER%\cowork_local" "%REPO%" >nul 2>&1 if not defined REPO_KEY set "REPO_KEY=default"
if errorlevel 1 ( set "PKGPATH=%LAUNCHER%\!REPO_KEY!"
echo. if not exist "!PKGPATH!" mkdir "!PKGPATH!" >nul 2>&1
echo [LỖI] Không tạo được liên kết thư mục. Chạy install.bat lại. if exist "!PKGPATH!\cowork_local" rmdir "!PKGPATH!\cowork_local" >nul 2>&1
echo. mklink /J "!PKGPATH!\cowork_local" "%REPO%" >nul 2>&1
pause if errorlevel 1 (
exit /b 1 echo.
) echo [LOI] Khong tao duoc lien ket thu muc:
echo "!PKGPATH!\cowork_local" -> "%REPO%"
echo.
echo Thuong gap khi ma nguon nam tren o mang, hoac tren o dia dinh
echo dang khong phai NTFS - junction khong tro sang duoc.
echo Cach khac: chep ma nguon ve o dia trong may, hoac doi ten thu
echo muc ma nguon thanh cowork_local la khong can junction nua.
echo.
pause
exit /b 1
) )
set "PKGPATH=%LAUNCHER%" )
rem Chot lai: goi phai THAT SU nhin thay duoc qua duong dan vua dung. Khong co
rem buoc nay thi mot junction hong chi hien ra duoi dang loi Python kho hieu
rem ("'cowork_local' is a package and cannot be directly executed").
if not exist "!PKGPATH!\cowork_local\__main__.py" (
echo.
echo [LOI] Khong tim thay cowork_local\__main__.py qua duong dan:
echo "!PKGPATH!"
echo.
echo Ma nguon o "%REPO%" bi thieu file, hoac lien ket dang tro nham cho.
echo Chay install.bat de dung lai tu dau.
echo.
pause
exit /b 1
) )
rem -------------------------------------------------------------------------- rem --------------------------------------------------------------------------