diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index 6d28680..edab967 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -153,6 +153,26 @@ jobs: shell: bash run: bash packaging/build-runtime.sh + # ── CRLF 断言(rant 2026-08-13T09:44:32)──────────────── + # git blob 的 *.cmd 是 LF,工作区 CRLF 靠 .gitattributes checkout 转换; + # 若 CI 检出未应用 .gitattributes,build-runtime 拷到的就是 LF → + # cmd.exe 整文件串行拼接 → 安装器 "stop-emrg.cmd exit code 1"。 + # build-runtime.sh 已强制转 CRLF(R126),此步双保险验证产物确实是纯 CRLF。 + - name: Verify runtime *.cmd are pure CRLF + shell: bash + run: | + set -euo pipefail + for f in dist/runtime/bin/emrg.cmd dist/runtime/bin/emrgd.cmd dist/runtime/bin/stop-emrg.cmd; do + [ -f "$f" ] || continue + cr=$(tr -cd '\r' < "$f" | wc -c) + lf=$(tr -cd '\n' < "$f" | wc -c) + if [ "$cr" -ne "$lf" ] || [ "$lf" -eq 0 ]; then + echo "::error::$f has bare-LF line(s) ($((lf-cr)) of $lf) — cmd.exe misparses LF → installer exit 1" + exit 1 + fi + echo "OK $f: pure CRLF ($lf lines)" + done + # ── 图标资产生成(rant 2026-08-11T18:28:09:仓库只保留 icon.svg 设计源, # png/icns/ico 由 CI 构建时现生成,必须先于 GUI 构建执行)── - name: Generate icon assets (icon.svg → png/icns/ico) diff --git a/Agent.md b/Agent.md index 645cd20..daf0a0b 100644 --- a/Agent.md +++ b/Agent.md @@ -118,7 +118,7 @@ Community needs voiced in HN agent-UI discussions map directly to EMRG's design: pkill -f "emrg.server"; rm -f ~/.emrg/emrgd.port; python -m emrg ``` -Python: `uv run pytest tests/ -v` (758) — import check: `uv run python -c "from emrg.client.app import run_client"` +Python: `uv run pytest tests/ -v` (760) — import check: `uv run python -c "from emrg.client.app import run_client"` GUI: `cd emrg/gui && npm test` (229: 44 daemon_client + 19 conn-manager + 22 app-commands + 107 renderer smoke + 15 i18n + 7 integration + 3 commands + 5 build-config + 7 gui-state) — syntax: `node --check main.js preload.js daemon_client.js renderer/js/*.js` CI: `uv run pytest` (ubuntu + **windows-2025 matrix** — Windows pytest 回归在 PR CI 即失败,v0.2.29 教训 #725) + GUI tests + **actionlint workflow lint** (`rhysd/actionlint@v1.7.12` gate, #444 — workflow 解析错误在 PR CI 即失败,如 `if:` secrets 上下文) Re-trigger: `scripts/re-trigger-ci.sh [branch]` (workflow_dispatch, #527 — 替代空 commit 重触发:Actions outage 会整段丢弃 push 事件,dispatch 走 API 路径不受影响) diff --git a/packaging/build-runtime.sh b/packaging/build-runtime.sh index 32bed99..9e1a43a 100755 --- a/packaging/build-runtime.sh +++ b/packaging/build-runtime.sh @@ -71,6 +71,21 @@ cp -R "$PY_ROOT/." "$DIST/bin/python-dist/" cp "$ROOT/bin/stop-emrg.cmd" stop-emrg.cmd 2>/dev/null || true cp "$ROOT/bin/emrg-uninstall" emrg-uninstall chmod +x emrg emrgd emrg-uninstall + # R126: rant 2026-08-13T09:44:32 — 打包强制 CRLF,不依赖 .gitattributes 在 CI + # 检出是否生效(git blob 是 LF;工作区 CRLF 靠 checkout 转换,CI 打包若拿到 + # LF → cmd.exe 整文件串行拼接 → 安装器 "stop-emrg.cmd exit code 1")。 + # 拷贝后统一为纯 CRLF(幂等:先归 LF 再转 CRLF),并断言纯 CRLF 才继续。 + for f in emrg.cmd emrgd.cmd stop-emrg.cmd; do + [ -f "$f" ] || continue + python3 - "$f" <<'PY' +import sys +p = sys.argv[1] +b = open(p, 'rb').read() +b = b.replace(b'\r\n', b'\n').replace(b'\n', b'\r\n') +assert b.count(b'\r\n') == b.count(b'\n') and b.count(b'\n') > 0, f'{p} CRLF normalization failed' +open(p, 'wb').write(b) +PY + done ) # ── 3. source/(只读,排除 gui node_modules R14;⚠️ 只含 emrg/ 源码,不含 bin/ —— diff --git a/tests/test_installer_stop.py b/tests/test_installer_stop.py index e6ec0de..e1e1efa 100644 --- a/tests/test_installer_stop.py +++ b/tests/test_installer_stop.py @@ -133,3 +133,23 @@ def test_build_runtime_copies_stop_scripts(): content = (REPO_ROOT / "packaging" / "build-runtime.sh").read_text(encoding="utf-8") assert 'cp "$ROOT/bin/stop-emrg.cmd" stop-emrg.cmd' in content assert 'cp "$ROOT/bin/stop-git.ps1"' not in content + + +def test_build_runtime_forces_crlf(): + """R126 / rant 2026-08-13T09:44:32 — build-runtime.sh must force pure CRLF + on the packaged *.cmd files (git blob is LF; .gitattributes checkout + conversion may not apply in CI → LF .cmd misparsed by cmd.exe → installer + "exit code 1").""" + content = (REPO_ROOT / "packaging" / "build-runtime.sh").read_text(encoding="utf-8") + # Normalization loop covers all three Windows launchers + assert "for f in emrg.cmd emrgd.cmd stop-emrg.cmd" in content + # Pure-CRLF assertion gates the build (LF-only → build fails) + assert "b.count(b'\\r\\n') == b.count(b'\\n')" in content + + +def test_build_release_workflow_verifies_crlf(): + """R126 companion — build-release.yml must assert packaged *.cmd are pure + CRLF after the Build runtime step (CI red on LF).""" + wf = (REPO_ROOT / ".github" / "workflows" / "build-release.yml").read_text(encoding="utf-8") + assert "Verify runtime *.cmd are pure CRLF" in wf + assert "bare-LF" in wf