Override image tag of dockerTools.buildImage derivation

Hey folks,

I’m looking into building container images with nix and I can’t figure out how to do something that I expected to be easy.

{
  description = "test";

  outputs = { self, nixpkgs }:
  let
    pkgs = nixpkgs.legacyPackages.x86_64-linux;
  in {

    packages.x86_64-linux = {
      docker = pkgs.dockerTools.buildImage {
        name = "test-cowsay";
        config = {
          EntryPoint = [ "${pkgs.cowsay}/bin/cowsay" ];
        };
      };
      default = self.packages.x86_64-linux.docker.overrideAttrs (_: {
        # This doesn't work, and .override doesn't exist
        imageTag = "latest";
        tag = "latest";
      });
    };
  };
}

I want .#docker to be tagged with the nix hash and .#default to be tagged with latest, but overrideAttrs doesn’t work, afaict because tag isn’t actually passed to mkDerivation.

Is this functionality missing from dockerTools? I’m surely not the first to try this, am I?

I also tried this:

default = pkgs.dockerTools.buildImage {
  name = self.packages.x86_64-linux.docker.imageName;
  fromImage = self.packages.x86_64-linux.docker;
  tag = "latest";
};

but afaict that would require me to repeat the whole config. (Docker says “No command specified”.) Seems tedious and unidiomatic.

A friend pointed me towards makeOverridable, which is exactly what I need in this situation.

This works

{
  description = "test";

  outputs = { self, nixpkgs }:
  let
    pkgs = nixpkgs.legacyPackages.x86_64-linux;
  in {

    packages.x86_64-linux = {
      docker = pkgs.makeOverridable pkgs.dockerTools.buildImage {
        name = "test-cowsay";
        config = {
          EntryPoint = [ "${pkgs.cowsay}/bin/cowsay" ];
        };
      };
      docker-latest = self.packages.x86_64-linux.docker.override (_: {
        tag = "latest";
      });
    };
  };
}

As far as I understand it, makeOverridable is basically this:

fun: args:
  let
    result = fun args;
    override = newArgs: fun (args // newArgs);
  in
    result // { inherit override; }

(The actual function does more and I’m sure this one is subtly wrong, consider it a snapshot of my growing understanding)