R7.1: bundled installers, one dmg and one setup.exe for all six apps

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
ZacharyZhang-NYandClaude Opus 5.5 committed 2026-10-16 11:17:47 -04:00
1 parent e22f276c1d
commit b12858c7f0
9 files changed
+200 -21

No files matched your search

+4 -1
View File
@@ -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 不出合集。单个应用的包、更新清单照旧。
+31 -19
View File
@@ -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 <out> <volume> <app>...; 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"
+53
View File
@@ -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}"
+27
View File
@@ -0,0 +1,27 @@
#!/usr/bin/env bash
# Builds dist/ElyOffice-<version>-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"
+4 -1
View File
@@ -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
+11
View File
@@ -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] 并链过去
+3
View File
@@ -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`.
+1
View File
@@ -0,0 +1 @@
PASS
+66
View File
@@ -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"