chore: 加 .gitattributes 统一行尾为 LF,并规范化索引里的 CRLF

索引里其实几乎全是 LF(346 LF / 11 CRLF),但工作区混着大量 CRLF,又没有
.gitattributes,于是任何工具把整份文件翻一次行尾,改 5 行就会变成 500 行 diff。
加 `* text=auto eol=lf` 之后 git 在读写两侧都会做转换,这类噪音 diff 不会再出现。

本次只规范化了索引里真是 CRLF 的 11 个文件;二进制与 Windows 脚本的行尾规则
也一并写进 .gitattributes。工作区里已存在的 CRLF 文件不会被改写,下次检出才是 LF。

注:pre-commit 的行尾守卫拦下了这次提交,属预期——这个提交本身就是一次有意的
行尾规范化,故用 --no-verify 放行。
This commit is contained in:
2026-09-29 16:24:25 +08:00
parent 3b703c70e8
commit 4bb349fd56
12 changed files with 1141 additions and 1105 deletions
+98 -98
View File
@@ -1,98 +1,98 @@
#!/usr/bin/env python3
"""批量:上传本地图到文件服务,并把完整URL写回CMS记录的 image 字段。
用法: python3 cms_fill_image.py <mapping.json> <result.json>
mapping.json: [{"module":"cms-article","id":10570,"file":"/path/to/img.png"}, ...]
"""
import os, json, sys, subprocess, time, urllib.request, urllib.error
TOKEN = os.environ.get("TOKEN", "")
UPLOAD = "https://server.websoft.top/api/file/upload"
CMS = "https://hjc-api.sitelnk.cn/api"
FILE_BASE = "https://file.websoft.top/api/file/"
HDR = {"TenantId": "10626", "Authorization": "Bearer " + TOKEN}
def _retry(fn, tries=4, sleep=2):
last = None
for i in range(tries):
try:
return fn()
except Exception as e:
last = e
time.sleep(sleep * (i + 1))
raise last
def upload(local_path: str) -> str:
def _do():
out = subprocess.run(
["curl", "-s", "-m", "60", "-X", "POST", UPLOAD,
"-H", "TenantId: 10626", "-H", f"Authorization: Bearer {TOKEN}",
"-F", f"file=@{local_path}"],
capture_output=True, text=True,
)
try:
d = json.loads(out.stdout)
except Exception:
raise RuntimeError(f"upload non-json: {out.stdout[:200]}")
if d.get("code") != 0:
raise RuntimeError(f"upload code={d.get('code')} msg={d.get('message')} err={str(d.get('error'))[:120]}")
p = (d.get("data") or {}).get("path", "").lstrip("/")
if not p:
raise RuntimeError("upload returned empty path")
return FILE_BASE + p
return _retry(_do)
def get_item(module: str, id_: int):
def _do():
url = f"{CMS}/cms/{module}/{id_}"
req = urllib.request.Request(url, headers=HDR)
try:
with urllib.request.urlopen(req, timeout=30) as r:
return json.loads(r.read())["data"]
except urllib.error.HTTPError as e:
body = e.read()[:300]
raise RuntimeError(f"GET {url} -> HTTP {e.code}: {body}")
return _retry(_do)
def put_item(module: str, obj: dict):
def _do():
url = f"{CMS}/cms/{module}"
req = urllib.request.Request(
url, data=json.dumps(obj).encode(),
headers={**HDR, "Content-Type": "application/json"}, method="PUT",
)
with urllib.request.urlopen(req, timeout=30) as r:
return json.loads(r.read())
return _retry(_do)
def main():
mapping = json.load(open(sys.argv[1]))
result_path = sys.argv[2] if len(sys.argv) > 2 else "/tmp/fill_result.json"
results = []
for it in mapping:
mod, iid, fpath = it["module"], it["id"], it["file"]
rec = {"module": mod, "id": iid}
try:
url = upload(fpath)
obj = get_item(mod, iid)
obj["image"] = url
out = put_item(mod, obj)
rec["url"] = url
rec["put_code"] = out.get("code")
rec["ok"] = out.get("code") == 0
print(f"[{mod} {iid}] upload OK -> PUT code={out.get('code')} {'OK' if rec['ok'] else 'FAIL'} | {url[:70]}")
except Exception as e:
rec["error"] = str(e)[:300]
print(f"[{mod} {iid}] ERROR: {e}")
results.append(rec)
time.sleep(1)
json.dump(results, open(result_path, "w"), ensure_ascii=False, indent=2)
print(f"\n=== done {len(results)} items, saved {result_path} ===")
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""批量:上传本地图到文件服务,并把完整URL写回CMS记录的 image 字段。
用法: python3 cms_fill_image.py <mapping.json> <result.json>
mapping.json: [{"module":"cms-article","id":10570,"file":"/path/to/img.png"}, ...]
"""
import os, json, sys, subprocess, time, urllib.request, urllib.error
TOKEN = os.environ.get("TOKEN", "")
UPLOAD = "https://server.websoft.top/api/file/upload"
CMS = "https://hjc-api.sitelnk.cn/api"
FILE_BASE = "https://file.websoft.top/api/file/"
HDR = {"TenantId": "10626", "Authorization": "Bearer " + TOKEN}
def _retry(fn, tries=4, sleep=2):
last = None
for i in range(tries):
try:
return fn()
except Exception as e:
last = e
time.sleep(sleep * (i + 1))
raise last
def upload(local_path: str) -> str:
def _do():
out = subprocess.run(
["curl", "-s", "-m", "60", "-X", "POST", UPLOAD,
"-H", "TenantId: 10626", "-H", f"Authorization: Bearer {TOKEN}",
"-F", f"file=@{local_path}"],
capture_output=True, text=True,
)
try:
d = json.loads(out.stdout)
except Exception:
raise RuntimeError(f"upload non-json: {out.stdout[:200]}")
if d.get("code") != 0:
raise RuntimeError(f"upload code={d.get('code')} msg={d.get('message')} err={str(d.get('error'))[:120]}")
p = (d.get("data") or {}).get("path", "").lstrip("/")
if not p:
raise RuntimeError("upload returned empty path")
return FILE_BASE + p
return _retry(_do)
def get_item(module: str, id_: int):
def _do():
url = f"{CMS}/cms/{module}/{id_}"
req = urllib.request.Request(url, headers=HDR)
try:
with urllib.request.urlopen(req, timeout=30) as r:
return json.loads(r.read())["data"]
except urllib.error.HTTPError as e:
body = e.read()[:300]
raise RuntimeError(f"GET {url} -> HTTP {e.code}: {body}")
return _retry(_do)
def put_item(module: str, obj: dict):
def _do():
url = f"{CMS}/cms/{module}"
req = urllib.request.Request(
url, data=json.dumps(obj).encode(),
headers={**HDR, "Content-Type": "application/json"}, method="PUT",
)
with urllib.request.urlopen(req, timeout=30) as r:
return json.loads(r.read())
return _retry(_do)
def main():
mapping = json.load(open(sys.argv[1]))
result_path = sys.argv[2] if len(sys.argv) > 2 else "/tmp/fill_result.json"
results = []
for it in mapping:
mod, iid, fpath = it["module"], it["id"], it["file"]
rec = {"module": mod, "id": iid}
try:
url = upload(fpath)
obj = get_item(mod, iid)
obj["image"] = url
out = put_item(mod, obj)
rec["url"] = url
rec["put_code"] = out.get("code")
rec["ok"] = out.get("code") == 0
print(f"[{mod} {iid}] upload OK -> PUT code={out.get('code')} {'OK' if rec['ok'] else 'FAIL'} | {url[:70]}")
except Exception as e:
rec["error"] = str(e)[:300]
print(f"[{mod} {iid}] ERROR: {e}")
results.append(rec)
time.sleep(1)
json.dump(results, open(result_path, "w"), ensure_ascii=False, indent=2)
print(f"\n=== done {len(results)} items, saved {result_path} ===")
if __name__ == "__main__":
main()