From 9fe7714b7b53e8128b9bba8504d3b9db929657e3 Mon Sep 17 00:00:00 2001 From: shenmo Date: Sun, 7 Jun 2026 20:32:46 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E9=AA=8C=E6=80=A7=E6=94=AF=E6=8C=81?= =?UTF-8?q?=20nixos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + README.md | 3 +- default.nix | 3 + docs/NIXOS.md | 123 +++++++++++++++ flake.nix | 50 +++++++ nix/module.nix | 37 +++++ nix/package.nix | 200 +++++++++++++++++++++++++ src/var/lib/apm/apm/files/ace-run | 14 +- src/var/lib/apm/apm/files/ace-run-pkg | 11 +- src/var/lib/apm/apm/files/bin/ace-init | 15 +- src/var/lib/apm/apm/files/bin/ace-run | 13 +- 11 files changed, 460 insertions(+), 11 deletions(-) create mode 100644 .gitignore create mode 100644 default.nix create mode 100644 docs/NIXOS.md create mode 100644 flake.nix create mode 100644 nix/module.nix create mode 100644 nix/package.nix diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..750baeb --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +result +result-* diff --git a/README.md b/README.md index 6916cb4..e52a2cd 100644 --- a/README.md +++ b/README.md @@ -59,8 +59,9 @@ Commands: ### 完整命令列表 使用 `apm --help-all` 查看完整的命令列表,包括高级命令如 `sandbox-run`、`bwrap-run`、`hold`、`unhold`、`full-upgrade`、`download`、`ssinstall`、`ssaudit`、`debug` 等。 +## NixOS 构建与本地测试 - +NixOS 本地构建、安装、module 使用以及 NUR/nixpkgs 打包复用说明见 [docs/NIXOS.md](docs/NIXOS.md)。 ## APM Deb 包全自动转换器使用方法 diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..58b8364 --- /dev/null +++ b/default.nix @@ -0,0 +1,3 @@ +{ pkgs ? import { } }: + +pkgs.callPackage ./nix/package.nix { } diff --git a/docs/NIXOS.md b/docs/NIXOS.md new file mode 100644 index 0000000..fd0961f --- /dev/null +++ b/docs/NIXOS.md @@ -0,0 +1,123 @@ +# NixOS 构建与本地测试 + +本仓库提供了实验性的 Nix 打包文件,可用于在 NixOS 上本地构建和测试 APM。 + +## 本地构建 + +在仓库根目录执行: + +```bash +nix-build default.nix +``` + +构建成功后会生成 `result` 符号链接,可先做基础命令测试: + +```bash +./result/bin/apm --version +./result/bin/apm --help +./result/bin/amber-pm-init-state --help +``` + +如果使用 Flake,也可以执行: + +```bash +nix build .#amber-pm +nix flake check +``` + +## 初始化本地状态目录 + +APM 需要可写的 `/var/lib/apm` 目录保存自身运行环境和已安装应用。Nix 包中的文件位于只读 Nix store,因此首次测试前需要初始化状态目录: + +```bash +sudo ./result/bin/amber-pm-init-state +``` + +如需用新构建结果覆盖 APM 自身文件,可执行: + +```bash +sudo ./result/bin/amber-pm-init-state --force +``` + +`--force` 会原地覆盖 `/var/lib/apm/apm` 中的 APM 自身文件,不会移动、备份或删除整个 `/var/lib/apm/apm` 目录,以免影响已经安装在该目录下的 APM 应用。 + +随后初始化内置 AmberCE 环境: + +```bash +sudo /var/lib/apm/apm/files/bin/ace-init +``` + +完成后可继续测试: + +```bash +./result/bin/apm debug +./result/bin/apm update +./result/bin/apm search amber-pm- +``` + +## 作为 NixOS Module 使用 + +可在 NixOS 配置中引入本仓库的 module: + +```nix +{ pkgs, ... }: +{ + imports = [ + /path/to/amber-pm/nix/module.nix + ]; + + nixpkgs.overlays = [ + (final: prev: { + amber-pm = final.callPackage /path/to/amber-pm/nix/package.nix { }; + }) + ]; + + programs.amber-pm.enable = true; +} +``` + +然后执行: + +```bash +sudo nixos-rebuild switch +``` + +该 module 会将 `amber-pm` 加入 `environment.systemPackages`,并在系统激活时初始化 `/var/lib/apm/apm`。APM 使用 bwrap 与 fuse-overlayfs,module 默认会设置 `kernel.apparmor_restrict_unprivileged_userns = 0`,并启用 `nix-ld` 以提高兼容性。 + +## NUR/nixpkgs 打包复用 + +`nix/package.nix` 支持外部传入 `version` 和 `src`,因此 NUR 或 nixpkgs 中可以复用同一个表达式,不必依赖本地源码路径。 + +NUR 仓库中的示例: + +```nix +{ pkgs ? import { } }: + +{ + amber-pm = pkgs.callPackage ./pkgs/amber-pm { + version = "1.3.4.0"; + src = pkgs.fetchFromGitHub { + owner = "amber-ce"; + repo = "amber-pm"; + rev = "v1.3.4.0"; + hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; + }; + }; +} +``` + +nixpkgs 中的包通常应放在类似路径: + +```text +pkgs/by-name/am/amber-pm/package.nix +``` + +提交 nixpkgs 前建议先满足以下条件: + +- 使用正式 tag 或 release,不使用本地路径作为源码。 +- 固定 `src.hash`。 +- 本地通过 `nix-build -A amber-pm` 或 `nix build .#amber-pm`。 +- 确认 `apm --version`、`apm --help`、`amber-pm-init-state --help` 正常。 +- `meta` 中填写 license、homepage、platforms 和 maintainers。 + +当前 NixOS 适配仍偏测试用途。建议先发布到 NUR 收集测试反馈,再投 nixpkgs。 diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..594ebeb --- /dev/null +++ b/flake.nix @@ -0,0 +1,50 @@ +{ + description = "Amber Package Manager packaged for NixOS testing"; + + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + + outputs = + { self, nixpkgs }: + let + systems = [ + "x86_64-linux" + "aarch64-linux" + "loongarch64-linux" + ]; + forAllSystems = nixpkgs.lib.genAttrs systems; + in + { + packages = forAllSystems ( + system: + let + pkgs = import nixpkgs { inherit system; }; + in + { + amber-pm = pkgs.callPackage ./nix/package.nix { }; + default = self.packages.${system}.amber-pm; + } + ); + + checks = forAllSystems ( + system: + let + pkgs = import nixpkgs { inherit system; }; + amber-pm = self.packages.${system}.amber-pm; + in + { + cli-smoke = pkgs.runCommand "amber-pm-cli-smoke" { } '' + ${amber-pm}/bin/apm --version + ${amber-pm}/bin/apm --help >/dev/null + ${amber-pm}/bin/amber-pm-init-state --help >/dev/null + touch "$out" + ''; + } + ); + + nixosModules.default = import ./nix/module.nix; + + overlays.default = final: prev: { + amber-pm = final.callPackage ./nix/package.nix { }; + }; + }; +} diff --git a/nix/module.nix b/nix/module.nix new file mode 100644 index 0000000..e225439 --- /dev/null +++ b/nix/module.nix @@ -0,0 +1,37 @@ +{ + config, + lib, + pkgs, + ... +}: + +let + cfg = config.programs.amber-pm; +in +{ + options.programs.amber-pm = { + enable = lib.mkEnableOption "Amber Package Manager"; + + package = lib.mkPackageOption pkgs "amber-pm" { }; + + initializeState = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Create /var/lib/apm/apm during system activation when it does not already exist."; + }; + }; + + config = lib.mkIf cfg.enable { + environment.systemPackages = [ cfg.package ]; + + programs.nix-ld.enable = lib.mkDefault true; + + boot.kernel.sysctl."kernel.apparmor_restrict_unprivileged_userns" = lib.mkDefault 0; + + system.activationScripts.amber-pm-state = lib.mkIf cfg.initializeState '' + if [ ! -e /var/lib/apm/apm ]; then + ${cfg.package}/bin/amber-pm-init-state + fi + ''; + }; +} diff --git a/nix/package.nix b/nix/package.nix new file mode 100644 index 0000000..f8ebadd --- /dev/null +++ b/nix/package.nix @@ -0,0 +1,200 @@ +{ + lib, + stdenvNoCC, + makeWrapper, + bash, + bubblewrap, + coreutils, + curl, + desktop-file-utils, + dpkg, + fakeroot, + file, + findutils, + fuse-overlayfs, + gawk, + glib, + gnugrep, + gnused, + gzip, + libnotify, + procps, + sudo, + systemd, + gnutar, + util-linux, + which, + xdg-user-dirs, + xz, + zenity, + version ? "1.3.4.0", + sourceRoot ? ../., + src ? lib.cleanSourceWith { + src = sourceRoot; + filter = + path: type: + let + base = baseNameOf path; + in + ! lib.elem base [ + ".git" + "result" + ]; + }, +}: + +let + runtimePath = lib.makeBinPath [ + bash + bubblewrap + coreutils + curl + desktop-file-utils + dpkg + fakeroot + file + findutils + fuse-overlayfs + gawk + glib + gnugrep + gnused + gzip + libnotify + procps + sudo + systemd + gnutar + util-linux + which + xdg-user-dirs + xz + zenity + ]; +in +stdenvNoCC.mkDerivation { + pname = "amber-pm"; + inherit version; + + inherit src; + + nativeBuildInputs = [ makeWrapper ]; + + dontConfigure = true; + dontBuild = true; + + installPhase = '' + runHook preInstall + + echo "copying Debian-style install tree" + mkdir -p "$out" + cp -a src/* "$out"/ + + rm -f "$out/usr/bin/apm" \ + "$out/usr/bin/amber-pm-debug" \ + "$out/usr/bin/amber-pm-configure-nvidia" + + echo "substituting version and store paths" + substituteInPlace "$out/usr/libexec/apm/apm-main" \ + --replace-fail '@VERSION@' '${version}' \ + --replace-fail '/usr/libexec/apm/apm-eggs' "$out/usr/libexec/apm/apm-eggs" + + while IFS= read -r -d "" file; do + if grep -Iq '@VERSION@' "$file" && grep -q '@VERSION@' "$file"; then + sed -i 's|@VERSION@|${version}|g' "$file" + fi + done < <(find "$out/usr" "$out/etc" -type f -print0) + + echo "patching host script shebangs" + patchShebangs "$out/usr/bin" "$out/usr/libexec" + patchShebangs \ + "$out/var/lib/apm/apm/files/ace-run" \ + "$out/var/lib/apm/apm/files/ace-run-pkg" \ + "$out/var/lib/apm/apm/files/bin/ace-init" \ + "$out/var/lib/apm/apm/files/bin/ace-run" \ + "$out/var/lib/apm/apm/files/bin/amber-ce-configure-nvidia" \ + "$out/var/lib/apm/apm/files/build-container.sh" \ + "$out/var/lib/apm/apm/files/feedback.sh" \ + "$out/var/lib/apm/apm/files/amber-ce-tools/ace-upgrader/ace-upgrader" \ + "$out/var/lib/apm/apm/files/amber-ce-tools/container-init/init.sh" \ + "$out/var/lib/apm/apm/files/amber-ce-tools/bin-override/apm-debug" \ + "$out/var/lib/apm/apm/files/amber-ce-tools/bin-override/bwrap" \ + "$out/var/lib/apm/apm/files/amber-ce-tools/bin-override/gio" \ + "$out/var/lib/apm/apm/files/amber-ce-tools/bin-override/pkexec" \ + "$out/var/lib/apm/apm/files/amber-ce-tools/bin-override/sudo" \ + "$out/var/lib/apm/apm/files/amber-ce-tools/bin-override/xdg-open" + + echo "installing wrappers" + mkdir -p "$out/bin" "$out/share/amber-pm/var-lib-apm" + ln -s /var/lib/apm/apm/files/bin/ace-run "$out/bin/amber-pm-debug" + ln -s /var/lib/apm/apm/files/bin/amber-ce-configure-nvidia "$out/bin/amber-pm-configure-nvidia" + + for prog in "$out"/usr/bin/*; do + if [ -f "$prog" ] || [ -L "$prog" ]; then + name="$(basename "$prog")" + if [ "$name" != apm ] \ + && [ "$name" != amber-pm-debug ] \ + && [ "$name" != amber-pm-configure-nvidia ]; then + makeWrapper "$prog" "$out/bin/$name" \ + --prefix PATH : "$out/bin:${runtimePath}" + fi + fi + done + + cp -a "$out/var/lib/apm/apm" "$out/share/amber-pm/var-lib-apm/apm" + rm -rf "$out/var" + + makeWrapper "$out/usr/libexec/apm/apm-main" "$out/bin/apm" \ + --prefix PATH : "$out/bin:${runtimePath}" + + cat > "$out/bin/amber-pm-init-state" <<'EOF' +#!@bash@/bin/bash +set -euo pipefail + +if [ "''${1:-}" = "--help" ]; then + echo "Usage: amber-pm-init-state [--force]" + echo "Initializes /var/lib/apm/apm from the Nix store seed." + exit 0 +fi + +if [ "$(id -u)" != 0 ]; then + echo "amber-pm-init-state must be run as root because it writes /var/lib/apm" >&2 + exit 1 +fi + +seed="@out@/share/amber-pm/var-lib-apm/apm" +target="/var/lib/apm/apm" + +mkdir -p /var/lib/apm +if [ -e "$target" ] && [ "''${1:-}" != "--force" ]; then + echo "$target already exists; leaving it untouched." + echo "Run 'amber-pm-init-state --force' to refresh APM's own files." + exit 0 +fi + +mkdir -p "$target" +cp -a "$seed"/. "$target"/ +chmod -R u+rwX "$target" +echo "Initialized $target" +echo "Next step: run '/var/lib/apm/apm/files/bin/ace-init' as root, or run 'apm --help' for CLI smoke testing." +EOF + substituteInPlace "$out/bin/amber-pm-init-state" \ + --replace-fail '@bash@' '${bash}' \ + --replace-fail '@out@' "$out" + chmod +x "$out/bin/amber-pm-init-state" + + runHook postInstall + ''; + + meta = { + description = "bwrap and fuse-overlayfs based package manager for Debian-style application containers"; + homepage = "https://gitee.com/amber-ce/amber-pm/"; + license = lib.licenses.gpl3Only; + platforms = [ + "x86_64-linux" + "aarch64-linux" + "loongarch64-linux" + ]; + maintainers = [ ]; + }; +} diff --git a/src/var/lib/apm/apm/files/ace-run b/src/var/lib/apm/apm/files/ace-run index 25cf2a6..e8c979a 100755 --- a/src/var/lib/apm/apm/files/ace-run +++ b/src/var/lib/apm/apm/files/ace-run @@ -27,8 +27,18 @@ function ensure_dir() { return 0 fi } + chrootEnvPath="${chrootEnvPath:-$(dirname $0)/ace-env}" +is_nixos() { + [ -f /etc/os-release ] && grep -Eq '^ID="?nixos"?$' /etc/os-release +} + +APM_CONTAINER_PATH="/amber-ce-tools/bin-override:$PATH" +if is_nixos; then + APM_CONTAINER_PATH="/amber-ce-tools/bin-override:/usr/local/bin:/usr/bin/" +fi + APM_PKG_NAME="${APM_PKG_NAME:-apm-general}" non_root_user=$(who | awk '{print $1}' | head -n 1) @@ -106,7 +116,7 @@ done ENV_VARS=( "FAKEROOTDONTTRYCHOWN 1" "PULSE_SERVER /run/user/\$uid/pulse/native" - "PATH /amber-ce-tools/bin-override:\$PATH" + "PATH $APM_CONTAINER_PATH" "IS_ACE_ENV 1" "GTK_USE_PORTAL 1" "XDG_DATA_DIRS /amber-ce-tools/additional-data-dir-in-container:\$XDG_DATA_DIRS" @@ -201,4 +211,4 @@ add_command "bash -c \"${container_command}\"" # echo "${EXEC_COMMAND}" # 注意: 实际执行时,请确保所有变量(如 $uid, $chrootEnvPath 等)都已正确定义 -eval "${EXEC_COMMAND}" \ No newline at end of file +eval "${EXEC_COMMAND}" diff --git a/src/var/lib/apm/apm/files/ace-run-pkg b/src/var/lib/apm/apm/files/ace-run-pkg index 44d05f1..a75d269 100755 --- a/src/var/lib/apm/apm/files/ace-run-pkg +++ b/src/var/lib/apm/apm/files/ace-run-pkg @@ -7,6 +7,14 @@ export -f bash chrootEnvPath="${chrootEnvPath:-$(pwd)/ace-env}" +is_nixos() { + [ -f /etc/os-release ] && grep -Eq '^ID="?nixos"?$' /etc/os-release +} + +APM_CONTAINER_PATH="/amber-ce-tools/bin-override:$PATH" +if is_nixos; then + APM_CONTAINER_PATH="/amber-ce-tools/bin-override:/usr/local/bin:/usr/bin/" +fi non_root_user=$(who | awk '{print $1}' | head -n 1) @@ -80,7 +88,7 @@ done ENV_VARS=( "FAKEROOTDONTTRYCHOWN 1" "PULSE_SERVER /run/user/\$uid/pulse/native" - "PATH /amber-ce-tools/bin-override:\$PATH" + "PATH $APM_CONTAINER_PATH" "IS_ACE_ENV 1" "GTK_USE_PORTAL 1" "XDG_DATA_DIRS /amber-ce-tools/additional-data-dir-in-container:\$XDG_DATA_DIRS" @@ -139,4 +147,3 @@ add_command "bash -c \"${container_command}\"" # 注意: 实际执行时,请确保所有变量(如 $uid, $chrootEnvPath 等)都已正确定义 eval ${EXEC_COMMAND} - diff --git a/src/var/lib/apm/apm/files/bin/ace-init b/src/var/lib/apm/apm/files/bin/ace-init index 61ba309..18df9d5 100755 --- a/src/var/lib/apm/apm/files/bin/ace-init +++ b/src/var/lib/apm/apm/files/bin/ace-init @@ -15,6 +15,15 @@ else PKGNAME=$PACKAGE_NAME fi chrootEnvPath=/var/lib/apm/$PKGNAME/files/ace-env +is_nixos() { + [ -f /etc/os-release ] && grep -Eq '^ID="?nixos"?$' /etc/os-release +} + +APM_CONTAINER_PATH="/amber-ce-tools/bin-override:$PATH" +if is_nixos; then + APM_CONTAINER_PATH="/amber-ce-tools/bin-override:/usr/local/bin:/usr/bin/" +fi + #if [ ! -e $chrootEnvPath ];then echo "Uncompress the env...." tar -xvf $chrootEnvPath.tar.xz -C /var/lib/apm/$PKGNAME/files/ @@ -41,7 +50,7 @@ uid=$(id -u $non_root_user) function bookworm-run(){ bwrap --dev-bind $chrootEnvPath/ / \ --setenv PULSE_SERVER /run/user/$uid/pulse/native \ - --setenv PATH /amber-ce-tools/bin-override:$PATH \ + --setenv PATH "$APM_CONTAINER_PATH" \ --setenv IS_ACE_ENV "1" \ --dev-bind-try /media /media \ --dev-bind-try /tmp /tmp \ @@ -89,7 +98,7 @@ bookworm-run apt clean bookworm-run chown -R $(get_current_user) /usr/lib/locale/ sudo -u $(get_current_user) bwrap --dev-bind $chrootEnvPath/ / \ --setenv PULSE_SERVER /run/user/$uid/pulse/native \ - --setenv PATH /amber-ce-tools/bin-override:$PATH \ + --setenv PATH "$APM_CONTAINER_PATH" \ --setenv IS_ACE_ENV "1" \ --dev-bind $chrootEnvPath/ / \ --dev-bind-try /media /media \ @@ -121,4 +130,4 @@ ln -sfv ../../usr/share/pixmaps/ $chrootEnvPath/amber-ce-tools/data-dir/ chmod 777 -R $chrootEnvPath/usr/share/icons rm -vfr $chrootEnvPath/dev/* -true \ No newline at end of file +true diff --git a/src/var/lib/apm/apm/files/bin/ace-run b/src/var/lib/apm/apm/files/bin/ace-run index ed76655..f29abc8 100755 --- a/src/var/lib/apm/apm/files/bin/ace-run +++ b/src/var/lib/apm/apm/files/bin/ace-run @@ -14,6 +14,15 @@ export ACE_PACKAGE_NAME=$PKGNAME chrootEnvPath=/var/lib/apm/$PKGNAME/files/ace-env +is_nixos() { + [ -f /etc/os-release ] && grep -Eq '^ID="?nixos"?$' /etc/os-release +} + +APM_CONTAINER_PATH="/amber-ce-tools/bin-override:$PATH" +if is_nixos; then + APM_CONTAINER_PATH="/amber-ce-tools/bin-override:/usr/local/bin:/usr/bin/" +fi + # if [ ! -e $chrootEnvPath/finish.flag ];then # if [ "$(id -u)" = "0" ]; then @@ -94,7 +103,7 @@ done ENV_VARS=( "FAKEROOTDONTTRYCHOWN 1" "PULSE_SERVER /run/user/\$uid/pulse/native" - "PATH /amber-ce-tools/bin-override:\$PATH" + "PATH $APM_CONTAINER_PATH" "IS_ACE_ENV 1" "IS_APM_ENV 1" "XDG_DATA_DIRS /amber-ce-tools/additional-data-dir-in-container:\$XDG_DATA_DIRS" @@ -149,5 +158,3 @@ add_command "bash -c \"${container_command}\"" # 注意: 实际执行时,请确保所有变量(如 $uid, $chrootEnvPath 等)都已正确定义 eval ${EXEC_COMMAND} - -