跳到內容
Syno Package Land
語言: 繁體中文

封裝指令碼與直譯式語言

synopkgland 不會只用指令碼建置 SPK。native layout 要求每個架構至少有一個 ELF 執行檔,也沒有 noarch 選項;native-go 與 binary 套件執行的是 Go 或靜態 ELF CGI。所以用指令碼語言寫的程式需要下列其中一種做法:

做法SPK 裡放什麼架構大小(實測)
A. 編譯成單一執行檔一個內含 runtime 與程式碼的 ELFBun 與 Deno:x86_64、armv8。沒有 armv7。執行檔 Bun 78 MiB、Deno 103 MiB;SPK 約 36 MiB
B. 附帶直譯器直譯器執行檔加上你的原始碼Node.js:x86_64、armv8、armv7node 102–119 MiB;SPK 38–43 MiB
C. 在容器裡執行一個 Compose 檔;安裝時才拉取 image支援 Container Manager 的機型:x86_64、aarch64SPK 約 80 KB,另加 image

大小是以 Bun 1.4.2、Deno 2.9.4 與 Node.js 22.23.3 建置一行程式的結果。native layout 允許每個檔案 256 MiB、整個 payload 1 GiB,所以上述執行檔都放得下。

不論用哪種做法,安裝前都先用 synopkgland inspect <spk> 檢查。

問題ABC
NAS 需要 Container Manager否否是;容器 App 需要 DSM 7.2.1-69057 以上
NAS 的 glibc 版本要夠新是是否
能在 armv7 執行否(Bun、Deno)是(Node.js)否
SPK 內可直接讀到原始碼否(打包進執行檔)是只在 image 裡
安裝時可離線是是否,NAS 要拉取 image
升級 runtime重新建置改 toolkit 的釘選釘選新的 image digest

這裡的三種執行檔都動態連結 glibc,所以宣告 linkage: dynamic。各自需要的最新 glibc 符號版本是 Bun 2.17、Deno 2.27、Node.js 22 為 2.28。請確認目標 NAS 的 glibc 版本,例如透過 SSH 執行 ldd --version。本指南沒有列出哪些 DSM 機型符合各個版本。

套件中心有 Synology 提供的 Node.js 與 Python 套件。SPK 可以用 INFO 欄位 install_dep_packages 依賴它們。synopkgland 不支援這個做法:native layout 自己寫 INFO,沒有對應欄位;inspect 會回報所有套件依賴,只有容器 App 依賴 Web Station 或 Container Manager 例外。如果需要這種依賴,請手動打包 SPK(見不需建置的 shell 指令碼),並在 INFO 加上 install_dep_packages。inspect 對非它建置的 SPK 接受這個欄位,但不會檢查被依賴的套件是否存在。

bun build --compile 與 deno compile 會把 TypeScript 或 JavaScript 編譯成一個 Linux 執行檔,也能在 macOS 或 Linux 上交叉編譯。兩者都沒有 32 位元 ARM 目標。PyInstaller 對 Python 做同樣的事,但不能交叉編譯,請在每種目標架構的 Linux 機器上執行。

專案結構:

dsm-package.yaml
icon.png square PNG, 256×256 or larger
src/main.ts
package/compile.sh
spk/conf/privilege
spk/scripts/start-stop-status
spk/scripts/preinst … the other six lifecycle scripts

dsm-package.yaml:

schema: 2
backend: {kind: native}
package: HelloBun
version: 1.0.0
build: 1
displayname: Hello Bun
description: A TypeScript worker compiled with bun build --compile
maintainer: Example
os_min_ver: 7.0-40000
arches: [x86_64, armv8]
icon: icon.png
layout:
payload: target/payload/{arch}
metadata: spk
binaries:
- {path: bin/hello, linkage: dynamic}
native:
hook: {command: [sh, package/compile.sh]}

64 位元 ARM 請用 armv8:native layout 會把它原樣寫進 INFO arch,它在 Developer Guide 的 arch 對照表中代表整個系列。aarch64 也可以用,會寫入 native-go 使用的平台 armada37xx rtd1296 rtd1619 rtd1619b。

package/compile.sh 對每個架構執行一次:

#!/bin/sh
set -eu
case "$SYNOPKGLAND_ARCH" in
x86_64) target=bun-linux-x64 ;;
armv8) target=bun-linux-arm64 ;;
*) echo "bun has no target for $SYNOPKGLAND_ARCH" >&2; exit 1 ;;
esac
out="target/payload/$SYNOPKGLAND_ARCH/bin"
rm -rf "$out" && mkdir -p "$out"
bun build --compile --target="$target" src/main.ts --outfile "$out/hello"

