{ lib, runCommand, pkgs, name, packages, services ? [], osId ? "patos", version ? null, }: let metadata = { ID = osId; VERSION_ID = osId; IMAGE_ID = name; IMAGE_VERSION = version; } // lib.optionalAttrs (services != []) { EXTENSION_RELOAD_MANAGER = "1"; }; metadataFile = lib.concatStringsSep "\n" ( lib.mapAttrsToList (k: v: "${k}=${v}") (lib.filterAttrs (_: v: v != null) metadata) ); doCopy = { drv, prefix ? "usr", path, destpath ? null, }: "do_copy ${prefix} ${drv} ${path} ${drv.name} " + builtins.concatStringsSep "," (map (l: l.shortName or "unknown") (lib.toList (drv.meta.license or []))) + lib.optionalString (destpath != null) " ${destpath}"; in runCommand name { passthru.name = name; inherit metadataFile; passAsFile = [ "metadataFile" ]; nativeBuildInputs = [ pkgs.erofs-utils pkgs.cryptsetup pkgs.gawk pkgs.jq ]; } '' set -ex -o pipefail do_copy () { local prefix="$1" local drv="$2" local path="$3" local pkgname="$4" local license="$5" local destpath="''${6:-$path}" local srcfile local destdir local destfile srcfile="$drv/$path" destfile="$out/tree/$prefix/$destpath" destdir="$(dirname -- "$destfile")" echo "pkgname=\"$pkgname\",licenses=\""$license"\"" >> $out/.tmp-pkgs.txt mkdir -pv "$destdir" # recursively copy if ending with / if [[ "$destfile" =~ /$ ]]; then basedir="$(dirname -- "$destfile")" chmod -R 755 "$basedir" # remove if exists for f in $srcfile/*; do basename="$(basename -- "$f")" rm -rf "$destfile/$basename" done cp -rPv "$srcfile" "$basedir" chmod -R 755 "$basedir" for f in $destfile/*; do interpreter=$(patchelf --print-interpreter $f || echo "") [ -n "$interpreter" ] && ldLinux=$(basename $interpreter) patchelf --set-interpreter /lib/$ldLinux $f || true patchelf --set-rpath /usr/lib $f || true done return fi cp -Pv "$srcfile" "$destfile" chmod 755 "$destfile" interpreter=$(patchelf --print-interpreter $destfile || echo "") [ -n "$interpreter" ] && ldLinux=$(basename $interpreter) patchelf --set-rpath /usr/lib $destfile || true patchelf --set-interpreter /lib/$ldLinux $destfile || true } do_service () { local unit="$1" local content="$2" local unit_file="$out/tree/usr/lib/systemd/system/$unit" mkdir -p $out/tree/usr/lib/systemd/system echo "$content" > $unit_file # look for [Install] section and WantedBy in unit if ! grep -q "^\[Install\]" "$unit_file"; then echo "No [Install] section found in $unit_file" return fi local wanted_by=$(sed -n '/^\[Install\]/,/^\[/{/^WantedBy=/s/^WantedBy=//p}' "$unit_file") if [ -z "$wanted_by" ]; then echo "No WantedBy found in [Install] section of $unit_file" exit 1 fi mkdir -p $out/tree/usr/lib/systemd/system/"$wanted_by".wants ln -s ../$unit $out/tree/usr/lib/systemd/system/"$wanted_by".wants/$unit } mkdir -p $out/tree ${lib.concatStringsSep "\n" (map doCopy packages)} ${lib.concatStringsSep "\n" (map (service: "do_service '${service.unit}' '${service.content}'") services)} # bake metadata into the structure if ! [ -f $out/tree/usr/lib/extension-release.d/extension-release."${name}" ]; then mkdir -p $out/tree/usr/lib/extension-release.d cat "$metadataFilePath" > $out/tree/usr/lib/extension-release.d/extension-release."${name}" fi pushd $out find tree -type d -exec chmod 0755 {} \; mkfs.erofs -zlz4hc,12 -C1048576 -Efragments,dedupe,ztailpacking --all-root $name.raw tree/ veritysetup format --root-hash-file $name.roothash $name.raw $name.verity # TODO: pcks7 signature # openssl smime -sign -nocerts -noattr -binary -in ${name}.roothash \ # -inkey key.pem -signer cert.pem -outform der -out ${name}.roothash.p7s # create contents list pushd tree find . -ls > $out/"$name"_contents.txt popd # create nixpkgs packages list sort -u $out/.tmp-pkgs.txt > $out/"$name"_packages.txt rm -f $out/.tmp-pkgs.txt jq -R -s 'split("\n") | map(select(length > 0)) | map(capture("pkgname=\"(?<name>[^\"]*)\",licenses=\"(?<licenses>[^\"]*)\"") | .licenses |= split(",")) | map(select(. != null))' $out/"$name"_packages.txt > $out/"$name"_packages.json rm -rf tree sha256sum * > SHA256SUMS ln -s SHA256SUMS SHA256SUMS.asc # TODO: add gpg signature popd ''