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

封裝既有的原生產品

當你已經有一個會自行建置執行檔、啟動指令碼與 DSM 視窗的產品,只需要在外面包一層 SPK 時,就使用 native layout。synopkgland 會執行你的建置 hook、檢查結果,並為每個架構輸出一個 SPK。它不會產生應用程式碼、後端或 UI。

如果你想要的是有產生程式碼的新 App,請改從建立原生 App開始。

請手寫 dsm-package.yaml。native layout 不是 init 的模板。

schema: 2
backend: {kind: native}
package: ExampleNative
version: 1.2.3
build: 8
displayname: Example Native
description: Existing native product
maintainer: Example
os_min_ver: 7.0-40000
arches: [x86_64, armv8, armv7]
layout:
payload: target/native-layout/{arch}/payload
metadata: target/spk-metadata
binaries:
- {path: bin/app, linkage: static, arches: [x86_64, armv8]}
- {path: bin/app, linkage: dynamic, arches: [armv7]}
native:
prepare: {cwd: ., command: [python3, package/prepare.py]}
hook: {cwd: ., command: [python3, package/compile.py]}
toolkits:
x86_64:
- {url: 'https://example.com/toolkit.txz', sha256: '<64 lowercase hex digits>'}

請把 toolkit 的 URL 與 digest 換成你自己的;如果編譯 hook 不需要 toolkit,就移除 toolkits。

然後執行:

Terminal window
synopkgland check
synopkgland build --arch x86_64,armv8,armv7
synopkgland inspect dist/ExampleNative-1.2.3-0008-x86_64.spk

build 會輸出 dist/<package>-<version>-<補滿 4 位數的 build>-<arch>.spk。沒有 --arch 時,會建置 arches 內的每個架構。

要安裝到 NAS,執行 synopkgland deploy --host <nas>。它只建置 NAS 的架構(在 aarch64 NAS 上,若你宣告了 armv8 就使用它)並安裝 SPK。每次部署前請提高 build。之後 synopkgland status --host <nas> 會回報安裝狀態。

欄位規則
允許的鍵schema、backend、package、version、build、displayname、description、maintainer、metadata、icon、os_min_ver、privilege、arches、layout、native
schema2
backend{kind: native}
package^[A-Za-z][A-Za-z0-9]{1,63}$
version^\d+(\.\d+){0,3}$
build1 到 2147483647 的整數
os_min_ver7.x-<build>,例如 7.0-40000
privilegenone,或省略
arches非空、不可重複:x86_64、aarch64、armv8、armv7
layout.payload放入套件檔案的目錄。可含 {arch}。
layout.metadata放 SPK 中繼資料檔案的目錄(見下方)
layout.binaries一個或多個 {path, linkage, arches?}。linkage 為 static 或 dynamic。arches 必須是 manifest arches 的子集。每個架構至少要有一個執行檔。
icon選用。專案內至少 256×256 的正方形 PNG,或含 icon_256.png 的 icon_<size>.png 目錄。build 由它產生 PACKAGE_ICON.PNG 與 PACKAGE_ICON_256.PNG;metadata 目錄不可同時放這兩個檔案。視窗圖示仍由 payload 提供。
layout.anchors選用。<key>_sha256: <payload 路徑>。為每個架構把該檔案的 SHA-256 寫進 INFO。見產品自帶的 root helper 與升級。
native.hook必填。為單一架構編譯或收集 payload。
native.prepare選填。在各架構的 hook 之前執行一次。
native.toolkits選填。<arch>: [{url, sha256}]。url 是 HTTPS。sha256 是 64 個小寫十六進位字元。
reproducible選填。build --verify-reproducible 用的 {inputs?: [檔案...], clean?: [路徑...]}。把 target/{arch} 這類建置產物列在 clean,第二次建置才會從頭編譯。參閱可重現建置。

manifest 中的路徑都相對於專案,不可是絕對路徑,也不可含 ..,除了 {arch} 之外不可含 { 或 }。同一專案中若有 compose.yaml 會被拒絕。

