113 lines
3.9 KiB
Python
113 lines
3.9 KiB
Python
# -*- coding: utf-8 -*-
|
||||
|
|
"""
|
|||
|
|
补缺脚本:扫描 D:\dsh-install 下所有已装包的 dependencies,
|
|||
|
|
把缺失的包(新版 dsh 新增的依赖)从 npmmirror 下载补上。
|
|||
|
|
用法: python -S fix_deps.py [版本号]
|
|||
|
|
"""
|
|||
|
|
import urllib.request, tarfile, io, json, os, shutil, sys, glob
|
|||
|
|
|
|||
|
|
# 自动检测 D:\dsh-install 下最新部署目录(避免硬编码 hash,换机器/升级通用)
|
|||
|
|
_install_dirs = sorted(glob.glob(r'D:\dsh-install\*'), key=os.path.getmtime, reverse=True)
|
|||
|
|
if _install_dirs:
|
|||
|
|
NM = os.path.join(_install_dirs[0], 'node_modules')
|
|||
|
|
else:
|
|||
|
|
print('错误: D:\dsh-install 下无部署目录,先跑 dsh_upgrade.py'); sys.exit(1)
|
|||
|
|
REG = 'https://registry.npmmirror.com'
|
|||
|
|
VERSION = sys.argv[1] if len(sys.argv) > 1 else os.environ.get('DSH_UPGRADE_VERSION', '0.1.1-rc.2')
|
|||
|
|
|
|||
|
|
def log(m):
|
|||
|
|
print(m, flush=True)
|
|||
|
|
|
|||
|
|
def get(url, timeout=60):
|
|||
|
|
req = urllib.request.Request(url, headers={'User-Agent': 'python-fix-deps'})
|
|||
|
|
with urllib.request.urlopen(req, timeout=timeout) as r:
|
|||
|
|
return r.read()
|
|||
|
|
|
|||
|
|
def collect_pkg_map():
|
|||
|
|
"""遍历所有已装包,返回 {包名: package.json 路径}"""
|
|||
|
|
pmap = {}
|
|||
|
|
for n in os.listdir(NM):
|
|||
|
|
p = os.path.join(NM, n, 'package.json')
|
|||
|
|
if n.startswith('@'):
|
|||
|
|
d = os.path.join(NM, n)
|
|||
|
|
if os.path.isdir(d):
|
|||
|
|
for s in os.listdir(d):
|
|||
|
|
q = os.path.join(d, s, 'package.json')
|
|||
|
|
if os.path.exists(q):
|
|||
|
|
pmap[f'{n}/{s}'] = q
|
|||
|
|
elif os.path.exists(p):
|
|||
|
|
pmap[n] = p
|
|||
|
|
return pmap
|
|||
|
|
|
|||
|
|
def resolve_version(full_name):
|
|||
|
|
if full_name.startswith('@deepseek-ai/'):
|
|||
|
|
return VERSION
|
|||
|
|
try:
|
|||
|
|
d = json.loads(get(f'{REG}/{full_name}'))
|
|||
|
|
return d['dist-tags']['latest']
|
|||
|
|
except Exception:
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
def install_pkg(full_name):
|
|||
|
|
ver = resolve_version(full_name)
|
|||
|
|
if not ver:
|
|||
|
|
log(f' SKIP {full_name}(版本解析失败)')
|
|||
|
|
return False
|
|||
|
|
esc = full_name.replace('/', '%2F')
|
|||
|
|
pkg_short = full_name.split('/')[-1]
|
|||
|
|
url = f'{REG}/{esc}/-/{pkg_short}-{ver}.tgz'
|
|||
|
|
try:
|
|||
|
|
data = get(url)
|
|||
|
|
except Exception as e:
|
|||
|
|
log(f' FAIL {full_name}@{ver}: {e}')
|
|||
|
|
return False
|
|||
|
|
target = os.path.join(NM, *full_name.split('/'))
|
|||
|
|
if os.path.exists(target):
|
|||
|
|
shutil.rmtree(target)
|
|||
|
|
os.makedirs(target, exist_ok=True)
|
|||
|
|
tf = tarfile.open(fileobj=io.BytesIO(data), mode='r:gz')
|
|||
|
|
for m in tf.getmembers():
|
|||
|
|
if m.isdir() or not m.name.startswith('package/'):
|
|||
|
|
continue
|
|||
|
|
rel = m.name[len('package/'):]
|
|||
|
|
dest = os.path.join(target, rel)
|
|||
|
|
os.makedirs(os.path.dirname(dest), exist_ok=True)
|
|||
|
|
src = tf.extractfile(m)
|
|||
|
|
if src:
|
|||
|
|
with open(dest, 'wb') as f:
|
|||
|
|
f.write(src.read())
|
|||
|
|
tf.close()
|
|||
|
|
log(f' INSTALL {full_name}@{ver}')
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
def main():
|
|||
|
|
missing = {}
|
|||
|
|
for round_no in range(1, 9):
|
|||
|
|
pmap = collect_pkg_map()
|
|||
|
|
have = set(pmap.keys())
|
|||
|
|
missing = {}
|
|||
|
|
for name, pj in pmap.items():
|
|||
|
|
try:
|
|||
|
|
with open(pj, encoding='utf-8') as f:
|
|||
|
|
data = json.load(f)
|
|||
|
|
except Exception:
|
|||
|
|
continue
|
|||
|
|
for dep_key in ('dependencies', 'optionalDependencies'):
|
|||
|
|
for dep, spec in (data.get(dep_key) or {}).items():
|
|||
|
|
if dep not in have:
|
|||
|
|
missing[dep] = spec
|
|||
|
|
if not missing:
|
|||
|
|
log(f'第 {round_no} 轮: 无缺失,依赖完整')
|
|||
|
|
break
|
|||
|
|
log(f'第 {round_no} 轮: 缺 {len(missing)} 个依赖')
|
|||
|
|
ok = sum(1 for dep in sorted(missing) if install_pkg(dep))
|
|||
|
|
if ok == 0:
|
|||
|
|
log('本轮 0 成功,停止')
|
|||
|
|
break
|
|||
|
|
have = collect_pkg_map()
|
|||
|
|
final = sorted(d for d in missing if d not in have)
|
|||
|
|
log('最终缺失: ' + (', '.join(final) if final else '无'))
|
|||
|
|
log('DONE')
|
|||
|
|
|
|||
|
|
if __name__ == '__main__':
|
|||
|
|
main()
|