若用 Deno,把最後一行換成 deno compile --target x86_64-unknown-linux-gnu -o "$out/hello" src/main.ts;armv8 則用 aarch64-unknown-linux-gnu。

spk/scripts/start-stop-status 以套件帳號啟動程式,並把 PID 存在套件的 var 資料夾:

#!/bin/sh
PID="$SYNOPKG_PKGVAR/hello.pid"
case "$1" in
start) "$SYNOPKG_PKGDEST/bin/hello" >>"$SYNOPKG_PKGVAR/hello.log" 2>&1 &
echo $! >"$PID" ;;
stop) [ -f "$PID" ] && kill "$(cat "$PID")"; rm -f "$PID" ;;
status) [ -f "$PID" ] && kill -0 "$(cat "$PID")" 2>/dev/null ;;
esac

建立其他生命週期指令碼與權限檔,然後建置:

Terminal window
for hook in preinst postinst preuninst postuninst preupgrade postupgrade; do
printf '#!/bin/sh\nexit 0\n' > "spk/scripts/$hook"
done
chmod 755 spk/scripts/*
echo '{"defaults":{"run-as":"package"}}' > spk/conf/privilege
synopkgland check
synopkgland build
synopkgland inspect dist/HelloBun-1.0.0-0001-x86_64.spk

metadata 資料夾裡的指令碼必須有執行權限:native layout 會原樣複製它們的權限。

把直譯器放進 payload,再用它啟動你的原始碼。沿用做法 A 的 spk 資料夾,把 start-stop-status 的啟動那一行改成:

Terminal window
start) "$SYNOPKG_PKGDEST/bin/node" "$SYNOPKG_PKGDEST/app/main.mjs" >>"$SYNOPKG_PKGVAR/hello.log" 2>&1 &

native.toolkits 會為每個架構下載 Node.js 官方建置並檢查 SHA-256,所以建置結果可重現:

schema: 2
backend: {kind: native}
package: HelloNode
version: 1.0.0
build: 1
displayname: Hello Node
description: A Node.js worker with its own Node.js runtime
maintainer: Example
os_min_ver: 7.0-40000
arches: [x86_64, armv8, armv7]
icon: icon.png
layout:
payload: target/payload/{arch}
metadata: spk
binaries:
- {path: bin/node, linkage: dynamic}
native:
hook: {command: [sh, package/collect.sh]}
toolkits:
x86_64:
- {url: 'https://nodejs.org/dist/v22.23.3/node-v22.23.3-linux-x64.tar.xz', sha256: df450af89261115ef9f9e3830c3eeb2cc9213b63c720b1af623cb5dcbe2e02de}
armv8:
- {url: 'https://nodejs.org/dist/v22.23.3/node-v22.23.3-linux-arm64.tar.xz', sha256: a44aeb94849a299b22df10b9e622ec2f605c2183501bc40590705131de7c740f}
armv7:
- {url: 'https://nodejs.org/dist/v22.23.3/node-v22.23.3-linux-armv7l.tar.xz', sha256: 590a2768199bdd0b848648a81cd7f07f88a5ceb27949f0c70d5e6652c3eaca69}

digest 取自該版本的 SHASUMS256.txt。package/collect.sh 把 runtime 與原始碼複製進 payload:

#!/bin/sh
set -eu
out="target/payload/$SYNOPKGLAND_ARCH"
rm -rf "$out" && mkdir -p "$out/bin"
cp "$SYNOPKGLAND_TOOLKIT_DIR"/node-v*/bin/node "$out/bin/node"
cp -R app "$out/app"

這裡只複製 node 執行檔,對沒有 npm 套件的程式已經足夠。有相依套件時,建置前先在 app 執行 npm ci --omit=dev,並一起複製 node_modules。含原生 addon 的相依套件必須為每個架構分別建置。

注意說明
封存格式native.toolkits 只解開 .tar.xz,並使用 GNU tar。在 macOS 上,請把 GNU tar(gtar)以 tar 的名稱放在 PATH 最前面。
Pythonpython-build-standalone 這類可攜式 Python 建置提供 .tar.gz 或 .tar.zst,不是 .tar.xz。請重新封裝成 .tar.xz 並釘選你的副本,或在 native.prepare 取得直譯器。本指南沒有建置過 Python 範例。
大小單是 node 就有 102–119 MiB。每個 payload 檔案都要小於 256 MiB。

