From b890587443e89d5deb3b6cd8652fba343be93966 Mon Sep 17 00:00:00 2001 From: Lin Jian Date: Wed, 11 Feb 2026 21:46:00 +0800 Subject: [PATCH] mirage: fix updateScript MirageOS unikernels are a package set of many targets, which share the same `src`. To avoid race condition, instead of attaching the same updateScript to each target and running the same updateScript multiple times, we create a dummy package called `update` in the package set and only attach the updateScript to it. This way, the updateScript is only run once. The result of the updateScript is two independent update commits: 1. pname: old-version -> new-version 2. pname: update deps The 1st commit only exists if `src` is updated. The 2nd commit only exists if ocaml dependencies are updated. The existence of one does not affect the existence of the other. Some detailed changes: - fix meta.position of `configure`d unikernel - support updating src and related flake inputs - update src, related flake inputs and deps of all targets in lockstep - add a no-op updateScript to each target to prevent .github/workflows/update-packages.yaml from running a default updateScript In addition, we provide a script for each target to generate/update its own deps: ```shellSession $ nix run -f . dnsvizor.hvt.depsUpdateScriptForThisTarget ``` --- pkgs/by-name/dnsvizor/mirage.nix | 168 ++++++++++++++++++++++--------- 1 file changed, 118 insertions(+), 50 deletions(-) diff --git a/pkgs/by-name/dnsvizor/mirage.nix b/pkgs/by-name/dnsvizor/mirage.nix index ece9d55c2..6dc40527c 100644 --- a/pkgs/by-name/dnsvizor/mirage.nix +++ b/pkgs/by-name/dnsvizor/mirage.nix @@ -6,6 +6,9 @@ opam-nix, stdenv, writeShellApplication, + unstableGitUpdater, + git, + emptyDirectory, }: rec { @@ -21,7 +24,7 @@ rec { target, opamPackages ? opam-nix.queryToScope { } ({ mirage = "*"; } // query), ... - }: + }@args: stdenv.mkDerivation { name = "mirage-${pname}-${target}"; inherit src version; @@ -43,6 +46,7 @@ rec { cp -R . $out runHook postBuild ''; + pos = builtins.unsafeGetAttrPos "src" args; }; # Description: read opam files from mirage-conf and build the unikernel. @@ -155,6 +159,7 @@ rec { builds = { pname, + src, targets, packages-materialized-path, monorepo-materialized-path, @@ -162,57 +167,120 @@ rec { ... }@args: let - self = lib.genAttrs targets ( + # deps of all targets, together with src and related flake inputs, are updated in lockstep + depsUpdateScript = lib.getExe (writeShellApplication { + name = "${pname}-update"; + inherit runtimeInputs; + text = + let + updateSrc = lib.escapeShellArgs (unstableGitUpdater { + url = src.gitRepoUrl; + }); + in + '' + set -x + + srcUpdateJson=$(UPDATE_NIX_ATTR_PATH=${pname}.${lib.head targets}.mirage-conf \ + ${updateSrc} | \ + jq '.[] += {attrPath:"${pname}"} | .[]') + + # update opam-related flake inputs because they are used when updating deps + nix --extra-experimental-features "nix-command flakes" \ + flake update opam-repository opam-overlays mirage-opam-overlays + + declare -a updatedFiles + # work around "unbound variable" error of empty array caused by set -u + updatedFiles+=() + + ${lib.concatLines (map updateDepsForTarget targets)} + + flakeLockFile="flake.lock" + depsUpdateJson="" + if [ ''${#updatedFiles[@]} -gt 0 ]; then + if ! git diff --quiet -- "$flakeLockFile"; then + updatedFiles+=("$flakeLockFile") + fi + depsUpdateJson=$(jq --null-input \ + '{"attrPath":"${pname}","oldVersion":"0","newVersion":"0","commitMessage":"${pname}: update deps","files":$ARGS.positional}' \ + --args -- "''${updatedFiles[@]}") + fi + + jq --slurp <<< "$srcUpdateJson$depsUpdateJson" + ''; + }); + depsUpdateScriptForTarget = target: - (build ( - args - // { - inherit target; - monorepo-materialized-path = monorepo-materialized-path + "/${target}.json"; - packages-materialized-path = packages-materialized-path + "/${target}.json"; - } - )).overrideAttrs - ( - lib.composeExtensions (finalAttrs: previousAttrs: { - passthru = previousAttrs.passthru or { } // { - updateScript = writeShellApplication { - name = "${pname}-${target}-update"; - runtimeInputs = [ - coreutils - jq - nix - ]; - text = '' - set -x - packagesJson=$(nix --extra-experimental-features nix-command -L build \ - --no-link --print-out-paths --allow-import-from-derivation -f. \ - ${pname}.${target}.packages-materialized) - jq <"$packagesJson" | - install -Dm660 /dev/stdin pkgs/by-name/${pname}/packages-materialized/${target}.json + writeShellApplication { + name = "${pname}-${target}-deps-update"; + inherit runtimeInputs; + text = '' + set -x + ${updateDepsForTarget target} + ''; + }; + runtimeInputs = [ + nix + jq + coreutils + git + ]; + updateDepsForTarget = target: '' + packagesMaterializedFile="pkgs/by-name/${pname}/packages-materialized/${target}.json" + packagesJson=$(nix --extra-experimental-features nix-command -L build \ + --no-link --print-out-paths --allow-import-from-derivation -f. \ + ${pname}.${target}.packages-materialized) + jq <"$packagesJson" | + install -Dm660 /dev/stdin "$packagesMaterializedFile" + if ! git diff --quiet -- "$packagesMaterializedFile"; then + updatedFiles+=("$packagesMaterializedFile") + fi - monorepoJson=$(nix --extra-experimental-features nix-command -L build \ - --no-link --print-out-paths --allow-import-from-derivation -f. \ - ${pname}.${target}.monorepo-materialized) - jq <"$monorepoJson" | - install -Dm660 /dev/stdin pkgs/by-name/${pname}/monorepo-materialized/${target}.json - ''; - }; - }; - }) overrideAttrs - ) - ); + monorepoMaterializedFile="pkgs/by-name/${pname}/monorepo-materialized/${target}.json" + monorepoJson=$(nix --extra-experimental-features nix-command -L build \ + --no-link --print-out-paths --allow-import-from-derivation -f. \ + ${pname}.${target}.monorepo-materialized) + jq <"$monorepoJson" | + install -Dm660 /dev/stdin "$monorepoMaterializedFile" + if ! git diff --quiet -- "$monorepoMaterializedFile"; then + updatedFiles+=("$monorepoMaterializedFile") + fi + ''; in - lib.recurseIntoAttrs ( - self - // { - updateScript = writeShellApplication { - name = "dnsvizor-update"; - runtimeInputs = [ - jq - nix - ]; - text = lib.concatMapStringsSep "\n" (target: lib.getExe self.${target}.updateScript) targets; + lib.genAttrs targets ( + target: + (build ( + args + // { + inherit target; + monorepo-materialized-path = monorepo-materialized-path + "/${target}.json"; + packages-materialized-path = packages-materialized-path + "/${target}.json"; + } + )).overrideAttrs + ( + lib.composeExtensions (finalAttrs: previousAttrs: { + passthru = previousAttrs.passthru or { } // { + depsUpdateScriptForThisTarget = depsUpdateScriptForTarget target; + # add a no-op updateScript to prevent .github/workflows/update-packages.yaml + # from running a default updateScript + updateScript = lib.getExe' coreutils "true"; + }; + }) overrideAttrs + ) + ) + // { + update = stdenv.mkDerivation { + pname = "${pname}-update"; + version = "0"; + src = emptyDirectory; + installPhase = '' + runHook preInstall + echo "This dummy package contains an updateScript updating the package set." > $out + runHook postInstall + ''; + passthru.updateScript = { + command = depsUpdateScript; + supportedFeatures = [ "commit" ]; }; - } - ); + }; + }; }