From b12858c7f095b83cd137dfaa3956d0ac2bf1a8c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Fri, 16 Oct 2026 11:17:27 -0400 Subject: [PATCH] R7.1: bundled installers, one dmg and one setup.exe for all six apps Co-Authored-By: Claude Opus 5.5 (1M context) --- agents.md | 5 ++- packaging/macos/release.sh | 50 +++++++++++++++---------- packaging/windows/bundle.nsi | 53 ++++++++++++++++++++++++++ packaging/windows/bundle.sh | 27 ++++++++++++++ scripts/ci.sh | 5 ++- tasks/TASKS.md | 11 ++++++ tasks/reviews/R7.1-r1.md | 3 ++ tasks/reviews/R7.1-r2.md | 1 + tests/scripts/windows-bundle.sh | 66 +++++++++++++++++++++++++++++++++ 9 files changed, 200 insertions(+), 21 deletions(-) create mode 100644 packaging/windows/bundle.nsi create mode 100644 packaging/windows/bundle.sh create mode 100644 tasks/reviews/R7.1-r1.md create mode 100644 tasks/reviews/R7.1-r2.md create mode 100644 tests/scripts/windows-bundle.sh diff --git a/agents.md b/agents.md index 485cea4..c82f7a2 100644 --- a/agents.md +++ b/agents.md @@ -141,7 +141,8 @@ tests/scripts/linux-distros.sh deb 在 Ubuntu 26.04、rpm 在 Fedora 44 容器 cargo test -p ely-show --test compose 切换半程帧经 Metal 取像素(主线程,仅 macOS) tests/scripts/*.sh *.py 脚本测试(缺工具时报 SKIP) packaging/macos/bundle.sh ely-doc debug 出 dist/ElyDoc.app(带图标、文档类型与该应用的引擎) -packaging/macos/release.sh [批次] 该批各应用的 release 包与 dmg;ELY_SIGN_IDENTITY 加 ELY_NOTARY_PROFILE 即公证 +packaging/macos/release.sh [批次] 该批各应用的 release 包与 dmg,另有装全部的 ElyOffice dmg;ELY_SIGN_IDENTITY 加 ELY_NOTARY_PROFILE 即公证 +packaging/windows/bundle.sh 由 dist 里各应用的安装程序出合集 ElyOffice 安装程序(先跑 installer.sh) packaging/icons/masters.py 写出 assets/icons 的全部 SVG 母版(图标设计只改这里) packaging/icons/build.sh 由 assets/icons 的母版渲染 icns、png、ico(只在 macOS) tests/scripts/icons.sh 八个图标齐全、未过期,macOS 为 squircle,其余无底形 @@ -171,6 +172,7 @@ tests/scripts/autosave.sh 真窗口 ElyDoc:自动保存开关、停手即 tests/scripts/print.sh 真窗口 ElyDoc:打印用文档自己的纸张,取消不动文档(需辅助功能权限) tests/scripts/show-fullscreen.sh 真窗口 ElyShow:F5 放映的窗口盖住整块屏幕(菜单栏与程序坞之上,需辅助功能权限) tests/scripts/dock-macos.sh dist 里 ElyDoc、ElyTable、ElyShow 与包外的 target/debug/ely-doc 各启动数次:LOK worker 从未登记为程序坞应用,图块照出 +tests/scripts/windows-bundle.sh 合集安装程序逐个静默运行所选应用的安装程序;某个被拒即停下并报出其名与退出码(缺 makensis 时 SKIP) tests/scripts/windows-installer.sh 假应用的安装程序:所选目录原有的文件经两次安装与卸载都在,旧版按注册表清理,文件被占用或程序在运行时什么也不改,写不进时放回旧版(缺 makensis 时 SKIP) tests/scripts/types.sh packaging/types.tsv 的文件类型在 Linux 桌面项、Windows 安装程序与 macOS 文档类型里一致,从不设为默认 tests/scripts/layout.sh 真窗口:六个应用在 640、800、1100 宽下无元素越出窗口左右,留截图(需辅助功能与屏幕录制权限) @@ -306,3 +308,4 @@ cargo run -p ely-doc 启动一个应用(ElyDoc 先 cargo build -p ely - 2026-10-15(用户报:ElyMark 三、四级标题不显眼):组件库 5954132 起标题字号随正文按 Typora 的倍数(一至六级 2.25、1.75、1.5、1.25、1、1),一律加粗,六级为次要色,行高为字号的 1.3 倍(原为固定的 24、20、16、14 像素,正文 13,三、四级只是中等字重)(R6.9)。90bd37b:粗体与标题同一字重(原粗体为半粗,标题里的粗体反而更细,正文粗体随之由半粗改为粗,同 Typora);标题行高按自身字号取 1.3 倍。Windows 真窗口核对(`.work/r69`):可视模式各级标题分明、“你好↵测试”两行,源码模式“测”后的插入点落在两字之间。 - 2026-10-15(用户报:macOS 打开一个文档,程序坞里有两个同样的图标):LOK worker 在应用包的 `Contents/MacOS` 里启动 AppKit,系统按包的 Info.plist 把它也登记为常规应用(`lsappinfo` 里应用与 worker 都是 Foreground),程序坞多一个图标;内核自己从不设激活策略。worker 在内核启动 AppKit 之前(`Lok::hook` 打开内核之后、调钩子之前)把本进程主包信息里的 `LSUIElement` 设为真,AppKit 登记时即为辅助应用(无程序坞图标、无菜单栏,仍可建屏外窗口);钩子之后核对激活策略是辅助,不是即报错、不启动(`ffi/accessory.rs`);包外的 worker(`cargo run`、单测)无包可登记,策略为禁止,此时改设辅助(R6.12 第 2 轮)。原先在登记之后才改策略,审查实测 15 次启动有 11 次先登记为 Foreground 约 30 毫秒(R6.12 第 1 轮)。`tests/scripts/dock-macos.sh` 连续启动三个应用与包外的开发构建,从 worker 启动起采样其类型到首块图块,见 Foreground 即失败(R6.12)。 - 2026-10-15(用户报:ElyShow 编辑区滚轮与上下键不换页,三端):内核在未选中对象时把上下键当作滚动窗口(`fupoor.cxx`),Ely 的画布整页适合、无处可滚,滚轮同样。`LokView::turn_parts`(ElyShow 首次适合时调用)之后:整页可见时滚轮换页(`view/turn.rs`):滚轮的行按本平台每格的行数(`ely_lok::notch`:macOS 1,Windows 取系统设置,X11 与 Wayland 为 3)折成格,整格的行每格一页(快速连滚与一次多格都照格数);一串行里出现不足一格的(X11、Windows 的触控板)即为平滑滚动,此后到停顿前不再换页,像素(macOS、Wayland 的触控板)同按手势:一次手势换一页(满一格或 50 像素),惯性不算;有起止的手势(macOS)只到结束才算完,没有起止的停 200 毫秒即为下一次。X11 的滚轮与触控板同为行、gpui 不分,以整格开头的触控板滑动在出现小数之前会按格换页(已知限制)。未选中对象、未编辑文字时上下键换页。换页一律经有序输入队列交 worker(`TURN`):worker 按之前各输入回报后的状态判定,有选区、选中对象或插入点即把方向键照常交内核,否则当场切页(到首末页即停,同缩略图条的 `setPart`),视图在落地时换上 worker 所示的页,其后排队的键与点按都落在新页上(Tab 后紧接的 Down 移动所选对象;Down、Tab、Down 是换页后在新页上选中并移动);ElyShow 后台读回的当前页只在视图已无待落地输入时采用(R6.13)。 +- 2026-10-16(用户定):合集安装包(R7.1)。macOS 一个公证的 `ElyOffice-<版本>-<架构>.dmg`,装首批六个已签名的 .app 与“应用程序”替身,用户拖入所要的(`release.sh` 的 `dmg` 与单个应用共用);无 Developer ID Installer 证书,不出 .pkg。Windows 一个 `ElyOffice-<版本>-windows-x86_64-setup.exe`(`packaging/windows/bundle.nsi`),组件页勾选应用,所选的各自的安装程序以 `/S` 运行,所以装法、位置、升级、卸载项与单装完全相同;某个退出码非零即停下并写明其名与退出码(2 为文件被占用或位置被占),之前装上的照旧。Linux 不出合集。单个应用的包、更新清单照旧。 diff --git a/packaging/macos/release.sh b/packaging/macos/release.sh index 94c723c..ac7a6db 100755 --- a/packaging/macos/release.sh +++ b/packaging/macos/release.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Usage: release.sh [batch]; a dmg per app of the batch. +# Usage: release.sh [batch]; a dmg per app, one of all. set -euo pipefail root=$(cd "$(dirname "$0")/../.." && pwd) @@ -12,32 +12,44 @@ releases=https://git.zacharyzhang.com/ZacharyZhang-NY/ElyOffice/releases echo "release.sh: notarizing needs ELY_SIGN_IDENTITY, a Developer ID" >&2 exit 1 } +# Usage: dmg ...; apps beside Applications. +dmg() { + local out=$1 volume=$2 stage + shift 2 + rm -f "$out" + # Drag an app onto Applications. + stage=$(mktemp -d) + for app in "$@"; do ditto "$app" "$stage/$(basename "$app")"; done + ln -s /Applications "$stage/Applications" + hdiutil create -quiet -volname "$volume" -srcfolder "$stage" -format UDZO "$out" + rm -rf "$stage" + if [ -n "${ELY_NOTARY_PROFILE:-}" ]; then + codesign --force --timestamp --sign "$ELY_SIGN_IDENTITY" "$out" + xcrun notarytool submit "$out" --keychain-profile "$ELY_NOTARY_PROFILE" --wait + xcrun stapler staple "$out" + else + echo "release.sh: $out is not notarized (no ELY_NOTARY_PROFILE)" >&2 + fi + echo "$out" +} "$root/packaging/source.sh" "$dist" awk -F'\t' -v batch="$batch" '$4 == batch { print $1 "\t" $2 }' "$root/packaging/apps.tsv" | while IFS=$'\t' read -r slug name; do "$root/packaging/macos/bundle.sh" "$slug" release codesign --verify --deep --strict "$dist/$name.app" - dmg=$dist/$name-$version-$arch.dmg - rm -f "$dmg" - # The app beside Applications: drag one onto the other. - stage=$(mktemp -d) - ditto "$dist/$name.app" "$stage/$name.app" - ln -s /Applications "$stage/Applications" - hdiutil create -quiet -volname "$name" -srcfolder "$stage" -format UDZO "$dmg" - rm -rf "$stage" - if [ -n "${ELY_NOTARY_PROFILE:-}" ]; then - codesign --force --timestamp --sign "$ELY_SIGN_IDENTITY" "$dmg" - xcrun notarytool submit "$dmg" --keychain-profile "$ELY_NOTARY_PROFILE" --wait - xcrun stapler staple "$dmg" - else - echo "release.sh: $dmg is not notarized (no ELY_NOTARY_PROFILE)" >&2 - fi - echo "$dmg" + out=$dist/$name-$version-$arch.dmg + dmg "$out" "$name" "$dist/$name.app" # What the app's update check reads from the latest release. - sha=$(shasum -a 256 "$dmg" | cut -d' ' -f1) - url="$releases/download/v$version/$(basename "$dmg")" + sha=$(shasum -a 256 "$out" | cut -d' ' -f1) + url="$releases/download/v$version/$(basename "$out")" printf '{"version": "%s", "assets": {"macos-%s": {"url": "%s", "sha256": "%s"}}}\n' \ "$version" "${arch/arm64/aarch64}" "$url" "$sha" >"$dist/$slug.json" echo "$dist/$slug.json" done +# Every app of the batch in one dmg. +apps=() +for name in $(awk -F'\t' -v batch="$batch" '$4 == batch { print $2 }' "$root/packaging/apps.tsv"); do + apps+=("$dist/$name.app") +done +dmg "$dist/ElyOffice-$version-$arch.dmg" "Ely Office" "${apps[@]}" rm -rf "$root/target/release/incremental" diff --git a/packaging/windows/bundle.nsi b/packaging/windows/bundle.nsi new file mode 100644 index 0000000..46e63a1 --- /dev/null +++ b/packaging/windows/bundle.nsi @@ -0,0 +1,53 @@ +; Installs the ticked apps, each by its own setup. +Unicode true +!include "MUI2.nsh" + +!ifndef VERSION + !error "pass -DVERSION" +!endif +!ifndef APPS + !error "pass -DAPPS, from packaging/windows/bundle.sh" +!endif +!ifndef OUTFILE + !error "pass -DOUTFILE" +!endif + +Name "Ely Office ${VERSION}" +OutFile "${OUTFILE}" +RequestExecutionLevel user +; The setups inside are compressed already. +SetCompress off + +!insertmacro MUI_PAGE_COMPONENTS +!insertmacro MUI_PAGE_INSTFILES +!insertmacro MUI_LANGUAGE "SimpChinese" +!insertmacro MUI_LANGUAGE "English" + +LangString Failed ${LANG_SIMPCHINESE} "$R0 未装上,其安装程序退出码为 $R1(2 表示文件被占用或安装位置被占)。之前装上的应用照旧可用。" +LangString Failed ${LANG_ENGLISH} "$R0 was not installed; its setup exited with $R1 (2: a file is in use or in the way). Apps installed before it work." + +Function .onInit + InitPluginsDir +FunctionEnd + +; One section per app; its setup runs silent. +!macro ElyApp NAME SETUP +Section "${NAME}" + SetOutPath "$PLUGINSDIR" + File "/oname=${NAME}-setup.exe" "${SETUP}" + DetailPrint "${NAME}" + StrCpy $R0 "${NAME}" + ClearErrors + ExecWait '"$PLUGINSDIR\${NAME}-setup.exe" /S' $R1 + IfErrors 0 +2 + StrCpy $R1 "-" + Delete "$PLUGINSDIR\${NAME}-setup.exe" + StrCmp $R1 0 ely_done_${NAME} + MessageBox MB_ICONSTOP "$(Failed)" /SD IDOK + SetErrorLevel 2 + Abort + ely_done_${NAME}: +SectionEnd +!macroend + +!include "${APPS}" diff --git a/packaging/windows/bundle.sh b/packaging/windows/bundle.sh new file mode 100644 index 0000000..26e7279 --- /dev/null +++ b/packaging/windows/bundle.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# Builds dist/ElyOffice--windows-x86_64-setup.exe from the apps' setups. +set -euo pipefail + +root=$(cd "$(dirname "$0")/../.." && pwd) +dist=${ELY_DIST:-$root/dist} +version=$(awk -F'"' '/^version = / { print $2; exit }' "$root/Cargo.toml") +makensis=${MAKENSIS:-"/c/Program Files (x86)/NSIS/makensis.exe"} +[ -x "$makensis" ] || { + echo "bundle.sh: no makensis at $makensis; install NSIS" >&2 + exit 1 +} +apps=$root/target/stage/bundle-apps.nsh +mkdir -p "$(dirname "$apps")" +: >"$apps" +for name in $(awk -F'\t' '$4 == 1 { print $2 }' "$root/packaging/apps.tsv"); do + setup=$dist/$name-$version-windows-x86_64-setup.exe + [ -f "$setup" ] || { + echo "bundle.sh: no $setup; run installer.sh first" >&2 + exit 1 + } + printf '!insertmacro ElyApp "%s" "%s"\n' "$name" "$(cygpath -w "$setup")" >>"$apps" +done +out=$dist/ElyOffice-$version-windows-x86_64-setup.exe +"$makensis" -V2 -INPUTCHARSET UTF8 -DVERSION="$version" -DAPPS="$(cygpath -w "$apps")" \ + -DOUTFILE="$(cygpath -w "$out")" "$(cygpath -w "$root/packaging/windows/bundle.nsi")" +echo "$out" diff --git a/scripts/ci.sh b/scripts/ci.sh index 32bf496..3451859 100755 --- a/scripts/ci.sh +++ b/scripts/ci.sh @@ -55,7 +55,10 @@ rm -rf "$dist" batch=$(awk -F'\t' '$4 == 1 { print $1 }' packaging/apps.tsv) case $os in macos) packaging/macos/release.sh 1 ;; - windows) for slug in $batch; do packaging/windows/installer.sh "$slug"; done ;; + windows) + for slug in $batch; do packaging/windows/installer.sh "$slug"; done + packaging/windows/bundle.sh + ;; linux) for slug in $batch; do ${inside[@]+"${inside[@]}"} packaging/linux/package.sh "$slug"; done ;; *) echo "ci.sh: no release for $os" >&2 diff --git a/tasks/TASKS.md b/tasks/TASKS.md index 51c0e64..9f75b24 100644 --- a/tasks/TASKS.md +++ b/tasks/TASKS.md @@ -273,3 +273,14 @@ - [x] R6.12 macOS 打开一个文档,程序坞里 ElyDoc、ElyTable、ElyShow 各出现两个图标(用户 2026-10-15 报):LOK worker 在应用包内启动 AppKit,系统按包的 Info.plist 把它也登记为常规应用(`lsappinfo` 两个 Foreground);worker 在内核启动 AppKit 前把本进程主包信息的 `LSUIElement` 设为真,登记即为辅助应用,钩子后核对策略(第 1 轮:原先登记后才改,15 次有 11 次先为 Foreground);`tests/scripts/dock-macos.sh` 在 Mac mini 上 30 次启动全为 UIElement,旧 worker 即失败;第 2 轮:包外的 worker(`cargo run`、单测)策略为禁止,原核对即退出,改设辅助,测试另启包外的开发构建(40 次全过);第 3 轮通过 - [x] R6.13 ElyShow 编辑区的滚轮与上下键不换页(用户报,三端;放映时正常):内核在未选中对象时把上下键当作滚动窗口,Ely 的画布整页适合、无处可滚;整页可见时滚轮换页(每格一页,触控板一次手势一页),未选中对象、未编辑文字时上下键换页,同缩略图条的路径,到首末页即停;第 1 轮:Windows 一格三行原换三页,X11 触控板的小数行一次滑动换多页,按住手指停顿后再滑又换一页,Tab 后紧接的 Down 按旧状态换页;改为按平台每格行数计格、小数行与像素按手势计、有起止的手势只到结束才算完、上下键由 worker 按输入次序判定;第 2 轮:换页在 worker 回复后才排进队列,排在其后的键先作用于旧页,以整格开头的 X11 平滑滚动仍按格多次换页;改为换页(键与滚轮)都由 worker 在输入次序中当场切页;第 3 轮:一律按手势计使快速连滚与一次多格只换一页,`LokView::part` 已无调用者;改为整格的行每格一页、出现小数即按手势,删去 `part`(第 3 轮为最后一轮,修后不再审查) - [x] R6.14 重发 0.1.5(用户定:仍为 0.1.5,全部重建并重传):删去 v0.1.5 的全部文件,标签移到 R6.12、R6.13 的提交,三端全部包重建并上传,本机重装(标签在 aa419fd;发布流程的 Windows 与 Linux 任务建出并上传,macOS 任务因仓库未设签名变量即停,dmg 由 Mac mini 本机建、公证、钉票;44 个文件,同名的源码包只留 Linux 的一份,见 P3-16;Mac mini 上 `dock-macos.sh` 对公证的三个包各五次启动全为 UIElement;本机六个应用经下载回的安装程序静默升级到 0.1.5,ElyMark 目录无 `git`,冒烟启动均正常退出、日志无警告) + +## R7 0.1.6(用户 2026-10-16 定:Gitea #3 至 #6 与 ElyMark 正文块;合集安装包;修完发 0.1.6,逐条答复,功能建议另开 [FEATURE REQUEST]) + +- [x] R7.1 合集安装包(§4,用户定):macOS 一个公证的 dmg 装六个 .app 与“应用程序”替身,用户拖入所要的;Windows 一个安装程序,组件页勾选六个应用,每个与单独安装程序装得一样;Linux 不做合集;单个应用的包照旧(`release.sh` 的 `dmg` 单个与合集共用,Mac mini 上以两个假应用核对卷内内容;`bundle.nsi` 逐个以 `/S` 运行所选的安装程序,某个退出码非零即停下并写明;`windows-bundle.sh` 三个假应用:都装上,第二个被拒时退出码 2、第三个不运行;真包随 R7.7 建出 — 第 1 轮:被拒的是最后一个,去掉 `Abort` 测试仍过;加第三个应用后去掉即失败;第 2 轮 PASS) +- [ ] R7.2 设置窗口多出一行应用菜单(#6,§5):设置窗口沿用主窗口的外壳 `Shell`,Windows 与 Linux 上随之画出菜单栏与更新横幅;设置窗口不画这两者 +- [ ] R7.3 装了宋体仍报缺少(#4,§7、§8):内核按 `宋体` 找到 SimSun,缺字检查却比较原名与所用字体的英文名,报为缺失;查明 Linux 上的版面是否真用 SimSun,一页变两页的根因 +- [ ] R7.4 打开 PDF 总说已损坏(#5,§11):画图块时 MuPDF 的任何警告都算损坏,未嵌入的中日韩字体(“non-embedded font using identity encoding”)即触发;损坏只认结构修复、读不出的对象与流、画不出的图片与内容 +- [ ] R7.5 ElyMark 打开的正文块变成一块深色框(§14,用户定):按下的段落保持原样,不加底色与内边距,不挪位置;代码块仍有自己的底板 +- [ ] R7.6 Windows 与 Linux 不能打印(#3,§19.3):`print_pdf` 只有 macOS;Windows 用系统打印对话框,Linux 经 xdg 桌面门户的打印接口 +- [ ] R7.7 发布 0.1.6:版本号,三端建包、签名公证、上传,合集包一并上传,本机装上 +- [ ] R7.8 答复 #3 至 #6:每条写已修于 0.1.6;#6 的工作区建议另开 [FEATURE REQUEST] 并链过去 diff --git a/tasks/reviews/R7.1-r1.md b/tasks/reviews/R7.1-r1.md new file mode 100644 index 0000000..30e81f1 --- /dev/null +++ b/tasks/reviews/R7.1-r1.md @@ -0,0 +1,3 @@ +FAIL + +- tests/scripts/windows-bundle.sh:53 — [P2] The refusal test cannot detect continuing after a failed installer: it locks the last of only two apps, then checks the exit code and existing files. Removing only `Abort` from `bundle.nsi` still makes this test report PASS. A separate three-app probe confirms the consequence: when app two exits 2, the mutant runs app three, while the current bundle correctly stops after app two; both return 2. Add a selected app after the failing one and assert that it is never invoked or changed, using an invocation marker or different upgrade payload. Evidence: `.work/r71-r1/mutation-no-abort.log` and `.work/r71-r1/stop-probe.log`. diff --git a/tasks/reviews/R7.1-r2.md b/tasks/reviews/R7.1-r2.md new file mode 100644 index 0000000..7ef22e9 --- /dev/null +++ b/tasks/reviews/R7.1-r2.md @@ -0,0 +1 @@ +PASS diff --git a/tests/scripts/windows-bundle.sh b/tests/scripts/windows-bundle.sh new file mode 100644 index 0000000..1f45f0d --- /dev/null +++ b/tests/scripts/windows-bundle.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash +# The bundle runs each setup; a refusal stops it. +set -euo pipefail + +root=$(cd "$(dirname "$0")/../.." && pwd) +makensis=${MAKENSIS:-"/c/Program Files (x86)/NSIS/makensis.exe"} +case "$(uname -s)" in MINGW* | MSYS* | CYGWIN*) ;; *) echo "SKIP: not Windows"; exit 0 ;; esac +[ -x "$makensis" ] || { echo "SKIP: no makensis"; exit 0; } +export MSYS2_ARG_CONV_EXCL='*' + +work=$root/target/bundle-test +names="ElyProbe ElyProbeTwo ElyProbeThree" +clean() { + for name in $names; do + reg delete "HKCU\\Software\\Ely Office\\$name" /f >/dev/null 2>&1 || true + reg delete "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Ely Office $name" /f >/dev/null 2>&1 || true + rm -f "$APPDATA/Microsoft/Windows/Start Menu/Programs/$name.lnk" + done +} +trap 'kill $(jobs -p) 2>/dev/null || true; clean' EXIT +clean +rm -rf "$work" +mkdir -p "$work" +fail() { echo "FAIL: $*" >&2; exit 1; } + +printf '!macro ElyTypesInstall\n!macroend\n!macro ElyTypesUninstall\n!macroend\n' >"$work/types.nsh" +: >"$work/apps.nsh" +for name in $names; do + slug=$(echo "$name" | sed 's/\([A-Z]\)/-\L\1/g; s/^-//') + stage=$work/stage-$name + mkdir -p "$stage" + echo "$name" >"$stage/$slug.exe" + "$root/packaging/windows/files.sh" "$stage" "$slug" >"$work/files-$name.txt" + "$makensis" -V1 -INPUTCHARSET UTF8 -DAPP_NAME="$name" -DAPP_SLUG="$slug" -DVERSION=1 \ + -DSTAGE="$(cygpath -w "$stage")" -DICON="$(cygpath -w "$root/assets/icons/ely-mark.ico")" \ + -DTYPES="$(cygpath -w "$work/types.nsh")" -DLIST="$(cygpath -w "$work/files-$name.txt")" \ + -DOUTFILE="$(cygpath -w "$work/$name-setup.exe")" "$(cygpath -w "$root/packaging/windows/installer.nsi")" + printf '!insertmacro ElyApp "%s" "%s"\n' "$name" "$(cygpath -w "$work/$name-setup.exe")" >>"$work/apps.nsh" + # Each setup installs where its own key points. + reg add "HKCU\\Software\\Ely Office\\$name" /v InstallDir /d "$(cygpath -w "$work/app-$name")" /f >/dev/null +done +"$makensis" -V1 -INPUTCHARSET UTF8 -DVERSION=1 -DAPPS="$(cygpath -w "$work/apps.nsh")" \ + -DOUTFILE="$(cygpath -w "$work/bundle.exe")" "$(cygpath -w "$root/packaging/windows/bundle.nsi")" + +run() { "$work/bundle.exe" /S && echo 0 || echo $?; } +[ "$(run)" = 0 ] || fail "the bundle failed" +for name in $names; do + [ -f "$work/app-$name/.ely-installed" ] || fail "$name has no install list" + reg query "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Ely Office $name" >/dev/null 2>&1 || + fail "$name has no uninstall entry" +done + +# The second app is refused; the third never runs. +rm -rf "$work/app-ElyProbeThree" "$work/held" +held=$(cygpath -w "$work/app-ElyProbeTwo/ely-probe-two.exe") +powershell -NoProfile -Command "\$f=[IO.File]::Open('$held','Open','Read','Read'); \ + New-Item '$(cygpath -w "$work/held")' | Out-Null; Start-Sleep 60" & +until [ -e "$work/held" ]; do sleep 0.2; done +result=$(run) +kill %% 2>/dev/null; wait 2>/dev/null || true +[ "$result" = 2 ] || fail "the bundle exited $result over a refused app" +grep -q ElyProbeTwo "$work/app-ElyProbeTwo/ely-probe-two.exe" || fail "the refused app changed" +[ -f "$work/app-ElyProbe/.ely-installed" ] || fail "the first app is gone" +[ ! -e "$work/app-ElyProbeThree" ] || fail "the bundle went on after a refusal" +rm -rf "$work" +echo "PASS: windows bundle"