diff --git a/README.md b/README.md index 7a6b6c5..c678784 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,24 @@ GIT_LFS_SKIP_SMUDGE=1 git clone git@192.168.10.248:cogito/calculet-npu-research- 随后可按需执行 `git lfs pull --include='archives/<路径>'`。 +### 从当前网络追加 LFS 大文件 + +Gitea 当前对 SSH LFS 上传返回的内部地址是 `http://gitea/...`。匿名克隆和 LFS 下载通过上面的 IP 地址已经验收;如果贡献者电脑不能解析主机名 `gitea`,SSH 推送 LFS 时可在另一个终端启动仅监听本机回环地址的临时转发: + +```bash +python3 scripts/lfs-host-proxy.py --port 18765 +``` + +然后在仓库终端执行: + +```bash +http_proxy=http://127.0.0.1:18765 \ +HTTP_PROXY=http://127.0.0.1:18765 \ +no_proxy= NO_PROXY= git push +``` + +推送完成后用 `Ctrl-C` 关闭转发器。长期方案是在 Gitea 服务端把 `ROOT_URL`/LFS 外部地址改成团队实际可解析的 IP 或 DNS 名称。 + ## 目录 | 路径 | 内容 | 用途 | diff --git a/checksums/published-files.sha256 b/checksums/published-files.sha256 index 53538e6..a8240cc 100644 --- a/checksums/published-files.sha256 +++ b/checksums/published-files.sha256 @@ -1,5 +1,5 @@ 174185a34cee598ff33cf4b2b75dbe1688d1000478f95f37dcc3f24ecb11cf46 ./.gitattributes -3f01c5ebf226064b0f0992ea7506c038dfb9d453e35d2864db06f91966b5baaf ./README.md +0b94311217982321bcd106d3170256676b2313eda512af1519099b236a5290eb ./README.md 6dda8faa0e2a14362eac47dcdc192094e631fc47a37e978eb0f745e556b4e7be ./checksums/main-archives.sha256 4b1b7a92f9502c7d84a240888fac838d43af2bfbe7c821468d148a643dce5d6a ./documentation/Calculet Runtime API C++ 开发文档-v78-20260710_074837.pdf 4a7c3b83977fc853eebcd8e306dbd9a4f846d9cfb03c89214a088c7034ec632c ./documentation/VPN接入及远程连接.pdf @@ -269,6 +269,7 @@ c16023ac93e955138329561a82a0b5932ea168acb1346ec05197bee8f8b96af4 ./reports/Calc eb0692b1e09e8c0049ecddc4ecaf84860a4b31375535bbed3716b983f2579b30 ./reports/Calculet-NPU-逐算子优化实验手册-20260802.md a81d9dd8e5b8ae475f4eabdb2f187381ea4c45d77aa352c1d7ee30d2dea22502 ./reports/calculet_package_validate.rb 29727a6e2734c9458ff1853a4f94e7fdf1e109d2fa6b51cd7062a5bd11924479 ./scripts/clone-bundle.sh +363b3bf7e2c3d53df0c95843b770e1922b19b044e8be01e6acef088401821747 ./scripts/lfs-host-proxy.py fa1c6a60eab70ae3ee215f4850313e1abf1ed5765bfd9860e7b3042afa638e17 ./scripts/reassemble-archives.sh 15e8c8711c017cb9a31a1499ad9f8479216eff47427a303cafbdc34959f797c2 ./scripts/verify-repository.sh 46ce4c2a910cadf07a9f45d3f2ad0b5a151fbbc9c60a82bbcbb354e0bd71a00e ./source/baseline-expanded/calrt-sdk-0.7.6/usr/local/include/calrt/calrt.h diff --git a/scripts/lfs-host-proxy.py b/scripts/lfs-host-proxy.py new file mode 100755 index 0000000..ce1f7bf --- /dev/null +++ b/scripts/lfs-host-proxy.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 +import argparse +import http.client +import shutil +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +from urllib.parse import urlsplit, urlunsplit + + +HOP_BY_HOP = { + "connection", + "keep-alive", + "proxy-authenticate", + "proxy-authorization", + "proxy-connection", + "te", + "trailers", + "transfer-encoding", + "upgrade", +} + + +class GiteaProxy(BaseHTTPRequestHandler): + protocol_version = "HTTP/1.1" + target_host = "192.168.10.248" + + def do_GET(self): + self._forward() + + def do_HEAD(self): + self._forward(send_body=False) + + def do_POST(self): + self._forward() + + def do_PUT(self): + self._forward() + + def _forward(self, send_body=True): + parsed = urlsplit(self.path) + path = urlunsplit(("", "", parsed.path or "/", parsed.query, "")) + print( + f"{self.command} content-length={self.headers.get('Content-Length')} " + f"transfer-encoding={self.headers.get('Transfer-Encoding')}", + flush=True, + ) + upstream = http.client.HTTPConnection(self.target_host, 80, timeout=3600) + upstream.putrequest( + self.command, + path, + skip_host=True, + skip_accept_encoding=True, + ) + + for name, value in self.headers.items(): + lower_name = name.lower() + if lower_name in HOP_BY_HOP: + continue + upstream.putheader(name, "gitea" if lower_name == "host" else value) + transfer_encoding = self.headers.get("Transfer-Encoding", "").lower() + if transfer_encoding == "chunked": + upstream.putheader("Transfer-Encoding", "chunked") + upstream.endheaders() + + if transfer_encoding == "chunked": + self._forward_chunked_body(upstream) + else: + content_length = int(self.headers.get("Content-Length", "0")) + remaining = content_length + while remaining: + chunk = self.rfile.read(min(1024 * 1024, remaining)) + if not chunk: + raise ConnectionError("request body ended early") + upstream.send(chunk) + remaining -= len(chunk) + + response = upstream.getresponse() + self.send_response(response.status, response.reason) + for name, value in response.getheaders(): + if name.lower() not in HOP_BY_HOP: + self.send_header(name, value) + self.send_header("Connection", "close") + self.end_headers() + self.close_connection = True + + if send_body: + shutil.copyfileobj(response, self.wfile, 1024 * 1024) + upstream.close() + + def _forward_chunked_body(self, upstream): + while True: + size_line = self.rfile.readline() + if not size_line: + raise ConnectionError("chunked request ended before size line") + upstream.send(size_line) + chunk_size = int(size_line.split(b";", 1)[0].strip(), 16) + if chunk_size == 0: + while True: + trailer_line = self.rfile.readline() + upstream.send(trailer_line) + if trailer_line in (b"\r\n", b"\n", b""): + return + + remaining = chunk_size + 2 + while remaining: + data = self.rfile.read(min(1024 * 1024, remaining)) + if not data: + raise ConnectionError("chunked request ended inside data") + upstream.send(data) + remaining -= len(data) + + def log_message(self, fmt, *args): + print("%s - %s" % (self.address_string(), fmt % args), flush=True) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--port", type=int, default=18765) + args = parser.parse_args() + server = ThreadingHTTPServer(("127.0.0.1", args.port), GiteaProxy) + print(f"Listening on 127.0.0.1:{args.port}", flush=True) + server.serve_forever() + + +if __name__ == "__main__": + main()