From c6b465eef837a7cfaa39f8c49794e0c13430e927 Mon Sep 17 00:00:00 2001 From: Timothy DeHerrera Date: Mon, 20 Mar 2023 19:36:14 -0600 Subject: [PATCH] feat(ops): add `revise` family of packages --- cells/lib/ops.nix | 8 ++++- cells/lib/ops/revise.nix | 55 ++++++++++++++++++++++++++++ cells/lib/ops/reviseOCI.nix | 64 +++++++++++++++++++++++++++++++++ cells/lib/ops/revisePackage.nix | 38 ++++++++++++++++++++ 4 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 cells/lib/ops/revise.nix create mode 100644 cells/lib/ops/reviseOCI.nix create mode 100644 cells/lib/ops/revisePackage.nix diff --git a/cells/lib/ops.nix b/cells/lib/ops.nix index 80e006bb..d10a5951 100644 --- a/cells/lib/ops.nix +++ b/cells/lib/ops.nix @@ -3,8 +3,10 @@ cell, }: let inherit (inputs.cells.std.errors) requireInput; - inherit (import "${inputs.self}/deprecation.nix" inputs) warnWriteShellEntrypoint; + inherit (inputs.nixpkgs) lib; in { + hashOfPath = path: baseNameOf (lib.head (lib.splitString "-" path)); + mkMicrovm = import ./ops/mkMicrovm.nix { inputs = requireInput "microvm" "github:astro/microvm.nix" "std.lib.ops.mkMicrovm"; }; @@ -18,4 +20,8 @@ in { mkOCI = import ./ops/mkOCI.nix {inherit inputs cell;}; mkDevOCI = import ./ops/mkDevOCI.nix {inherit inputs cell;}; mkStandardOCI = import ./ops/mkStandardOCI.nix {inherit inputs cell;}; + + revise = import ./ops/revise.nix {inherit inputs cell;}; + revisePackage = import ./ops/revisePackage.nix {inherit inputs cell;}; + reviseOCI = import ./ops/reviseOCI.nix {inherit inputs cell;}; } diff --git a/cells/lib/ops/revise.nix b/cells/lib/ops/revise.nix new file mode 100644 index 00000000..264303fa --- /dev/null +++ b/cells/lib/ops/revise.nix @@ -0,0 +1,55 @@ +{ + inputs, + cell, +}: let + inherit (inputs) nixpkgs; + l = nixpkgs.lib // builtins; +in + /* + For use with `revisePackage` and `reviseOCI` to build containers in a mono-repo style + environment where the source code is contained in the same repository as the std code, + specifically so that one may detect meaningful changes to the image via its tag in the + special case where the package's output includes the revision of the source code (e.g. for + displaying the version to the user). + + Without special processing, this kind of package would cause the OCI image tag to change + on each new revision whether the actual contents of the image changed or not. Combined + with `std.incl`, one may have a very strong indicator for when the contents of the image + actually includes meaningful changes which avoids flooding the remote registry with superflous + copies. + + Args: + op: Optional function which takes the package as an argument. + pkg: The package you wish to revise. + fn: Optional functor with a reference to `pkg` if needed by `op`. + + Returns: + The package with a clone of itself in the passthru where the expected revision is set to + "not-a-commit" for later use by `reviseOCI`. + */ + op: pkg: fn: let + result = op (fn pkg); + dummy = "not-a-commit"; + rev = pkg.src.rev or pkg.src.origSrc.rev or dummy; + in + if pkg ? sansrev || (rev != dummy && result == pkg) + then + result.overrideAttrs (_: { + passthru = + result.passthru + or {} + // { + sansrev = let + pkg' = op (fn (pkg.sansrev or (pkg.override {rev = dummy;}))); + in + pkg'.overrideAttrs (_: { + passthru = + pkg'.passthru + or {} + // { + outHash = cell.ops.hashOfPath pkg'.outPath; + }; + }); + }; + }) + else result diff --git a/cells/lib/ops/reviseOCI.nix b/cells/lib/ops/reviseOCI.nix new file mode 100644 index 00000000..325cd21f --- /dev/null +++ b/cells/lib/ops/reviseOCI.nix @@ -0,0 +1,64 @@ +{ + inputs, + cell, +}: let + inherit (inputs) nixpkgs; + l = nixpkgs.lib // builtins; +in + /* + Utility function to allow for building containers in a mono-repo style environment where + the source code is contained in the same repository as the std code, specifically so that + one may detect meaningful changes to the image via its tag in the special case where the + package's output includes the revision of the source code (e.g. for displaying the version + to the user). + + Without special processing, this kind of package would cause the OCI image tag to change + on each new revision whether the actual contents of the image changed or not. Combined + with `std.incl`, one may have a very strong indicator for when the contents of the image + actually includes meaningful changes which avoids flooding the remote registry with superflous + copies. + + This function can also be called where the package does not need the revsion at build time + but you simply want to tag the image by its hash for later processing by the proviso, and + you also want to include additional tags on the image, such as the revision. + + Args: + args: arguments to the mkOCI function to be called. + mkOCI: defaults to `mkStandardOCI` + operable: The operable to include in the container + ...: The same arguments expected by the given standard OCI builder + + Returns: + An image tagged with the output hash of an identical image, except where the target package + and operable are both built with the revision input set to "not-a-commit" instead of the true + revision, so that the hash does not change unless something inside the image does. + */ + args' @ { + operable, + mkOCI ? cell.ops.mkStandardOCI, + ... + }: let + args = builtins.removeAttrs args' ["mkOCI"]; + revision = cell.ops.revise mkOCI args.operable (operable: args // {inherit operable;}); + in + if args.operable ? sansrev + then + mkOCI (args + // { + meta = + args.meta + or {} + // { + tags = [revision.sansrev.outHash] ++ (args.meta.tags or []); + }; + }) + else + mkOCI (args + // { + meta = + args.meta + or {} + // { + tags = [(cell.ops.hashOfPath revision.outPath)] ++ (args.meta.tags or []); + }; + }) diff --git a/cells/lib/ops/revisePackage.nix b/cells/lib/ops/revisePackage.nix new file mode 100644 index 00000000..63abd4c1 --- /dev/null +++ b/cells/lib/ops/revisePackage.nix @@ -0,0 +1,38 @@ +{ + inputs, + cell, +}: let + inherit (inputs) nixpkgs; + l = nixpkgs.lib // builtins; +in + /* + For use with `revise` and `reviseOCI` to build containers in a mono-repo style + environment where the source code is contained in the same repository as the std code, + specifically so that one may detect meaningful changes to the image via its tag in the + special case where the package's output includes the revision of the source code (e.g. for + displaying the version to the user). + + Without special processing, this kind of package would cause the OCI image tag to change + on each new revision whether the actual contents of the image changed or not. Combined + with `std.incl`, one may have a very strong indicator for when the contents of the image + actually includes meaningful changes which avoids flooding the remote registry with superflous + copies. + + Args: + target: Same as the first argument to upstream `callPackage`. + args: Arguments to `callPackage`. + + Returns: + The package with a clone of itself in the passthru where the expected revision is set to + "not-a-commit" for later use by `revise` & `reviseOCI`. + */ + target: args @ { + rev, + callPackage ? nixpkgs.callPackage, + ... + }: let + pkg = callPackage target (builtins.removeAttrs args ["callPackage"]); + in + if pkg ? sansrev + then pkg + else cell.ops.revise (_: _) pkg (_: _)