Linux: RUNPATH guard, packaging in the container on CI, source archives per commit (L1.7)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ZacharyZhang-NYandClaude Opus 5.5 committed 2026-10-11 14:32:36 -04:00
1 parent a9f8136e46
commit 5accf492ba
14 files changed
+124 -26

No files matched your search

+3 -2
View File
@@ -1,8 +1,8 @@
# Build output and downloads
/target/
/dist/
/engines/
/reference/
/engines
/reference
node_modules/
.DS_Store
.ruff_cache/
@@ -17,3 +17,4 @@ node_modules/
/docs/test-map.md
/assets/papirus_icons-master.zip
/Gitea.md
/.work/
+3 -1
View File
@@ -136,6 +136,7 @@ tests/scripts/linux-backends.sh Linux 构建带 Wayland 与 X11;Hyprland 上
packaging/linux/container.sh 命令 在 Ubuntu 26.04 镜像里运行(引擎、应用、打包;目标在 target/ubuntu)
tests/scripts/linux-glibc.sh Linux 的应用、worker 与引擎要求的 glibc 不高于 2.43
packaging/linux/package.sh 应用 出该应用的 AppImage、deb、rpm 与 Arch 包到 dist/(在容器里运行)
packaging/source.sh dist 本提交的源码包与随包的 copyleft 上游源码(打过补丁的 MuPDF、Git),文件名带提交号;工作区有未提交改动即拒绝(macOS 的 release.sh 与 CI 的 Windows、Linux 调用)
tests/scripts/linux-packages.sh deb、rpm、Arch 包在干净的 Ubuntu 26.04、Fedora 44、Arch 容器里装上,每个 ELF 都找得到库
tests/scripts/linux-pins.sh Linux 打包工具(appimagetool、linuxdeploy、运行时、nfpm)的下载与钉的散列相符
tests/scripts/linux-installed.sh installed|appimage Hyprland 上装好的六个应用或 AppImage:一个窗口、随 Omarchy(Catppuccin)、读出页面文字(PDF、Markdown 经各自引擎),ElyTree 随换主题到 Latte(grim 按窗口截取)
@@ -293,7 +294,7 @@ cargo run -p ely-doc 启动一个应用(ElyDoc 先 cargo build -p ely
- 2026-10-09:每个应用只跑一份(缺陷清单 X-03)。`ely_ui::run` 在写日志之前取应用数据目录的 `instance.lock`(`ely_platform::instance::hold`;Unix 为 fcntl 记录锁,进程结束即放,被拒时以 `F_GETLK` 向系统问出持锁的进程号,不另存进程号文件,同时启动的几份也报得准;Windows 为 LockFileEx,不报进程号);已有一份在跑时第二份不启动,报“已在运行(进程号)”。原先第二份启动即清空第一份的工作目录、把它的恢复点当作干净结束删掉、改写崩溃锁。macOS 经访达打开文件本就交给运行中的那份;`open -n` 与 Linux 每文件一个进程时第二份被拒,文件须在已开的窗口里打开(未做转交)。`tests/scripts/one-instance.sh` 核对。
- 2026-10-10:ElyMark 的链接(M-05,我定,同常见编辑器):可视模式单击链接是编辑,Cmd 单击(Windows、Linux 为 Ctrl)交应用;预览单击即交应用;应用照 ElyPDF:http、https、mailto 交系统打开,其余提示拒绝。
- 2026-10-10:Windows 与 Linux 的构建环境(E-01)。Windows:MSVC(VS 2022,v143);MuPDF 由 `build-mupdf.sh` 以 MSBuild 编上游 `platform/win32` 的 `libmupdf`(静态运行库 /MT,同 Rust 的 `crt-static`;`HAVE_LIBCRYPTO`,`libmupdf.lib` 内含第三方、Tesseract 与 PKCS#7);静态 OpenSSL 取自钉在 2026.07.29 的 vcpkg(`engines/vcpkg`,`openssl:x64-windows-static`);Git 为 Git for Windows 的 MinGit 2.56.0(钉散列,`engines/git/cmd/git.exe`,LFS 在 `ucrt64/libexec/git-core`);CEF 照 cef-dll-sys 放在可执行文件旁(Windows、Linux 都是),不另载入;`%TEMP%` 下的程序被系统的应用控制拦下,构建与探针都不放那里;cmake 与 ninja 须在 PATH;LibreOffice 的 configure 只认 VS 自带的 CMake(组件 `VC.CMake.Project`)。仓库与 `reference/` 一律 LF(`core.autocrlf false`、`core.eol lf`;`fetch-references.sh` 对每个上游照设)。Linux:WSL 的 Ubuntu 26.04;静态 libcrypto 另需其 pkg-config 列出的库(jitterentropy、zstd、z)。LibreOffice 在 Linux 照 Collabora 的 LOKit 配置构建(无图形后端、cairo RGBA、自带各库)。
- 2026-10-10:Windows 与 Linux 的内核与 worker(W-01、K-01,用户定:0.1.3 先三端发布)。内核只认合并库(`ffi::KERNEL`:`libmergedlo.dylib`、`libmergedlo.so`、`mergedlo.dll`;`sofficeapp` 在合并构建里是文本占位);Windows 载入时一并搜索该 DLL 所在的 `program/`。查找次序照旧,Windows 与 Linux 的包内位置是应用旁的 `libreoffice/program`,开发构建为 `engines/lo-src/instdir/program`。worker 的主循环:macOS 照旧以 GCD;Linux 以 unipoll,`runLoop` 的轮询回调里每次答一个请求,内核的唤醒回调往同一队列放信号(`worker/unipoll.rs`;回调的数据指针须非空,VCL 见空即不调用);Windows 也以 unipoll:LOK 不另起主线程,worker 的主线程跑 `runLoop`(Windows 的消息循环),请求经只收消息的窗口投到这条线程上执行(`worker/messages.rs`),同 macOS 的 GCD;原先 LOK 自起主线程、worker 在另一线程作答,载入表格即死锁。Windows 的循环不调轮询回调,但须传非空的回调,`isUnipoll` 只看它。补丁 `0043-lok-callbacks-flush-inline-everywhere.patch`:回调队列在各平台都即时冲出(原只 macOS),否则请求里等的命令结果与状态要等下一轮主循环才到。`0042-svx-boost-json-needs-no-library.patch`:svxcore 与 sd 定义 `BOOST_ALL_NO_LIB`,MSVC 不再按 Boost 的自动链接索要 `libboost_json`、`libboost_container`。Linux 的内核以 cairo 画图块(worker 设 `SAL_DISABLESKIA`;构建去掉 `--enable-cairo-rgba`,自带的 cairo 要清掉重建),字节为 BGRA,同 macOS 与 Windows;`getTileMode` 只看 cairo 开关,不可信。worker 启动核对在三端都画一段红字,按 BGRA 找不到红色即拒绝。`scripts/apply-lo-patches.sh` 的临时库设 `core.autocrlf false`(Windows 默认转换使任何前缀都对不上)。Windows 的 LibreOffice 在 Cygwin 下构建(`build-libreoffice.sh` 的 CYGWIN 分支:去掉 Unix 专用开关,加 VS 2022,用 `/opt/lo/bin` 的 make,清掉 Git Bash 带进来的 `MSYSTEM`);构建目录与 Cygwin 排除在 Defender 扫描之外(否则并行 configure 删不掉 `conftest.dir`)。英文词典扩展里的 Lightproof(Python 写的语法检查)在构建后删去:macOS 与 Linux 的内核不带 Python,它本不生效;Windows 必带内置 Python(Cygwin 下 configure 不许去掉),第二次用同一配置启动时初始化它,worker 挂起。worker 以 `SAL_NOLOCK_PROFILE` 运行,`ely.xcd` 关掉 LibreOffice 的文档锁(`Common/Misc/UseLocking`):被强杀的 worker 留下的锁会让下次启动或载入弹出交互而挂起;每个应用只有一个 worker 用自己的配置,冲突由 Ely 自己核对。三端发布(CI-01):`.gitea/workflows/release.yml` 在打标签时于三台运行器上执行 `scripts/ci.sh`:macOS 在 Mac mini 的常驻克隆里按需从零构建引擎,Windows 在本机运行器上把 `engines` 与 `reference` 接到开发克隆,Linux 由同一台 Windows 运行器经 WSL 构建(服务器上的 Linux 运行器是一次性容器,建不了内核);各自打包后上传到该标签的发布。Windows 为 NSIS 安装程序,Linux 为 AppImage、deb、rpm 与 Arch 包,都由 `packaging/stage.sh` 分装引擎;更新清单只含 macOS(Windows 与 Linux 的自动安装未做)。
- 2026-10-10:Windows 与 Linux 的内核与 worker(W-01、K-01,用户定:0.1.3 先三端发布)。内核只认合并库(`ffi::KERNEL`:`libmergedlo.dylib`、`libmergedlo.so`、`mergedlo.dll`;`sofficeapp` 在合并构建里是文本占位);Windows 载入时一并搜索该 DLL 所在的 `program/`。查找次序照旧,Windows 与 Linux 的包内位置是应用旁的 `libreoffice/program`,开发构建为 `engines/lo-src/instdir/program`。worker 的主循环:macOS 照旧以 GCD;Linux 以 unipoll,`runLoop` 的轮询回调里每次答一个请求,内核的唤醒回调往同一队列放信号(`worker/unipoll.rs`;回调的数据指针须非空,VCL 见空即不调用);Windows 也以 unipoll:LOK 不另起主线程,worker 的主线程跑 `runLoop`(Windows 的消息循环),请求经只收消息的窗口投到这条线程上执行(`worker/messages.rs`),同 macOS 的 GCD;原先 LOK 自起主线程、worker 在另一线程作答,载入表格即死锁。Windows 的循环不调轮询回调,但须传非空的回调,`isUnipoll` 只看它。补丁 `0043-lok-callbacks-flush-inline-everywhere.patch`:回调队列在各平台都即时冲出(原只 macOS),否则请求里等的命令结果与状态要等下一轮主循环才到。`0042-svx-boost-json-needs-no-library.patch`:svxcore 与 sd 定义 `BOOST_ALL_NO_LIB`,MSVC 不再按 Boost 的自动链接索要 `libboost_json`、`libboost_container`。Linux 的内核以 cairo 画图块(worker 设 `SAL_DISABLESKIA`;构建去掉 `--enable-cairo-rgba`,自带的 cairo 要清掉重建),字节为 BGRA,同 macOS 与 Windows;`getTileMode` 只看 cairo 开关,不可信。worker 启动核对在三端都画一段红字,按 BGRA 找不到红色即拒绝。`scripts/apply-lo-patches.sh` 的临时库设 `core.autocrlf false`(Windows 默认转换使任何前缀都对不上)。Windows 的 LibreOffice 在 Cygwin 下构建(`build-libreoffice.sh` 的 CYGWIN 分支:去掉 Unix 专用开关,加 VS 2022,用 `/opt/lo/bin` 的 make,清掉 Git Bash 带进来的 `MSYSTEM`);构建目录与 Cygwin 排除在 Defender 扫描之外(否则并行 configure 删不掉 `conftest.dir`)。英文词典扩展里的 Lightproof(Python 写的语法检查)在构建后删去:macOS 与 Linux 的内核不带 Python,它本不生效;Windows 必带内置 Python(Cygwin 下 configure 不许去掉),第二次用同一配置启动时初始化它,worker 挂起。worker 以 `SAL_NOLOCK_PROFILE` 运行,`ely.xcd` 关掉 LibreOffice 的文档锁(`Common/Misc/UseLocking`):被强杀的 worker 留下的锁会让下次启动或载入弹出交互而挂起;每个应用只有一个 worker 用自己的配置,冲突由 Ely 自己核对。三端发布(CI-01):`.gitea/workflows/release.yml` 在打标签时于三台运行器上执行 `scripts/ci.sh`:macOS 在 Mac mini 的常驻克隆里按需从零构建引擎,Windows 在本机运行器上把 `engines` 与 `reference` 接到开发克隆,Linux 由同一台 Windows 运行器经 WSL 构建(服务器上的 Linux 运行器是一次性容器,建不了内核);各自打包后上传到该标签的发布。Windows 为 NSIS 安装程序,Linux 为 AppImage、deb、rpm 与 Arch 包(引擎构建与打包都经 `packaging/linux/container.sh` 进 Ubuntu 26.04 容器,WSL 里须有 docker;`ELY_SHARE` 的目录一并挂进容器),都由 `packaging/stage.sh` 分装引擎;每个平台都上传本提交的源码包(`packaging/source.sh`);更新清单只含 macOS(Windows 与 Linux 的自动安装未做)。
- 2026-10-11:gpui 的 Windows 窗口关闭(W-03 冒烟查出)。`gpui-pre-windows` 0.3.8 在窗口关闭时报三条错:系统已销毁窗口(ElyTree 的关窗交给系统)后 Drop 仍对旧句柄 `RevokeDragDrop` 与 `DestroyWindow`(拖放处理器因此不释放,连带窗口状态);销毁时的失活回报排在异步任务里,执行时窗口已移除(“window not found”);退出后后台任务向已销毁的平台窗口投递(`Invalid window handle`)。修复在 `vendor/gpui-pre-windows`:`WM_DESTROY` 时记下已销毁、在句柄仍有效时撤销拖放、去掉失活回调,Drop 见已销毁即不再动句柄;平台窗口已不在时投递失败只记 debug(应用已退出),其余投递失败照报。三个应用关窗与退出的日志无错。
- 2026-10-11(用户报:Windows 启动带黑色命令行窗口、“打开方式”里无图标、点 X 不退出、表格全黑无网格线):四处根因。命令行窗口:应用是控制台子系统;发行构建改为 `windows_subsystem = "windows"`(开发构建照旧有控制台看日志),子进程(两个 worker、Git、`cmd start`)一律经 `ely_ipc::windowless` 以 `CREATE_NO_WINDOW` 启动。图标:exe 里只有 gpui 的清单资源;各应用共用构建脚本 `packaging/windows/icon.rs`,把 `assets/icons/<应用>.ico` 编成资源 1(gpui 的窗口图标也取资源 1)。点 X 不退出:窗口其实弹了“保存更改”询问。`ely.xcd` 深色方案的 `CalcNotesBackground` 与浅色不同,Calc 在配色变化时改写批注样式并记一步撤销(`ScDetectiveFunc::UpdateAllComments`),未动的表格切到深色即算已修改;两套方案的批注底色改为同一值(LibreOffice 默认的浅黄),切换主题不再改文档,三端同此(`sheet.rs` 的 `a_theme_switch_is_no_edit`)。全黑:Win32 的虚拟设备没有 `SetSizeUsingBuffer`(基类默认返回假,断言在发行构建里不在),给客户缓冲区的设备停在 1×1 像素,Calc 图块只画了一个像素,放映图层同空;补丁 `0044-lok-win32-paints-then-copies.patch` 让 Windows 走 macOS 的路:按全尺寸画进设备,再拷进客户缓冲区(`doc_paintTile` 与 `SlideshowLayerRenderer`)。Writer 自设设备尺寸,原本不受影响。`tests/scripts/lo-patches.sh` 的临时库也设 `core.autocrlf false`(原先在 Windows 上恒失败)。
- 2026-10-11(用户报:Windows 上标题栏的最小化、最大化、关闭三个按钮都按不动):gpui 回答系统的命中测试(`WM_NCHITTEST`)时按绘制次序取第一个含指针的窗口控制区,标题栏的拖动区先于其中的按钮画出,按钮上一律答“标题栏”,系统把按下当作拖动窗口的开始,点击到不了按钮。修复在 `vendor/gpui-pre`(原样一个提交,其后是修复):按命中测试的次序取最上层的控制区,且只看未被遮挡的部分(同 `is_hovered`,盖在标题栏上的对话框挡住其下的按钮)。实测(发行构建,DPI 感知的探针移动真实指针):自右向左依次答关闭、最大化、最小化、拖动;按下最小化即最小化,最大化即最大化,关闭即退出、worker 随之结束;v0.1.3 的安装版同一探针处处答拖动。该回调只有 Windows 平台调用。
@@ -307,3 +308,4 @@ cargo run -p ely-doc 启动一个应用(ElyDoc 先 cargo build -p ely
- 2026-10-11(用户定):Linux 的引擎、应用与包都在 `ubuntu:26.04` 容器里构建(`packaging/linux/Dockerfile`,钉摘要;glibc 2.43),不在本机(Arch 的 glibc 更新,建出的程序在 Ubuntu 上起不来)。`packaging/linux/container.sh` 以本人身份运行,Cargo 的目标与家目录另放 `target/ubuntu*`;`stage.sh` 取 `CARGO_TARGET_DIR`;Git 2.56 用自己的 `target/` 链接其 Rust 部分,`build-git.sh` 为它去掉该变量。`tests/scripts/linux-glibc.sh` 核对每个动态链接的 ELF(静态的 git-lfs 不算)。
- 2026-10-11(用户要求:Linux 不只 AppImage):`packaging/linux/package.sh` 由同一个 AppDir 出四种包:AppImage,以及经 nfpm v2.47.0(钉散列)的 deb、rpm、Arch 包。包把 AppDir 的 `usr` 装到 `/opt/ely-office/<应用>`,`/usr/bin/<应用>` 为链接,桌面项与 512 像素图标在 `/usr/share`。依赖由 AppDir 里每个 ELF 所需而未随包的库,加 gpui 按名载入的 Vulkan 与 Wayland 客户端库得出:deb 与 Arch 按 `packaging/linux/depends.tsv` 换成包名(表里没有即打包失败),rpm 直接依赖库名(`libxcb.so.1()(64bit)`)。同级应用多找一处 `/opt/ely-office/<应用>/bin`(ElyMark 打开 ElyTree)。LibreOffice 的 cairo 由 meson 构建,内核直接拷其构建目录的产物,RUNPATH 仍指向构建树里的 pixman 与 fontconfig(别的机器上找不到);`build-libreoffice.sh` 在 Linux 上把指向源码树的 RUNPATH 改为 `$ORIGIN`(patchelf)。
- 2026-10-11:Linux 包的实测(L1.6)。本机(Omarchy,Hyprland 0.56)装上六个 Arch 包与六个 AppImage,`linux-installed.sh` 两种都过;deb、rpm、Arch 包在 Ubuntu 26.04、Fedora 44、Arch 容器里装上、全部 ELF 找得到库;deb 与 rpm 的六个应用在各自容器的 Xvfb 下画出窗口(`linux-distros.sh`)。截图按窗口(`grim -T` 加 Hyprland 的 `stableId`):按区域截时用户切换工作区或别的窗口盖住即截错。ElyMark 的文件监视略去读取后不会自激:重读前先比指纹(长度与修改时间),不打开文件。
- 2026-10-11(用户定:替换 v0.1.3 的 Linux 包):v0.1.3 发布里原有的六个 AppImage(无头,打不开窗口)已删去,换成本机容器里建出的六个应用各四种包(共 24 个),出自 L1.7 的提交(晚于该标签);上传后逐个核对大小,ElyTree 的 AppImage 下载回来散列相同。`package.sh` 把每个 RUNPATH 按所在 ELF 解析(`$ORIGIN` 或 `${ORIGIN}` 开头,规范化后须在 AppDir 内),否则拒绝(如未修正的 cairo):CI 用的是别处建好的引擎,旧构建会在这里报错而不是带进包里。替换的 Linux 包对应的源码随之上传到同一发布(`source.sh`,文件名带构建提交号),发布说明写明 Linux 包出自哪个提交。
+4 -1
View File
@@ -11,7 +11,10 @@ image=ely-office-linux-build
docker build -q -t "$image" "$root/packaging/linux" >/dev/null
# Own target and Cargo home: host builds need newer glibc.
mkdir -p "$root/target/ubuntu" "$root/target/ubuntu-cargo" "$root/target/ubuntu-home"
docker run --rm --user "$(id -u):$(id -g)" -v "$root:$root" -w "$root" \
# Engines shared from another clone resolve inside too.
share=()
[ -z "${ELY_SHARE:-}" ] || share=(-v "$ELY_SHARE:$ELY_SHARE")
docker run --rm --user "$(id -u):$(id -g)" -v "$root:$root" ${share[@]+"${share[@]}"} -w "$root" \
-e HOME="$root/target/ubuntu-home" -e CARGO_HOME="$root/target/ubuntu-cargo" \
-e CARGO_TARGET_DIR="$root/target/ubuntu" -e ELY_RELEASE_TOKEN \
"$image" "$@"
+35
View File
@@ -34,6 +34,10 @@ fi
echo "package.sh: Linux only" >&2
exit 1
}
command -v patchelf >/dev/null || {
echo "package.sh: no patchelf; run in packaging/linux/container.sh" >&2
exit 1
}
arch=$(uname -m)
case $arch in
x86_64)
@@ -131,6 +135,7 @@ broken() {
}
find "$appdir" \( -type f -o -type l \) -name '*.so*' -print0 >"$work/libs.list" || broken "cannot list $appdir"
find "$appdir" -type f -print0 >"$work/files.list" || broken "cannot list $appdir"
real=$(realpath "$appdir")
declare -A bundled=()
while IFS= read -r -d '' file; do
bundled[${file##*/}]=1
@@ -141,6 +146,36 @@ while IFS= read -r -d '' file; do
magic=$(head -c 4 "$file" | od -An -c | tr -d ' ') || broken "cannot read $file"
[ "$magic" = '177ELF' ] || continue
dynamic=$(readelf -dW "$file") || broken "readelf cannot read $file"
# Library paths resolve inside the package.
records=$(awk '/\((RUNPATH|RPATH)\)/ { n++ } END { print n + 0 }' <<<"$dynamic") || broken "cannot count paths of $file"
[ "$records" -le 1 ] || broken "${file#"$appdir"/} has $records library path records"
lines=()
if [ "$records" -eq 1 ]; then
# The raw string; readelf's text can hide a newline.
path=$(patchelf --print-rpath "$file" && printf x) || broken "patchelf cannot read $file"
path=${path%x}
path=${path%$'\n'}
[[ $path != *$'\n'* ]] || broken "${file#"$appdir"/} has a newline in its library path"
lines=("$path")
fi
entries=()
for line in "${lines[@]}"; do
# An empty entry searches the working directory.
[[ $line != :* && $line != *: && $line != *::* && -n $line ]] ||
broken "${file#"$appdir"/} searches the working directory: $line"
IFS=: read -r -a parts <<<"$line"
entries+=("${parts[@]}")
done
for path in "${entries[@]}"; do
case $path in
'$ORIGIN' | '$ORIGIN/'*) rest=${path#'$ORIGIN'} ;;
'${ORIGIN}' | '${ORIGIN}/'*) rest=${path#'${ORIGIN}'} ;;
*) broken "${file#"$appdir"/} loads from $path, not from \$ORIGIN" ;;
esac
target=$(realpath -m "$(dirname "$file")$rest") || broken "cannot resolve $path for $file"
[[ $target == "$real" || $target == "$real"/* ]] ||
broken "${file#"$appdir"/} loads from $path, outside the package"
done
while IFS= read -r lib; do
[ -n "${bundled[$lib]:-}" ] || needed+=("$lib")
done < <(sed -n 's/.*Shared library: \[\(.*\)\]/\1/p' <<<"$dynamic")
+1 -11
View File
@@ -12,17 +12,7 @@ releases=https://git.zacharyzhang.com/ZacharyZhang-NY/ElyOffice/releases
echo "release.sh: notarizing needs ELY_SIGN_IDENTITY, a Developer ID" >&2
exit 1
}
# Our source as published: the committed tree.
source=$dist/ElyOffice-$version-source.tar.gz
mkdir -p "$dist"
git -C "$root" archive --format=tar.gz --prefix="ElyOffice-$version/" -o "$source" HEAD
echo "$source"
# Copyleft upstream as shipped: patched MuPDF (AGPL), Git (GPLv2).
upstream=$dist/ElyOffice-$version-upstream-source.tar.gz
[ -f "$root/engines/mupdf-src/COPYING" ] || { echo "release.sh: no engines/mupdf-src; run the MuPDF build" >&2; exit 1; }
tar -czf "$upstream" --exclude .git --exclude .DS_Store \
-C "$root/engines" mupdf-src -C "$root/reference" git
echo "$upstream"
"$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
+32
View File
@@ -0,0 +1,32 @@
#!/usr/bin/env bash
# Usage: source.sh <dist>; the source of what this commit builds.
set -euo pipefail
root=$(cd "$(dirname "$0")/.." && pwd)
dist=${1:?the folder to write into}
version=$(awk -F'"' '/^version = / { print $2; exit }' "$root/Cargo.toml")
# Binaries match a commit only when the tree is clean.
changed=$(git -C "$root" status --porcelain --untracked-files=all)
[ -z "$changed" ] || {
echo "source.sh: uncommitted changes; commit before a release:" >&2
echo "$changed" >&2
exit 1
}
commit=$(git -C "$root" rev-parse --short=12 HEAD)
mkdir -p "$dist"
# Our source as published: the committed tree.
ours=$dist/ElyOffice-$version-$commit-source.tar.gz
git -C "$root" archive --format=tar.gz --prefix="ElyOffice-$version/" -o "$ours" HEAD
echo "$ours"
# Copyleft upstream as shipped: patched MuPDF (AGPL), Git (GPLv2).
upstream=$dist/ElyOffice-$version-$commit-upstream-source.tar.gz
[ -f "$root/engines/mupdf-src/COPYING" ] || {
echo "source.sh: no engines/mupdf-src; run scripts/build-mupdf.sh" >&2
exit 1
}
[ -f "$root/reference/git/COPYING" ] || {
echo "source.sh: no reference/git; run scripts/fetch-references.sh" >&2
exit 1
}
tar -czf "$upstream" --exclude .git --exclude .DS_Store -C "$root/engines" mupdf-src -C "$root/reference" git
echo "$upstream"
+14 -8
View File
@@ -13,6 +13,9 @@ version=$(awk -F'"' '/^version = / { print $2; exit }' "$root/Cargo.toml")
: "${ELY_RELEASE_TOKEN:?the Gitea token that uploads}"
[ -z "${ELY_PATH:-}" ] || PATH=$ELY_PATH:$PATH
cd "$root"
# Linux builds and packages in the Ubuntu 26.04 container.
inside=()
[ "$os" != linux ] || inside=(packaging/linux/container.sh)
# Engines another clone on this host built; else built here.
if [ -n "${ELY_SHARE:-}" ]; then
@@ -26,13 +29,13 @@ if [ -n "${ELY_SHARE:-}" ]; then
done
else
scripts/fetch-references.sh
scripts/build-mupdf.sh
scripts/fetch-cef.sh
scripts/build-git.sh
scripts/fetch-git-lfs.sh
scripts/fetch-tessdata.sh
scripts/build-mark-content.sh
scripts/build-libreoffice.sh
${inside[@]+"${inside[@]}"} scripts/build-mupdf.sh
${inside[@]+"${inside[@]}"} scripts/fetch-cef.sh
${inside[@]+"${inside[@]}"} scripts/build-git.sh
${inside[@]+"${inside[@]}"} scripts/fetch-git-lfs.sh
${inside[@]+"${inside[@]}"} scripts/fetch-tessdata.sh
${inside[@]+"${inside[@]}"} scripts/build-mark-content.sh
${inside[@]+"${inside[@]}"} scripts/build-libreoffice.sh
fi
dist=$root/dist
@@ -41,13 +44,16 @@ 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 ;;
linux) for slug in $batch; do packaging/linux/package.sh "$slug"; done ;;
linux) for slug in $batch; do ${inside[@]+"${inside[@]}"} packaging/linux/package.sh "$slug"; done ;;
*)
echo "ci.sh: no release for $os" >&2
exit 1
;;
esac
# macOS's release.sh writes its own source archives.
[ "$os" = macos ] || packaging/source.sh "$dist"
# The tag's release, made by whichever platform comes first.
api=https://git.zacharyzhang.com/api/v1/repos/ZacharyZhang-NY/ElyOffice
auth="Authorization: token $ELY_RELEASE_TOKEN"
+2 -1
View File
@@ -207,4 +207,5 @@
- [x] L1.4 Linux 构建环境:`ubuntu:26.04` 容器里构建全部引擎与六个应用(glibc 不高于 2.43);`packaging/linux/container.sh`、`tests/scripts/linux-glibc.sh` — R1 读不出当作静态、缺件照过、注释超长;R2 链接文件、读错;R3 链接目录;R4 PASS
- [x] L1.5 Linux 包:deb、rpm、Arch(pkg.tar.zst)、AppImage 出自同一份分装,装到 `/opt/ely-office/<应用>`,带桌面项、图标与文件类型;依赖按 `packaging/linux/depends.tsv`;LibreOffice 的 cairo RUNPATH 改为 `$ORIGIN`;`tests/scripts/linux-packages.sh` — R1 列举与读错被吞、坏入口照过、无执行位;R2 坏 git-lfs 照过;R3 PASS
- [x] L1.6 本机出六个应用的四种包;Omarchy 上装 Arch 包与 AppImage 出窗口;deb、rpm 在各自发行版的容器里装上并冒烟;`linux-installed.sh`、`linux-distros.sh`、`linux-mark-idle.sh` — R1 deb/rpm 未运行、外部改动未核对显示;R2 内核未出图也过、OCR 大小写、清理竞态;R3 公式栏冒充网格、OCR 与 pgrep 错误被吞;R4 取色、存活、截图错误被吞;R5 hyprctl 查询失败被当作未出窗口(第五轮后修正并以注入失败核对)
- [ ] L1.7 v0.1.3:删去旧 AppImage,上传新包;`ci.sh` 的 Linux 产出四种包;agents.md 与官网随之
- [x] L1.7 v0.1.3:删去旧 AppImage,上传新包;`ci.sh` 的 Linux 产出四种包;agents.md 与官网随之(官网只链到发布页,无需改);`package.sh` 拒绝指向包外的 RUNPATH;Linux CI 进容器;`packaging/source.sh` 随发布上传本提交的源码 — R1 前缀检查可越出、读错被吞、CI 未进容器、无对应源码;R2 空项、拆分错误、未跟踪文件;R3 换行、git 配置;R4 `]` 加换行;R5 末尾换行(第五轮后修正并以编译的 ELF 核对)
- [ ] L1.8 macOS 发布:CI 从零编 LibreOffice 超过 act_runner 的 3 小时被杀(v0.1.3 无 dmg);Mac 任务以 `ELY_SHARE` 借用 Mac mini 开发克隆 `ElyOfficeSuite` 的引擎,CI 克隆里自建的残留引擎即报错而非混用
+9
View File
@@ -0,0 +1,9 @@
FAIL
1. packaging/linux/package.sh:146 — The RUNPATH guard checks only a string prefix, so paths beginning with `$ORIGIN` can still escape the AppDir. In a copied tree, an ELF with `$ORIGIN/../../../../../../../../outside` passed packaging and produced all four formats. Its library loaded successfully while the external build-tree dependency existed, then failed with `libleaf.so: cannot open shared object file` when that dependency was removed. `$ORIGINsuffix` also passed. Resolve recognized origin tokens relative to each ELF, normalize the resulting paths, and reject destinations outside the packaged runtime tree. Add regression cases for traversal and malformed tokens. Evidence: `.work/L1.7-r1/guard-probes.log`.
2. packaging/linux/package.sh:147 — RUNPATH extraction errors do not propagate out of the process substitution. Making the guard's `sed` exit 73 caused a library with RUNPATH `/old/lo/workdir/fontconfig` to bypass validation; the copied packager exited 0 and produced all four formats. Check the extraction pipeline's status before consuming its output, and add a regression that requires packaging to fail when extraction fails. Evidence: `.work/L1.7-r1/guard-errors.log`.
3. scripts/ci.sh:44 — Linux CI bypasses the required Ubuntu 26.04 container. The branch calls the packager directly, whose staging step invokes Cargo in the caller's environment; the engine-build branch likewise runs directly. An isolated CI invocation with shared engines ran all six packagers with `container=no` and reported success. This contradicts the container requirement in `tasks/TASKS.md:202` and `agents.md:307`; the new same-glibc statement does not enforce that requirement. Route Linux builds and packaging through `packaging/linux/container.sh`, preserve access to shared engines, and test that CI actually enters it. Evidence: `.work/L1.7-r1/ci-probe.log`.
4. agents.md:310 — The replacement Linux binaries have no matching published source entry, violating PRD §§21.3 and 24.2. The [v0.1.3 release](https://git.zacharyzhang.com/api/v1/repos/ZacharyZhang-NY/ElyOffice/releases/tags/v0.1.3) has an empty description and only binary assets; its source archives still reference commit `62f9839170efe66f80be74975c65de53b9876438`. That version's [Cargo.toml](https://git.zacharyzhang.com/api/v1/repos/ZacharyZhang-NY/ElyOffice/raw/Cargo.toml?ref=v0.1.3) enables only `font-kit`, without the Wayland/X11 fix, and its new packaging script returns 404. The local L1.6 commit also returns 404 from the public commit API. A recipient following the release's source link therefore receives the old headless implementation and cannot reproduce the replacement packages. Publish an immutable corresponding-source snapshot, including the fixes and build materials, and link it from the release. Evidence: `.work/L1.7-r1/release.log` and `.work/L1.7-r1/source-release.log`.
+7
View File
@@ -0,0 +1,7 @@
FAIL
1. packaging/linux/package.sh:156 — Dropping empty RUNPATH components hides searches outside the AppDir: leading or trailing `:` means the current working directory to the loader. In the compiled ELF probe, both `:$ORIGIN/missing` and `$ORIGIN/missing:` passed packaging and produced deb, rpm, and Arch packages. Loading returned 42 with `libleaf.so` in the working directory, then failed when that external library was removed. Preserve empty components and reject current-directory searches; add leading/trailing-colon regression cases. Evidence: `.work/L1.7-r2/guard.log`.
2. packaging/linux/package.sh:156 — The original extraction error now propagates, but failures in the splitting process substitution still bypass validation. Making its `sed` exit 74 or its `tr` exit 75 let an ELF with an absolute external RUNPATH pass; the copied packager exited 0 and produced all three native packages. Capture and check the complete splitting pipeline before consuming its output, and add failure-injection regressions for both commands. Evidence: `.work/L1.7-r2/guard.log`.
3. packaging/source.sh:9 — `--untracked-files=no` lets uncommitted build inputs pass the clean-tree check, so the commit-labelled archive can differ from the binary, contrary to PRD §§21.3 and 24.2. In a Rust probe checkout using this script, an untracked, automatically discovered `build.rs` changed the built program's output to `untracked-build-input`; `source.sh` succeeded but omitted that file, and rebuilding its archive printed `committed`. Reject untracked build inputs or build from the same committed snapshot that is archived; add a regression covering this mismatch. Evidence: `.work/L1.7-r2/source.log`.
+5
View File
@@ -0,0 +1,5 @@
FAIL
1. packaging/linux/package.sh:146 — A RUNPATH containing a literal newline is silently treated as absent: readelf prints its value across lines, so the line-based sed extracts nothing and validation skips it. In Ubuntu 26.04, a compiled ELF with `$ORIGIN/missing\n:/outside` passed the unmodified guard and produced deb, rpm and Arch packages; the ELF extracted from its deb returned 42 with the external library present, then failed to load when that library was removed. Reject present but unparsed RUNPATH records, and parse or reject multiline values before validating every component; add a compiled-ELF regression. Evidence: `.work/L1.7-r3/guard.log`, `.work/L1.7-r3/ubuntu-newline.log`, `.work/L1.7-r3/native-newline.log`.
2. packaging/source.sh:9 — The clean-tree check inherits `status.showUntrackedFiles`, so round 2's untracked-build-input defect persists when that Git setting is `no`. In the Rust probe, an untracked, automatically discovered build.rs changed the binary output to `untracked-build-input`; source.sh exited 0 and omitted it, and rebuilding the commit-labelled archive printed `committed`, violating PRD §§21.3 and 24.2. Explicitly enumerate untracked files regardless of Git settings, and add a regression with `status.showUntrackedFiles=no`. Evidence: `.work/L1.7-r3/source.log`.
+3
View File
@@ -0,0 +1,3 @@
FAIL
1. packaging/linux/package.sh:146 — The newline fix still truncates RUNPATH when a literal `]` precedes the newline. With `$ORIGIN]\n:/outside`, sed extracts only `$ORIGIN`, and the record-count check accepts it, skipping the external directory. In Ubuntu 26.04, the staged packager with real linuxdeploy, appimagetool and nfpm produced all four formats; the library extracted from its deb returned 42 with the external dependency present and failed to load after it was removed. Parse complete ELF dynamic strings or reliably reject multiline values before validating components; add a compiled-ELF regression covering this case. Evidence: `.work/L1.7-r4/guard.log`, `.work/L1.7-r4/ubuntu-bracket-newline.log`, `.work/L1.7-r4/native-newline.log`.
+3
View File
@@ -0,0 +1,3 @@
FAIL
1. packaging/linux/package.sh:155 — Command substitution strips trailing newlines from `patchelf` output before line 156 checks it. A compiled ELF with RUNPATH `$ORIGIN\n` (literal terminal LF) passes as `$ORIGIN`; the unmodified staged packager in Ubuntu 26.04 exits 0 and produces all four formats. Its library extracted from the deb cannot load the adjacent bundled `libleaf.so`: it returns 42 only after supplying an unpackaged `program\n` directory, and fails again when that directory is removed. Preserve trailing newlines before validation and add a compiled-ELF regression. Evidence: `.work/L1.7-r5/guard.log`, `.work/L1.7-r5/guard-followup.log`.
+3 -2
View File
@@ -22,7 +22,8 @@ while IFS=$'\t' read -r slug name; do
[ -d "$dist/$name.app" ] || fail "no $dist/$name.app; run packaging/macos/release.sh"
done <<<"$apps"
version=$(awk -F'"' '/^version = / { print $2; exit }' "$root/Cargo.toml")
source=$dist/ElyOffice-$version-source.tar.gz
commit=$(git -C "$root" rev-parse --short=12 HEAD)
source=$dist/ElyOffice-$version-$commit-source.tar.gz
[ -s "$source" ] || fail "no $source; run packaging/macos/release.sh"
listed=$(tar -tzf "$source")
for part in LICENSE Cargo.lock patches/libreoffice/ patches/mupdf/ scripts/fetch-references.sh scripts/build-git.sh; do
@@ -32,7 +33,7 @@ for part in agents.md PRD.md tasks/; do
! grep -qx "ElyOffice-$version/$part" <<<"$listed" || fail "the source archive has the internal $part"
done
# Copyleft upstream as built: MuPDF with our patches, and Git.
upstream=$dist/ElyOffice-$version-upstream-source.tar.gz
upstream=$dist/ElyOffice-$version-$commit-upstream-source.tar.gz
[ -s "$upstream" ] || fail "no $upstream; run packaging/macos/release.sh"
for part in mupdf-src/COPYING git/COPYING git/GIT-VERSION-GEN; do
tar -tzf "$upstream" "$part" >/dev/null 2>&1 || fail "the upstream archive lacks $part"