hook 的格式是 {command: [argv...], cwd?: '.', env?: {...}}。指令在專案內以引數陣列執行,不經過 shell。hook 必須以狀態 0 結束。env 的鍵須符合 ^[A-Z_][A-Z0-9_]*$,且不可以 SYNOPKGLAND_ 開頭。

變數意義
SYNOPKGLAND_PACKAGE套件名稱
SYNOPKGLAND_VERSION、SYNOPKGLAND_BUILDmanifest 的版本與 build 編號
SYNOPKGLAND_SPK_VERSION版本加上補滿 4 位數的 build
SYNOPKGLAND_PROJECT_DIR專案根目錄的絕對路徑
SYNOPKGLAND_ARCH目前的架構。僅編譯 hook 有。
SYNOPKGLAND_TOOLKIT_DIR剛解開的 toolkit 目錄樹。僅編譯 hook 有。
SYNOPKGLAND_WORK_DIR這個架構專用的空白暫存資料夾,建置後刪除。僅編譯 hook 有。

synopkgland build --jobs <n> 最多同時為 n 個架構執行編譯 hook。只有 hook 能把各架構分開時才使用:layout.payload 必須含 {arch}(否則 CLI 會拒絕大於 1 的 --jobs),hook 也不可寫入共用資料夾,例如單一的 build/ 或 out/。暫存檔請放在 SYNOPKGLAND_WORK_DIR。Cargo 在每個架構使用各自的 --target 時是安全的,因為它會鎖住共用的 target/ 資料夾,但各架構會在那裡互相等待。native.prepare 仍只在所有架構開始前執行一次。

toolkit 封存檔會透過 HTTPS 下載到快取,並在每次建置時以 sha256 檢查。用 SYNOPKGLAND_CACHE_DIR 指定快取資料夾。預設是每位使用者一份、所有專案與 worktree 共用的快取:Linux 為 $XDG_CACHE_HOME/synopkgland 或 ~/.cache/synopkgland,macOS 為 ~/Library/Caches/synopkgland。多個建置可以同時使用這個快取:每次下載先寫入私有的暫存檔,檢查通過後才改名放入快取,每個建置也各自解開一份 toolkit。快取資料夾不可是符號連結,也不可讓其他使用者寫入。

封存檔由 PATH 上的 tar 解開,它必須是 GNU tar 或 bsdtar(macOS 預設)。後面的封存檔可以寫進前面封存檔建立的資料夾符號連結。使用 bsdtar 時,CLI 會先拒絕含有絕對路徑或 .. 成員的封存檔。

check 與 build 會驗證 payload 與中繼資料資料夾。