在有 Container Manager 的 NAS 上,容器 App 會執行官方 runtime image,SPK 只放 Compose 檔。CLI 拒絕 Compose build:,所以程式碼必須在已發布的 image 或掛載的資料夾裡。這個範例執行一段內嵌的短程式:

synology-app.yaml:

schema: 1
package: com.example.hello-python
version: 0.1.0
displayname: Hello Python
description: A Python worker in a container
maintainer: Example
os_min_ver: "7.2.1-69057"

compose.yaml:

services:
worker:
image: python:3.13-alpine
command: ["python", "-u", "-c", "import time\nwhile True:\n print('hello from HelloPython')\n time.sleep(60)"]

然後執行:

Terminal window
synopkgland images pin
synopkgland check
synopkgland build
synopkgland inspect com.example.hello-python.spk

images pin 會把 tag 換成 digest,讓每次安裝都拿到同一個 image。沒有 digest 時,build 會設定 force_pull,每次安裝都重新解析 tag。SPK 是 noarch;NAS 會在安裝時拉取符合自己 CPU 的 image。

只有 shell 指令碼的套件不含 ELF,可以是 noarch,但 synopkgland build 無法產生它。請用 tar 打包,再用 inspect 檢查:

Terminal window
set -eu
export COPYFILE_DISABLE=1 # macOS: keep ._ files out of the archives
mkdir -p HelloSh/payload/bin HelloSh/spk/scripts HelloSh/spk/conf
cd HelloSh
cat > payload/bin/hello.sh <<'EOF'
#!/bin/sh
while true; do date; sleep 60; done
EOF
chmod 755 payload/bin/hello.sh
tar -czf spk/package.tgz -C payload .
cat > spk/scripts/start-stop-status <<'EOF'
#!/bin/sh
PID="$SYNOPKG_PKGVAR/hello.pid"
case "$1" in
start) "$SYNOPKG_PKGDEST/bin/hello.sh" >>"$SYNOPKG_PKGVAR/hello.log" 2>&1 &
echo $! >"$PID" ;;
stop) [ -f "$PID" ] && kill "$(cat "$PID")"; rm -f "$PID" ;;
status) [ -f "$PID" ] && kill -0 "$(cat "$PID")" 2>/dev/null ;;
esac
EOF
for hook in preinst postinst preuninst postuninst preupgrade postupgrade; do
printf '#!/bin/sh\nexit 0\n' > "spk/scripts/$hook"
done
chmod 755 spk/scripts/*
echo '{"defaults":{"run-as":"package"}}' > spk/conf/privilege
cp ../icon-64.png spk/PACKAGE_ICON.PNG # your 64×64 PNG
cp ../icon-256.png spk/PACKAGE_ICON_256.PNG # your 256×256 PNG
checksum=$( (md5sum spk/package.tgz 2>/dev/null || md5 -r spk/package.tgz) | cut -d' ' -f1)
cat > spk/INFO <<EOF
package="HelloSh"
version="1.0.0-0001"
os_min_ver="7.0-40000"
displayname="Hello Shell"
description="A shell script worker"
maintainer="Example"
arch="noarch"
checksum="$checksum"
EOF
tar -cf HelloSh-1.0.0-0001-noarch.spk -C spk INFO package.tgz scripts conf PACKAGE_ICON.PNG PACKAGE_ICON_256.PNG
synopkgland inspect HelloSh-1.0.0-0001-noarch.spk

synopkgland deploy 只安裝它自己建置的專案。手動打包的 SPK 請用套件中心 > 手動安裝安裝。

synopkgland inspect <spk> 依 DSM 7 套件格式檢查任何 SPK,不論是誰建置的:封存結構、INFO 欄位與 checksum、package.tgz 內的路徑與連結、ELF 檔與 arch 是否相符、指令碼與執行權限、conf/privilege 與 conf/resource、圖示與精靈。它不會安裝或執行任何東西。

Terminal window
synopkgland inspect HelloSh-1.0.0-0001-noarch.spk
synopkgland inspect --json dist/HelloNode-1.0.0-0001-armv7.spk

每個問題都有固定的 SPK 規則碼。錯誤(例如 noarch 套件裡有 ELF 檔)會以 SPK_INSPECTION_FAILED 與結束碼 1 結束。警告(例如缺少 preinst)的結束碼為 0。synopkgland 建置的 SPK(例如上面的 native layout 與 Compose 範例)另有第二個區段檢查 synopkgland 建置合約;手動打包的 SPK 只有 DSM 格式區段。inspect 不能證明程式能在 NAS 上執行。分享之前,請先安裝到測試用 NAS。