dwgx@blog:~$dwgx
> cd ../posts

windsurf-ls-release: WindsurfAPI 向けに作った language server バイナリのミラー

// created

windsurf-ls-release は、もう一つの自分のプロジェクト WindsurfAPI 向けに作った language server バイナリのミラーリポジトリだ。WindsurfAPI を動かすには language server バイナリが必要で、この language_server_* の一式は Devin Desktop 公式のデスクトップパッケージの中に入っている。しかも配置・バージョン・構造は予告なく変わる。ユーザーの install-ls.sh から、いつ移動するか分からない上流に直接つなぐのは避けたかったので、このミラーを別に立てた。

毎日定期的に上流の最新バイナリを抜き出し、GitHub Release として再配布して fallback にする。インストーラはここを指せばいい。

https://github.com/dwgx/windsurf-ls-release/releases/latest/download

はっきり言っておくと、配布しているのは上流パッケージから直接抽出したバイナリで、自分がコンパイルしたものではない。自分は運搬と検証をする中間層でしかない。

中核は GitHub Actions で、cron: "17 3 * * *" として毎日未明に走る。三つのステップがそれぞれ三本の純 Python スクリプトに対応する。discover_release.pydocs.devin.ai をスクレイピングし、ページ内の JSON/MDX ブロックを解析して、stable と next の二つのチャンネルの最新版を取り、manifest を組み立てる。build_assets.py は manifest に従って公式アーカイブをダウンロードし、Linux は .tar.gz、mac/Win は .zip で、そこから language_server_* を抜き出す。展開部分にはパストラバーサル対策を入れてある。アーカイブの中身は信用できないし、外に書き込もうとする ../../ が仕込まれていないとも限らないからだ。render_release_notes.py は release notes をレンダリングし、あわせて release-manifest.jsonSHA256SUMS を付ける。

気にしているポイントがいくつかある。stable は Latest として、next は prerelease として発行する。こうすれば /releases/latest/download/ は常に安定版だけを指し、next に上書きされてユーザーを壊すことがない。fail closed: 上流の構造が変わったらスクリプトは即座に終了し、間違ったものを黙って発行することは絶対にしない。ミラーが一番やってはいけないのは、静かに間違ったバージョンを発行することだ。デフォルトブランチ以外では検証のみで発行はしない。実際の発行はデフォルトブランチでのみ行い、stable は v<version>、next は next-v<version> のタグを打つ。プラットフォームは Linux x64 だけを必須成功とし、それ以外はベストエフォート。

ついでに tools/update_ls_linux_x64.sh も書いた。単独の POSIX shell アップデータで、まず ghfast.top プロキシを優先し、プロキシが落ちていれば直結にフォールバックする。ダウンロード後にまず SHA256 で検証してから差し替える。中国国内から GitHub を引くとき、プロキシのフォールバックは必須要件だ。

LS_CHANNEL=stable ./tools/update_ls_linux_x64.sh

プロジェクト全体は Python 3.12 の標準ライブラリのみを使い、サードパーティ依存はゼロ、テストは unittest。ダウンロード・展開・検証というだけの仕事のために、先に pip install であれこれ入れたくなかった。薄ければ薄いほどいい。今は毎日静かに一度走り、WindsurfAPI の公共バイナリソースとして生き続けている。