範圍規則
檔案不可有符號連結與特殊檔案。不可有 setuid 或 setgid 位元。
大小每個檔案最多 256 MiB,所有檔案合計最多 1 GiB。
執行檔每個列出的執行檔都必須是該架構、可執行的 ELF 檔案,且連結方式符合 linkage。
metadata 最上層只能有 conf、scripts、WIZARD_UIFILES、PACKAGE_ICON.PNG 與 PACKAGE_ICON_256.PNG(設了 manifest icon 時不可放)
scripts/start-stop-status必要
scripts/*不可含 sudo、chown root 或 chmod +s
生命週期腳本必須可執行(chmod +x):preinst、postinst、preuninst、postuninst、preupgrade、postupgrade、start-stop-status、prereplace、postreplace
conf/privilegeJSON,defaults.run-as 須為 package。username 與 groupname 選填,且必須等於套件名稱。
conf/resource選填。只接受空的 systemd-unit 物件。
conf/systemd只能有最上層的 *.service 檔案。每個檔案恰有一個 User=,且等於套件名稱。若有 Group=,也必須等於套件名稱。
Service unit不可有續行。User、Group 與 Exec* 行只能在 [Service] 內。不可有 AmbientCapabilities、SupplementaryGroups、PermissionsStartOnly、RootDirectory 或 RootImage。Exec* 指令不可有 + 或 ! 前綴。

套件永遠以自己的套件帳號執行。native layout 沒有 root 選項。

如果你的產品自帶 root helper 並需要管理員授權,請讓授權綁定管理員實際同意的內容:capabilities、套件帳號與固定的排程任務指令。不要綁定 build、manifest 或檔案樹的 digest。若要讓新的 SPK 改變 root 程式碼而不需重新核准,請在 layout.anchors 宣告每個 root payload:

layout:
anchors:
example_helper_sha256: bin/helper
example_bootstrap_sha256: bin/bootstrap

build 會為每個架構把該架構 payload 檔案的 SHA-256 寫進 INFO,例如 example_helper_sha256="<64 個小寫 hex>"。Package Center 以 root 寫入 /var/packages/<package>/INFO,套件帳號改不了它。

規則值
Key^[a-z][a-z0-9_]{0,48}_sha256$,不可以 synopkgland_ 開頭,最多 16 個
路徑payload 中的一般檔案,相對於 payload 資料夾,不含 {arch}
build檔案不存在,或 key 與其他 INFO 欄位衝突時失敗
inspectINFO 錨點被改動、重複、缺少或未宣告時拒絕該 SPK

你的 root helper 從 root 擁有的 INFO 為每個 key 讀取剛好一行有效值,把 payload 複製到受保護的資料夾,用該值檢查複本後才執行。固定的任務指令不要內嵌某個 build 專屬的 digest。值缺少、重複或不符時,授權維持停用,直到管理員核准。只有 capabilities 改變時才在升級時撤銷授權,並讓 authorization.status 回傳 reason: upgrade_reapproval。SDK executor 以 synopkgland_executor_sha256 遵循同一規則。

arches 值ELF 檢查備註
x86_6464 位元,machine 62
aarch6464 位元,machine 183INFO 的 arch 列出 aarch64 平台(armada37xx rtd1296 rtd1619 rtd1619b),與 native-go、binary 套件相同。
armv864 位元,machine 183與 aarch64 的二進位格式相同。SPK 保留 armv8 這個名稱。
armv732 位元,machine 40

dynamic 連結表示你的執行檔相依於目標 DSM 的函式庫。請在 native.toolkits 中固定對應的 DSM toolkit。

限制值
可用的指令check、build、inspect、deploy、status
會拒絕它的指令logs
build 範圍1 到 2147483647
權限none(套件帳號)
架構x86_64、aarch64、armv8、armv7
檔案大小每個 256 MiB,合計 1 GiB

inspect 只檢查封存檔,不會安裝或執行任何東西。它不能證明產品在裝置上可正常運作。

錯誤碼或訊息原因解法
NATIVE_LAYOUT_INVALID: schema 2 requiredschema 不是 2,或缺少 backend。設定 schema: 2 與 backend: {kind: native}。
NATIVE_LAYOUT_INVALID: unknown field有鍵不在允許清單內。移除它。
NATIVE_LAYOUT_INVALID: path must stay inside project路徑是絕對路徑、含 ..,或含 {arch} 以外的 {。使用專案內的相對路徑。
NATIVE_LAYOUT_INVALID: ELF architecture mismatch執行檔不是為正在建置的架構編譯的。在 hook 中建置正確的執行檔。
NATIVE_LAYOUT_INVALID: ELF linkage mismatchlinkage 與執行檔不符。修正 linkage 或建置方式。
NATIVE_LAYOUT_INVALID: native hook failedhook 以非 0 狀態結束。手動執行該 hook 指令。
NATIVE_LAYOUT_INVALID: toolkit checksum mismatch下載內容與 sha256 不符。修正 URL 或 digest。
NATIVE_LAYOUT_INVALID: native layout requires package-only lifecycle privilegeconf/privilege 要求的權限超過套件帳號。把 defaults.run-as 設為 package。
NATIVE_LAYOUT_INVALID: start-stop-status missingmetadata 內沒有 scripts/start-stop-status。加入它。
NATIVE_TOOLKIT_TAR_UNSUPPORTEDPATH 上的 tar 不是 GNU tar 也不是 bsdtar。安裝其中一個並放在 PATH 最前面。
NATIVE_LAYOUT_SCRIPT_NOT_EXECUTABLEmetadata/scripts 的生命週期腳本沒有執行權限。對它執行 chmod +x。

所有錯誤碼請見錯誤碼。