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

+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"