mirror of
https://github.com/InstanceBrick/nightcap.git
synced 2026-10-17 21:00:47 +02:00
268 lines
11 KiB
Bash
Executable File
268 lines
11 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Modified by vii from komaruworld/mocktail. See README "About this fork".
|
|
# Copyright 2026 Mocktail Project Authors
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
set -Eeuo pipefail
|
|
umask 022
|
|
|
|
ANYLINUX_PROJECT_ROOT="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
ANYLINUX_BUILD_DIR="${ANYLINUX_PROJECT_ROOT}/build"
|
|
ANYLINUX_OUTPUT=""
|
|
ANYLINUX_WORK=""
|
|
ANYLINUX_FREEBSD_HELPER=""
|
|
ANYLINUX_SYSTEM_INSTALL=false
|
|
ANYLINUX_DEPLOY_INSTALLED=false
|
|
ANYLINUX_TOOL="${MOCKTAIL_ANYLINUX_PACKAGER:-quick-sharun}"
|
|
ANYLINUX_APPIMAGETOOL="${MOCKTAIL_ANYLINUX_APPIMAGETOOL:-appimagetool}"
|
|
|
|
AnyLinuxDie() {
|
|
printf '[anylinux] error: %s\n' "$*" >&2
|
|
exit 1
|
|
}
|
|
|
|
AnyLinuxCleanup() {
|
|
[[ -z "${ANYLINUX_WORK}" ]] || rm -rf -- "${ANYLINUX_WORK}"
|
|
}
|
|
|
|
AnyLinuxUsage() {
|
|
cat <<'EOF'
|
|
Usage: scripts/package_anylinux.sh --build-dir DIR --appimage FILE [OPTIONS]
|
|
|
|
Install the native application to /usr, let quick-sharun deploy its complete
|
|
runtime, then run quick-sharun --make-appimage. No portable bundle is embedded.
|
|
|
|
By default /usr is a private, read-only bubblewrap overlay of a DESTDIR install;
|
|
the host installation is never modified. Requires an Arch Linux build host.
|
|
--system-install Install directly to /usr in a disposable build container.
|
|
Refused outside Docker/Podman containers. Used by CI.
|
|
--deploy-installed Internal: package the already prepared /usr installation.
|
|
|
|
MOCKTAIL_ANYLINUX_PACKAGER and MOCKTAIL_ANYLINUX_APPIMAGETOOL select the tools.
|
|
MOCKTAIL_ANYLINUX_SHARUN and MOCKTAIL_ANYLINUX_C_SOURCE select verified local
|
|
upstream inputs; quick-sharun itself installs/builds them into the AppDir.
|
|
EOF
|
|
}
|
|
|
|
AnyLinuxParseArguments() {
|
|
while (( $# > 0 )); do
|
|
case "$1" in
|
|
--build-dir|--appimage)
|
|
(( $# >= 2 )) || AnyLinuxDie "$1 requires a path"
|
|
if [[ "$1" == --build-dir ]]; then
|
|
ANYLINUX_BUILD_DIR="$2"
|
|
else
|
|
ANYLINUX_OUTPUT="$2"
|
|
fi
|
|
shift 2
|
|
;;
|
|
--system-install) ANYLINUX_SYSTEM_INSTALL=true; shift ;;
|
|
--deploy-installed) ANYLINUX_DEPLOY_INSTALLED=true; shift ;;
|
|
-h|--help) AnyLinuxUsage; exit 0 ;;
|
|
*) AnyLinuxDie "unknown option: $1" ;;
|
|
esac
|
|
done
|
|
[[ -n "${ANYLINUX_OUTPUT}" ]] || AnyLinuxDie "--appimage is required"
|
|
[[ "${ANYLINUX_OUTPUT}" != *$'\n'* &&
|
|
"${ANYLINUX_OUTPUT}" != *$'\r'* ]] || AnyLinuxDie "unsafe output path"
|
|
[[ ! -L "${ANYLINUX_OUTPUT}" ]] || AnyLinuxDie "refusing symlink output"
|
|
ANYLINUX_OUTPUT="$(realpath -m -- "${ANYLINUX_OUTPUT}")"
|
|
[[ "${ANYLINUX_OUTPUT}" == *.AppImage && ! -d "${ANYLINUX_OUTPUT}" ]] ||
|
|
AnyLinuxDie "output must be an AppImage file"
|
|
}
|
|
|
|
AnyLinuxVerifyInstalled() {
|
|
local entry
|
|
for entry in /usr/bin/mocktail \
|
|
/usr/lib/mocktail/mocktail_updater \
|
|
/usr/lib/mocktail/mocktail_failure_dialog \
|
|
/usr/lib/mocktail/mocktail_webview_helper; do
|
|
[[ -x "${entry}" ]] || AnyLinuxDie "missing installed executable: ${entry}"
|
|
done
|
|
for entry in libEGL.so libvulkan.so libmediandk.so; do
|
|
[[ -r "/usr/lib/mocktail/${entry}" ]] ||
|
|
AnyLinuxDie "missing installed Android adapter: ${entry}"
|
|
done
|
|
for entry in roblox_compatibility.json roblox_bootstrap_sources.json \
|
|
roblox_host_abi_reference.json roblox_signing_certificates.json; do
|
|
[[ -r "/usr/share/mocktail/metadata/${entry}" ]] ||
|
|
AnyLinuxDie "missing installed metadata: ${entry}"
|
|
done
|
|
LC_ALL=C readelf -l /usr/bin/mocktail | grep -q 'ld-linux.*so' ||
|
|
AnyLinuxDie "AnyLinux requires a glibc runtime"
|
|
if [[ -f /usr/share/mocktail/helpers/mocktail_freebsd_socket_helper ]]; then
|
|
ANYLINUX_FREEBSD_HELPER=/usr/share/mocktail/helpers/mocktail_freebsd_socket_helper
|
|
fi
|
|
}
|
|
|
|
AnyLinuxVerifyAppDir() {
|
|
local app_dir="$1" entry
|
|
[[ ! -e "${app_dir}/usr" ]] || AnyLinuxDie "unexpected usr prefix in AppDir"
|
|
[[ ! -e "${app_dir}/share/mocktail-bundle" ]] ||
|
|
AnyLinuxDie "a portable bundle was embedded in AppDir"
|
|
for entry in mocktail mocktail_updater mocktail_failure_dialog \
|
|
mocktail_webview_helper; do
|
|
[[ -x "${app_dir}/bin/${entry}" ]] ||
|
|
AnyLinuxDie "quick-sharun did not deploy ${entry}"
|
|
done
|
|
for entry in WebKitNetworkProcess WebKitWebProcess WebKitGPUProcess; do
|
|
[[ -x "${app_dir}/lib/webkitgtk-6.0/${entry}" ]] ||
|
|
AnyLinuxDie "quick-sharun did not preserve the WebKit process layout: ${entry}"
|
|
done
|
|
[[ -r "${app_dir}/lib/webkitgtk-6.0/injected-bundle/libwebkitgtkinjectedbundle.so" ]] ||
|
|
AnyLinuxDie "missing injected WebKit bundle"
|
|
[[ -f "${app_dir}/AppRun.sh" && -f "${app_dir}/AppRun.lib" ]] ||
|
|
AnyLinuxDie "missing quick-sharun startup hooks"
|
|
for entry in libEGL.so libvulkan.so libmediandk.so; do
|
|
[[ -r "${app_dir}/lib/mocktail/${entry}" ]] ||
|
|
AnyLinuxDie "Android adapter was not deployed in its private directory: ${entry}"
|
|
done
|
|
for entry in roblox_compatibility.json roblox_bootstrap_sources.json \
|
|
roblox_host_abi_reference.json roblox_signing_certificates.json; do
|
|
[[ -r "${app_dir}/share/mocktail/metadata/${entry}" ]] ||
|
|
AnyLinuxDie "quick-sharun did not deploy metadata: ${entry}"
|
|
done
|
|
if [[ -n "${ANYLINUX_FREEBSD_HELPER}" ]]; then
|
|
cmp -s -- "${ANYLINUX_FREEBSD_HELPER}" \
|
|
"${app_dir}/share/mocktail/helpers/mocktail_freebsd_socket_helper" ||
|
|
AnyLinuxDie "the static FreeBSD helper was changed or omitted"
|
|
fi
|
|
}
|
|
|
|
AnyLinuxPrepareInstallTree() {
|
|
local prefix="$1"
|
|
# This static FreeBSD executable must not be turned into a Linux sharun
|
|
# wrapper. Keep it as application data, outside the nested lib executables
|
|
# that quick-sharun wraps. The native runtime uses its explicit .env path.
|
|
local helper="${prefix}/lib/mocktail/mocktail_freebsd_socket_helper"
|
|
if [[ -f "${helper}" ]]; then
|
|
install -d -- "${prefix}/share/mocktail/helpers"
|
|
mv -- "${helper}" "${prefix}/share/mocktail/helpers/"
|
|
fi
|
|
}
|
|
|
|
AnyLinuxDeploy() {
|
|
AnyLinuxVerifyInstalled
|
|
local app_dir="${ANYLINUX_WORK}/AppDir"
|
|
mkdir -p -- "${app_dir}" "${ANYLINUX_WORK}/tmp"
|
|
# .env is runtime configuration, not a custom AppRun. The generated
|
|
# upstream entry point must execute all relocation/certificate hooks.
|
|
install -m 0644 -- "${ANYLINUX_PROJECT_ROOT}/packaging/anylinux.env" \
|
|
"${app_dir}/.env"
|
|
|
|
local -a deployment_environment=(
|
|
"APPDIR=${app_dir}" "MAIN_BIN=mocktail" "LIB_DIR=/usr/lib"
|
|
"TMPDIR=${ANYLINUX_WORK}/tmp"
|
|
"DESKTOP=/usr/share/applications/io.github.CoderDayton.nightcap.desktop"
|
|
"ICON=/usr/share/icons/hicolor/scalable/apps/io.github.CoderDayton.nightcap.svg"
|
|
"DEPLOY_DATADIR=0" "DEPLOY_LOCALE=0" "DEPLOY_VULKAN=0"
|
|
"STRACE_MODE=1" "STRACE_BINARY=mocktail" "STRACE_FLAGS=--help"
|
|
"STRACE_TIME=1"
|
|
)
|
|
if [[ -n "${MOCKTAIL_ANYLINUX_SHARUN:-}" ]]; then
|
|
[[ -x "${MOCKTAIL_ANYLINUX_SHARUN}" ]] || AnyLinuxDie "invalid sharun input"
|
|
deployment_environment+=("SHARUN_LINK=file://$(realpath -e -- "${MOCKTAIL_ANYLINUX_SHARUN}")")
|
|
fi
|
|
if [[ -n "${MOCKTAIL_ANYLINUX_C_SOURCE:-}" ]]; then
|
|
[[ -r "${MOCKTAIL_ANYLINUX_C_SOURCE}" ]] || AnyLinuxDie "invalid anylinux.c input"
|
|
deployment_environment+=("ANYLINUX_LIB_SOURCE=file://$(realpath -e -- "${MOCKTAIL_ANYLINUX_C_SOURCE}")")
|
|
fi
|
|
# Upstream prefers wget, which cannot fetch file:// inputs. Adapt only
|
|
# those local tool downloads; all network downloads still use real wget.
|
|
if [[ -n "${MOCKTAIL_ANYLINUX_SHARUN:-}${MOCKTAIL_ANYLINUX_C_SOURCE:-}" ]] &&
|
|
command -v wget >/dev/null; then
|
|
local downloader_dir="${ANYLINUX_WORK}/download-tools"
|
|
mkdir -p -- "${downloader_dir}"
|
|
cat >"${downloader_dir}/wget" <<'EOF'
|
|
#!/usr/bin/env bash
|
|
set -Eeuo pipefail
|
|
if [[ "$#" == 3 && "$1" == -qO && "$3" == file://* ]]; then
|
|
cp -- "${3#file://}" "$2"
|
|
else
|
|
exec "${MOCKTAIL_ANYLINUX_REAL_WGET}" "$@"
|
|
fi
|
|
EOF
|
|
chmod 0755 -- "${downloader_dir}/wget"
|
|
deployment_environment+=(
|
|
"MOCKTAIL_ANYLINUX_REAL_WGET=$(command -v wget)"
|
|
"PATH=${downloader_dir}:${PATH}"
|
|
)
|
|
fi
|
|
|
|
printf '[anylinux] deploying the native /usr installation\n' >&2
|
|
env "${deployment_environment[@]}" "${ANYLINUX_TOOL}" \
|
|
/usr/bin/mocktail \
|
|
/usr/lib/mocktail/mocktail_webview_helper \
|
|
/usr/lib/mocktail/mocktail_failure_dialog \
|
|
/usr/lib/mocktail/mocktail_updater \
|
|
/usr/lib/mocktail /usr/share/mocktail /usr/bin/bash
|
|
AnyLinuxVerifyAppDir "${app_dir}"
|
|
|
|
# Use the upstream creation entry point, rather than maintaining a second
|
|
# appimagetool invocation and a second copy of the userspace libraries.
|
|
env "${deployment_environment[@]}" \
|
|
APPIMAGETOOL="${ANYLINUX_APPIMAGETOOL}" \
|
|
OUTPATH="${ANYLINUX_WORK}/image" \
|
|
OUTNAME="$(basename -- "${ANYLINUX_OUTPUT}")" \
|
|
"${ANYLINUX_TOOL}" --make-appimage
|
|
local result="${ANYLINUX_WORK}/image/$(basename -- "${ANYLINUX_OUTPUT}")"
|
|
[[ -x "${result}" ]] || AnyLinuxDie "quick-sharun did not create the requested AppImage"
|
|
if [[ -n "${UPINFO:-}" ]]; then
|
|
[[ -s "${result}.zsync" ]] || AnyLinuxDie "update information was set but no .zsync was generated"
|
|
fi
|
|
mv -f -- "${result}" "${ANYLINUX_OUTPUT}"
|
|
if [[ -f "${result}.zsync" ]]; then
|
|
mv -fT -- "${result}.zsync" "${ANYLINUX_OUTPUT}.zsync"
|
|
fi
|
|
printf '[anylinux] AppImage ready: %s\n' "${ANYLINUX_OUTPUT}" >&2
|
|
}
|
|
|
|
AnyLinuxMain() {
|
|
AnyLinuxParseArguments "$@"
|
|
local command_name
|
|
for command_name in cmake readelf realpath "${ANYLINUX_TOOL}" "${ANYLINUX_APPIMAGETOOL}"; do
|
|
command -v "${command_name}" >/dev/null ||
|
|
AnyLinuxDie "missing required command: ${command_name}"
|
|
done
|
|
ANYLINUX_TOOL="$(realpath -e -- "$(command -v "${ANYLINUX_TOOL}")")"
|
|
ANYLINUX_APPIMAGETOOL="$(realpath -e -- "$(command -v "${ANYLINUX_APPIMAGETOOL}")")"
|
|
export MOCKTAIL_ANYLINUX_PACKAGER="${ANYLINUX_TOOL}"
|
|
export MOCKTAIL_ANYLINUX_APPIMAGETOOL="${ANYLINUX_APPIMAGETOOL}"
|
|
mkdir -p -- "$(dirname -- "${ANYLINUX_OUTPUT}")"
|
|
ANYLINUX_WORK="$(mktemp -d "$(dirname -- "${ANYLINUX_OUTPUT}")/.mocktail-anylinux.XXXXXX")"
|
|
trap AnyLinuxCleanup EXIT
|
|
if [[ "${ANYLINUX_DEPLOY_INSTALLED}" == true ]]; then
|
|
AnyLinuxDeploy
|
|
return
|
|
fi
|
|
|
|
ANYLINUX_BUILD_DIR="$(realpath -e -- "${ANYLINUX_BUILD_DIR}")"
|
|
[[ -f "${ANYLINUX_BUILD_DIR}/cmake_install.cmake" ]] ||
|
|
AnyLinuxDie "build directory has no CMake installation rules"
|
|
local install_root="${ANYLINUX_WORK}/install"
|
|
if [[ "${ANYLINUX_SYSTEM_INSTALL}" == true ]]; then
|
|
[[ -f /.dockerenv || -f /run/.containerenv ]] ||
|
|
AnyLinuxDie "--system-install is only allowed in a disposable container"
|
|
[[ "$(id -u)" == 0 ]] || AnyLinuxDie "container installation requires root"
|
|
env -u DESTDIR cmake --install "${ANYLINUX_BUILD_DIR}" --prefix /usr
|
|
AnyLinuxPrepareInstallTree /usr
|
|
AnyLinuxDeploy
|
|
return
|
|
fi
|
|
|
|
command -v bwrap >/dev/null || AnyLinuxDie "bwrap is required for a private /usr install"
|
|
DESTDIR="${install_root}" cmake --install "${ANYLINUX_BUILD_DIR}" --prefix /usr
|
|
AnyLinuxPrepareInstallTree "${install_root}/usr"
|
|
# Overlay only /usr; keep the host root read-only, with narrowly scoped
|
|
# writable packaging scratch/output directories. No host package install.
|
|
bwrap --die-with-parent --ro-bind / / --dev-bind /dev /dev --proc /proc \
|
|
--overlay-src /usr --overlay-src "${install_root}/usr" --ro-overlay /usr \
|
|
--bind "$(dirname -- "${ANYLINUX_OUTPUT}")" "$(dirname -- "${ANYLINUX_OUTPUT}")" \
|
|
--bind /tmp /tmp \
|
|
-- "${BASH}" "${BASH_SOURCE[0]}" --deploy-installed --appimage "${ANYLINUX_OUTPUT}"
|
|
}
|
|
|
|
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
|
|
AnyLinuxMain "$@"
|
|
fi
|