Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

caddy: add all addons #14671

Open
davidak opened this issue Apr 13, 2016 · 69 comments
Open

caddy: add all addons #14671

davidak opened this issue Apr 13, 2016 · 69 comments
Labels
0.kind: enhancement Add something new

Comments

@davidak
Copy link
Member

davidak commented Apr 13, 2016

compile caddy with all addons listed on the download page.

Only middleware handlers which are invoked from the Caddyfile will be chained in, so small Caddyfiles are very fast and efficient.

source: Directive section on https://caddyserver.com/docs/caddyfile

a later improvement could be to select just the addons you want like vim_configurable.

@angus-g

@angus-g
Copy link
Contributor

angus-g commented Apr 14, 2016

This does sound like a good idea to improve the usefulness of caddy, but it's quite non-trivial from what I can see. It looks like there's some code generation required (in https://github.com/caddyserver/caddydev/blob/master/caddybuild/build.go) to insert the custom directives into caddy.

If that's the case, it would probably be easier to start with desired addons and do the code generation at build time. I expect this would need a bit more Nix code than currently exists for the package in order to support hashes for caddy and all addons, then modifying and building the source after this. I'm not great with the language just yet, so I'm not even sure of the feasibility of this. (Anything's possible, right?)

@davidak
Copy link
Member Author

davidak commented Apr 14, 2016

I found a Dockerfile where someone installed caddy with the git addon. That is also the one i want to use.

https://github.com/jumanjihouse/docker-caddy/blob/master/Dockerfile.build#L34

You could write the nix-expression like for a general program, but the go infrastructure is different.
You should ask in the IRC or Mailinglist how to realize that best. I'm also not that experienced with nix, never packaged a go program.

@davidak
Copy link
Member Author

davidak commented Apr 15, 2016

you can override build phases like described here https://nixos.org/nixpkgs/manual/#ssec-controlling-phases

for example the preBuildPhases like here:

then you can use caddyext install git in the build phase.

if you are experienced with go, you can do that. i could also figure it out and create a PR. how you like :)

@davidak
Copy link
Member Author

davidak commented Apr 17, 2016

we can also create an additional package of caddyext and use it as caddy buildInputs.

but i see 2 problems:

  • the hash of the addon source is not checked
  • reproducible builds are impossible since the source of the addons change over time

@0xABAB
Copy link
Contributor

0xABAB commented Apr 17, 2016

If the source of the addons changes over time, I would try to convince upstream to change their ways and otherwise completely ignore this project, for a lack of professional software development practices.

Sometimes "gratis" software is even too costly.

@davidak
Copy link
Member Author

davidak commented Apr 17, 2016

@0xABAB you may misunderstood me. caddy has it's own "package manager" for addons.

if i execute caddyext install git now and again in one year, i will get a different version. if we build our nix package this way, it will not be reproducible.

i asked the developer of caddyext if it can install from local directory. then we can clone the addons repository and checkout a specific version.

Caddy is free software.

@davidak
Copy link
Member Author

davidak commented Jun 16, 2016

in version 0.9 addons will be plugins. i havn't looked what that means for building from source.

https://forum.caddyserver.com/t/caddy-0-9-beta-version-available/146?u=matt

we should wait for 0.9 to integrate the fix for this issue into that.

@joepie91
Copy link
Contributor

Any updates on this?

@bricewge
Copy link
Contributor

bricewge commented Oct 1, 2017

It is now quite easy to add plugins in caddy: https://github.com/mholt/caddy/wiki/Plugging-in-Plugins-Yourself
But I don't know how to make the build reproducible. It seems that at most the major version can be specified , not a specific version or a commit. For example adding _ "gopkg.in/restic/caddy.v0" will for the moment use the v0.2 as explained here.

@bricewge
Copy link
Contributor

I managed to add the restic plugin to caddy by adding thoses lines to the current derivation:

    preConfigure = ''
        substituteInPlace ./caddy/caddymain/run.go \
        --replace '// This is where other plugins get plugged in (imported)' \
        '_ "github.com/restic/caddy"'
    '';
    goDeps = ./deps.nix;

And adding deps.nix:

[
  {
    goPackagePath = "github.com/restic/caddy";
    fetch = {
      type = "git";
      url = "https://github.com/restic/caddy";
      rev = "v0.2.0";
      sha256 = "0wjhbnm405vdpf3jwi9dzhz6nd5rhmxqibsa1nx2iyq43bc3p6sk";
    };
  }
  {
    goPackagePath = "github.com/restic/rest-server";
    fetch = {
      type = "git";
      url = "https://github.com/restic/rest-server";
      rev = "v0.9.3";
      sha256 = "0f6i952iy4arnc96wpayvh9wa1mdw7vprwwbnjxwj09jvifqd0hp";
    };
  }
]

So it is now really easy to add some plugins to caddy! Whoever I don't know in which form this configuration should be available in NixOS? Maybe having two versions would be the easiest:

  • caddy as it currently is, without any plugins
  • caddy-full with all the plugins availaible

@davidak
Copy link
Member Author

davidak commented Mar 25, 2018

I think that 2 versions would be OK.

Another option would be to enable each plugin like described for Chromium and Firefox here https://nixos.wiki/wiki/Chromium

@JakeStanger
Copy link
Contributor

The above method for me isn't working. Has anybody had any luck or got a better approach?

@CMCDragonkai
Copy link
Member

CMCDragonkai commented Dec 19, 2019

According to the https://github.com/caddyserver/caddy/wiki/Plugging-in-Plugins-Yourself

The current derivation is at: https://github.com/NixOS/nixpkgs/blob/master/pkgs/servers/caddy/default.nix

All we need to do is is something like:

{ caddy }:

caddy.overrideAttrs (attrs: {
  preBuild = ''
    cat << EOF > caddy/main.go
    package main
    import "github.com/caddyserver/caddy/caddy/caddymain"
    import _ "github.com/techknowlogick/caddy-s3browser"
    func main() {
      caddymain.EnableTelemetry = false
      caddymain.Run()
    }
    EOF
  '';
})

Which I override the preBuild to write our own main.go to be used.

You can see that I added in:

    import _ "github.com/techknowlogick/caddy-s3browser"

However that dependency is not being brought into the buildGoModule.

Normally with buildGoPackage, I'd add extra things to deps.nix. However here, this is buildGoModule, so how do we add caddy-s3browser as an extra dependency to buildGoModule?

@CMCDragonkai
Copy link
Member

CMCDragonkai commented Dec 19, 2019

There's a overrideModAttrs in the buildGoPackage function. However I can't seem to access that in the resulting derivation. As in pkgs.caddy.override cannot access overrideModAttrs and neither can pkgs.caddy.overrideAttrs.

Instead it seems like it is needed to override the go-modules derivation located in pkgs.caddy.go-modules.

No documentation on how to override the go-modules derivation to add an extra go package into the go-modules.

@CMCDragonkai
Copy link
Member

CMCDragonkai commented Dec 19, 2019

I think what needs to be done is that the go-modules.buildPhase needs to be overridden. Like a preBuild hook.

Because that's when go mod download is called.

Either that or the go.mod must have a patch applied to add that in prior to the source being used. I'm wondering if go.sum also needs a patch.

This does not work even if I update go.mod and go.sum. I think this is not working for overriding caddy. It seems some thing more drastic required.

The buildGoModule builder needs to have a better way of adding extra go dependencies.

@CMCDragonkai
Copy link
Member

Abandoned trying to do overrides. I just had to create my own Go package to do this. But in the future, caddy needs some work to be able to compile plugins more easily like how firefox/chrome does its plugins.

@ooesili
Copy link
Contributor

ooesili commented Feb 19, 2020

@CMCDragonkai Hey there! I couldn't get overrides to work either, but I ended up with this and thought I'd share. I ran into some issues with nix caching old versions of the module after tweaking the derivation but leaving modSha256 the same as before. Nix thought it had already built those modules even though the inputs technically changed. It tripped me up for a while, maybe it happened to you too? Anyways here's my local fork of caddy:

with pkgs; buildGoModule rec {
  pname = "caddy";
  version = "1.0.3";

  goPackagePath = "github.com/caddyserver/caddy";

  subPackages = [ "caddy" ];

  src = fetchFromGitHub {
    owner = "caddyserver";
    repo = pname;
    rev = "v${version}";
    sha256 = "1n7i9w4vva5x5wry7gzkyfylk39x40ykv7ypf1ca3zbbk7w5x6mw";
  };
  modSha256 = "1xs5qf89sddj21vig4lmhdfkyccf2alrd39915spz845xhv3pl8w";

  overrideModAttrs = (old: {
    preBuild = ''
      go mod edit -require=github.com/tarent/[email protected]
      go mod edit -require=github.com/BTBurke/[email protected]+incompatible
    '';
  });

  preBuild = ''
      cat << EOF > caddy/main.go
      package main

      import (
        "github.com/caddyserver/caddy/caddy/caddymain"

        _ "github.com/BTBurke/caddy-jwt"
        _ "github.com/tarent/loginsrv/caddy"
      )

      func main() {
        caddymain.EnableTelemetry = false
        caddymain.Run()
      }
      EOF
  '';

  meta = with stdenv.lib; {
    homepage = https://caddyserver.com;
    description = "Fast, cross-platform HTTP/2 web server with automatic HTTPS";
    license = licenses.asl20;
    maintainers = with maintainers; [ rushmorem fpletz zimbatm ];
  };
}

@stale
Copy link

stale bot commented Aug 17, 2020

Hello, I'm a bot and I thank you in the name of the community for opening this issue.

To help our human contributors focus on the most-relevant reports, I check up on old issues to see if they're still relevant. This issue has had no activity for 180 days, and so I marked it as stale, but you can rest assured it will never be closed by a non-human.

The community would appreciate your effort in checking if the issue is still valid. If it isn't, please close it.

If the issue persists, and you'd like to remove the stale label, you simply need to leave a comment. Your comment can be as simple as "still important to me". If you'd like it to get more attention, you can ask for help by searching for maintainers and people that previously touched related code and @ mention them in a comment. You can use Git blame or GitHub's web interface on the relevant files to find them.

Lastly, you can always ask for help at our Discourse Forum or at #nixos' IRC channel.

@stale stale bot added the 2.status: stale https://github.com/NixOS/nixpkgs/blob/master/.github/STALE-BOT.md label Aug 17, 2020
@davidak
Copy link
Member Author

davidak commented Aug 17, 2020

this is still important

@stale stale bot removed the 2.status: stale https://github.com/NixOS/nixpkgs/blob/master/.github/STALE-BOT.md label Aug 17, 2020
@stale
Copy link

stale bot commented Feb 13, 2021

I marked this as stale due to inactivity. → More info

@stale stale bot added the 2.status: stale https://github.com/NixOS/nixpkgs/blob/master/.github/STALE-BOT.md label Feb 13, 2021
@Garionion
Copy link

this is still important

@ljani
Copy link
Contributor

ljani commented Sep 22, 2021

Here's my take based on #86349 (comment):

overlays/caddy/default.nix:

final: prev: {
  caddy = (prev.caddy.override {
    buildGoModule = args: prev.buildGoModule (args // {
      vendorSha256 = "sha256-445MYvi487ls6g6i30UTTK2/n2wbILgJEuwNUQE//ZE";
      patches = [ ./0001-plugins.patch ];
    });
  });
}

overlays/caddy/0001-plugins.patch:

diff --git a/cmd/caddy/main.go b/cmd/caddy/main.go
index 2383546..59e467e 100644
--- a/cmd/caddy/main.go
+++ b/cmd/caddy/main.go
@@ -31,6 +31,7 @@ import (

        // plug in Caddy modules here
        _ "github.com/caddyserver/caddy/v2/modules/standard"
+       _ "github.com/caddy-dns/duckdns"
 )

 func main() {
diff --git a/go.mod b/go.mod
index 6fe6902..68bfedd 100644
--- a/go.mod
+++ b/go.mod
@@ -6,6 +6,7 @@ require (
        github.com/Masterminds/sprig/v3 v3.1.0
        github.com/alecthomas/chroma v0.8.2
        github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a
+       github.com/caddy-dns/duckdns v0.3.1
        github.com/caddyserver/certmagic v0.13.1
        github.com/dustin/go-humanize v1.0.1-0.20200219035652-afde56e7acac
        github.com/go-chi/chi v4.1.2+incompatible

Not exactly pretty, but at least it works:

> /nix/store/aly0qbnxh2drph0gmbbmrcrrijc2m809-caddy-2.4.1/bin/caddy build-info | grep duckdns
github.com/caddy-dns/duckdns v0.3.1
github.com/libdns/duckdns v0.1.1

@stale stale bot removed the 2.status: stale https://github.com/NixOS/nixpkgs/blob/master/.github/STALE-BOT.md label Sep 22, 2021
@curbengh
Copy link

I find #89268 (comment) works better than previous comment's because it is more caddy-version-agnostic.

@phaer
Copy link
Member

phaer commented Jan 19, 2022

Just in case, somebody else who isn't too familiar with nix golang infrastructure is trying to build a current Caddy with plugins: I've updated the package file by @diamondburned in #89268 (comment). Uses fetchFromGitHub instead of fetchGit and declares the current maintainer. I also needed to set "runVend = true" to get all plugin dependencies

pkgs/caddy.nix
{ lib, fetchFromGitHub, buildGoModule, plugins ? [], vendorSha256 ? "" }:

with lib;

let imports = flip concatMapStrings plugins (pkg: "\t\t\t_ \"${pkg}\"\n");

	main = ''
		package main

		import (
			caddycmd "github.com/caddyserver/caddy/v2/cmd"

			_ "github.com/caddyserver/caddy/v2/modules/standard"
${imports}
		)

		func main() {
			caddycmd.Main()
		}
	'';


in buildGoModule rec {
	pname = "caddy";
	version = "2.4.6";
  runVend = true;
	subPackages = [ "cmd/caddy" ];

  src = fetchFromGitHub {
    owner = "caddyserver";
    repo = "caddy";
    rev = "v${version}";
    sha256 = "sha256-xNCxzoNpXkj8WF9+kYJfO18ux8/OhxygkGjA49+Q4vY=";
  };

	inherit vendorSha256;

	overrideModAttrs = (_: {
		preBuild    = "echo '${main}' > cmd/caddy/main.go";
		postInstall = "cp go.sum go.mod $out/ && ls $out/";
	});

	postPatch = ''
		echo '${main}' > cmd/caddy/main.go
		cat cmd/caddy/main.go
	'';

	postConfigure = ''
		cp vendor/go.sum ./
		cp vendor/go.mod ./
	'';

	meta = {
		homepage = https://caddyserver.com;
		description = "Fast, cross-platform HTTP/2 web server with automatic HTTPS";
		license = licenses.asl20;
    maintainers = with maintainers; [ Br1ght0ne ];
	};
}
example.nix
pkgs.callPackage ./pkgs/caddy.nix {
          plugins = [
            "github.com/mholt/caddy-webdav"
            "github.com/greenpau/caddy-git"
            "github.com/greenpau/caddy-auth-portal"
            "github.com/greenpau/caddy-authorize"
          ];
          vendorSha256 = "sha256-K2i9MlrR8PMDHHW7V+d5mRgtgb0H5okMz/TlEncvgIo=";
        }

@shadowrylander
Copy link

shadowrylander commented Feb 5, 2022

@phaer So should I add this to my list of overlays? Also, how would I go about getting the vendorSha256, if I really need it?

@MROvaiz
Copy link

MROvaiz commented Aug 27, 2024

how do i use plugin?
do i have to compile anything? any doc?

@ntninja
Copy link
Contributor

ntninja commented Aug 27, 2024

@MROvaiz If you’ve never used overlays before it goes like this:

  1. Save the contents of https://raw.githubusercontent.com/jpds/nixpkgs/caddy-external-plugins/pkgs/servers/caddy/default.nix as /etc/nixos/overlay/caddy-custom/default.nix
  2. Create an overlay function in file /etc/nixos/overlay/default.nix (this can be extended for adding other external packages later if you want):
final: prev:

{
   # Updated Caddy with support for including extra modules
   #
   # Source: https://github.com/NixOS/nixpkgs/pull/259275
   caddy-custom = prev.callPackage ./caddy-custom {};
}
  1. Reference the overlay function from /etc/configuration.nix:
{
   # … other settings …

   # System package customization
   nixpkgs.overlays = [
      (import ./overlay)
   ];

   # … other settings …
}
  1. Override the Caddy derivation used by the Caddy service in whatever file you’re setting services.caddy (can also be in /etc/configuration.nix):
{
   services.caddy = {
      package = pkgs.caddy-custom.override {
         externalPlugins = [
            {name = "transform-encoder"; repo = "github.com/caddyserver/transform-encoder"; version = "58ebafa572d531b301fdbc6e2fd139766bac7e8d";}
            {name = "connegmatcher";     repo = "github.com/mpilhlt/caddy-conneg";          version = "v0.1.4";}
         ] ++ (
            # Caddy Layer4 modules
            lib.lists.map (name: {
               inherit name;
               repo = "github.com/mholt/caddy-l4";
               version = "f3a880d4c01c884f4a096ccceb6c6d1e2d1d983d";
            }) ["layer4" "modules/l4proxy" "modules/l4tls" "modules/l4proxyprotocol"]
         );
         vendorHash = "sha256-7cRI65foALEsfYhvdGresq7oma/cIsnVtbq+Gan5DCU=";
      };

      # … other caddy settings …
  };
}

Hope this helps!

@MROvaiz
Copy link

MROvaiz commented Aug 27, 2024

@ntninja Thank you, it's working.
so I'm using yours overlay package build derivation.
so if there is an update in caddy, what do i have to do?
or i have to wait for this derivation be merge with nixpkg git?

@airone01
Copy link

PR @ #317881

@crabdancing
Copy link

@ntninja I've tried this, but it's not working for me. I recreated the problem in a flake for easier debugging:

https://github.com/crabdancing/nixos-caddy-with-plugins

It says:

warning: creating lock file '/home/ada/projects/nixos-caddy-with-plugins/flake.lock'
error: builder for '/nix/store/apjbwk230vf97ckwcl8rqa7dd7lqln3q-caddy-2.7.5.drv' failed with exit code 1;
       last 10 log lines:
       > Running phase: unpackPhase
       > unpacking source archive /nix/store/6qhv0b3ripxgjyw8jhibqmn1i3r91bzs-source
       > source root is source
       > Running phase: patchPhase
       > Running phase: updateAutotoolsGnuConfigScriptsPhase
       > Running phase: configurePhase
       > Running phase: buildPhase
       > Building subPackage ./cmd/caddy
       > # github.com/caddyserver/caddy/v2/modules/caddytls
       > modules/caddytls/automation.go:254:18: cannot use func(name string) error {…} (value of type func(name string) error) as func(ctx context.Context, name string) error value in struct literal
       For full logs, run 'nix log /nix/store/apjbwk230vf97ckwcl8rqa7dd7lqln3q-caddy-2.7.5.drv'.

Does anyone have any ideas?

@airone01
Copy link

airone01 commented Aug 28, 2024

@crabdancing

Now it should work. Again this is a hack.

Edit: this does not actually work if you use it in an overlay, but crabdancing made a nice flake (see comment below) which I'm using in my setup. If you are coming from Google, Ignore my response above and head over to her flake, or wait for the PR to be merged 👍

@crabdancing
Copy link

crabdancing commented Aug 29, 2024

@airone01 Thanks so much! I'm very happy with my packaging for this now. :) It may be a hack, but it's the least hacky hack that we have for now.

For anyone struggling with using caddy-l4 or other Caddy modules / Caddy plugins declaratively on NixOS, I wrote a flake you can use for it here. I hope it can help someone :)

@ntninja
Copy link
Contributor

ntninja commented Aug 31, 2024

@ntninja Thank you, it's working. so I'm using yours overlay package build derivation. so if there is an update in caddy, what do i have to do? or i have to wait for this derivation be merge with nixpkg git?

  1. Open /etc/nixos/overlay/caddy-custom/default.nix and change the version number and make the value of hash inside dist and src empty
  2. Next set the vendorHash value inside the services.caddy configuration also to empty
  3. Run nixos-rebuild switch --fast (it should print “warning: found empty hash, assuming 'sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA='” exactly three times!)
  4. The switch will fail but should print you the correct value for the hash field inside src as ”actual” value – copy that value
  5. Edit /etc/nixos/overlay/caddy-custom/default.nix again and change the hash value inside src to the copied value
  6. Run nixos-rebuild switch --fast again and repeat steps 3 and 4 for the hash value inside dist
  7. Run nixos-rebuild switch --fast again and use the “actual” value as vendorHash inside services.caddy
  8. Run nixos-rebuild switch --fast one last time and hopefully this time it should work

@crabdancing It’s probably that some module you’re using isn’t compatible with the older version of Caddy that was used in my guide, try repeating after doing the steps above for Caddy 2.8.4!
(Also your profile text speaks to me. 🐺)

@ntninja
Copy link
Contributor

ntninja commented Aug 31, 2024

(Also, I’ll try doing a write-up on how I think plugin support could be done in a NixPkgs-acceptable way later on. Just know I probably won’t have time to implement it myself in any reasonable timeframe.)

@Diti
Copy link

Diti commented Sep 7, 2024

It would be neat if we could use caddyPackages in nixpkgs the same way we use nodePackages, python3Packages or vimPlugins!

@airone01
Copy link

airone01 commented Sep 8, 2024

It would be neat if we could use caddyPackages in nixpkgs the same way we use nodePackages, python3Packages or vimPlugins!

Is it possible? Considering the modules are used at compile time I mean. I'm a newbie so correct me if I'm wrong.
Also if it is indeed possible, I'm down to becoming a maintainer for some plugins.

@ntninja
Copy link
Contributor

ntninja commented Sep 9, 2024

@airone01 Yes, it should be possible. Unfortunately, it's nontrivial to even explain the problem and how it can be solved (very in-depth Nix). But since there is obviously interest, I’ll try sketching it out soon (can’t right now, we don’t have much time per day, sorry) – someone else will need to fumble with Nix derivations to actually make it work though.

@ntninja
Copy link
Contributor

ntninja commented Sep 10, 2024

Background on derivations

Before trying to fix this it is important to understand the two major kinds of derivations in Nix: In general derivations take some kind of input and run some command (typically a shell script) that produces some desired output.

If you’ve ever written a derivation before, it was most-likely a so-called fixed-input derivation: You can specify/use some arbitrary inputs and some (shell) command(s); once all the required inputs are available the Nix program will then run the specified command(s) are run it in an isolated build environment with access only to the used inputs (and, in particular no network access!) – the so-called “sandbox” you might have seen mentioned. The idea here is for fixed-input derivations we expect the command(s) basically just be some arbitrary-complex transformation from the given inputs to whatever output the command(s) result in. Notably we do not need to specify what the output actually will be since we know (within some minor margin of error) that it will essentially just be some alternate representation of the input.

The other major type of derivation is the so-called fixed-output derivation: Again you specify/use some arbitrary inputs and some (shell) command(s), but this time you also specify the hash of the resulting output. Unlike fixed-input derivations, fixed-output derivation are not run in a fully isolated environment and can access the network – there is no need to limit their inputs after all since we know there is only one possible output in may produce! Notably the fetchFromGitHub function produces a fixed-output derivation that downloads an archive from some GitHub URL from the internet; that means it may return essentially arbitrary data, however since you also need to specify the expected hash we know GitHub cannot lie to us and sometimes return something else.

The currently suggested approach for Caddy plugins

If you look at the current suggestion for external modules in Caddy, you can see that the externalPlugins attributes is used in only two places: modBuildPhase and preBuild; both of these simply patch the Caddy main entry point in a special upstream-supported way to also reference any plugins you specify. (This is also what the xcaddy tool does internally.)

However this brings us to the actual problem in terms of NixPkgs packaging: All we did is add some references to these external plugins, we have not actually made their source code available locally at all! Since building Caddy itself uses a fixed-input derivation (see above) we’d now get errors as the Go tool attempts to download these dependencies from the internet – something a fixed-input derivation is not allowed to do.

The reason why the approach still kinda works is because we haven’t just patched the preBuild phase (which would result in the error mentioned above), but also the modBuildPhase: The modBuildPhase is fixed-output derivation (run before the fixed-input build phase) that uses the go mod vendor tool to download all the needed sources of all project dependencies and store them as derivation output – after patching the Caddy source code its list of dependencies includes both all the dependencies of Caddy itself, as well as all the dependencies of all the plugins it now uses!

There is a major inconvenience with this however: Since it’s a fixed-output derivation we had to specify the expected output hash of this step and this hash, the vendorHash, now covers both Caddy itself as well as all its added dependencies – and since users of the externalPlugins need to also specify an updated combined vendorHash this means that every time there is a minor update of Caddy in NixPkgs users’ systems will start encountering errors when upgrading!

A potential solution

Basically, what we need to do is treat each Caddy plugin as its own separate Go project that we run go mod vendor for: One fixed-output derivation for Caddy itself (as is currently in NixPkgs), then one extra fixed-output derivation using go mod vendor for each Caddy plugin. This means that for each plugin we also need to specify its own vendorHash and source, in addition to the Go module path that is currently specified. After downloading each vendor directory separately as fixed-output derivations, we then need to then merge them using a fixed-input derivation and use that as Go vendor directory when building Caddy – I’m not aware of any official way to merge Go vendor directories, but reading https://go.dev/src/README.vendor it appears that just running cp -r --reflink=auto "$PLUGIN_VENDOR_DIR" "$CADDY_VENDOR_DIR" for every plugin may be enough to “merge” them.

It should be possible to do that last step as part of configurePhase of the main Caddy build even, as long as the result ends up in a directory called vendor inside the Caddy sources being built.

So in to total it would be something like this:

  1. For each plugin:
    1. Download its sources (using a fetch* function)
    2. Run go mod vendor and capture the result in a fixed-output derivation (write it to $out)
  2. When building Caddy:
    1. Merge all plugin vendor directories into vendor/ during postConfigure
    2. Keep the part about patching Caddy in preBuild

… maybe, there are probably several catches and pitfalls. You’ll need to experiment to actually get it to work. (You can also look at the buildGoModule source code for inspiration: https://github.com/hsjobeki/nixpkgs/blob/ad513b9c4fdbdc2f48faafdc802817536cb9c9b6/pkgs/build-support/go/module.nix#L3:C1)

Good luck!

(@airone01 why 😄 ?)

@airone01
Copy link

airone01 commented Sep 10, 2024

@ntninja I thought it was a "happy" emoji but it's a "laugh" emoji. My bad

Also great explanation, it's really detailed and simple and really helps a Nix newbie like me to understand how it works behind the scene. Thanks for that!
I won't have time to code this month because of an entrance exam, but I'll get right back at it after.

@W1M0R
Copy link

W1M0R commented Sep 11, 2024

@ntninja Great explanation! Thanks, I've written many derivations before, but now it all makes sense to me.

@diamondburned
Copy link

@ntninja I think gomod2nix already does quite a lot of this, especially the combining dependency derivations into a single usable vendor directory. This could be a good reference as well.

@ntninja
Copy link
Contributor

ntninja commented Sep 15, 2024

@airone01 Ah, I see 😁

@diamondburned Yes, it does. I just looked it over and it sadly cannot be used from inside NixPkgs directly: You’d have to run it ahead-of-time for every Caddy release (can be done on every Caddy update using a PR), as well as for every plugin you’d like to support (meaning every plugin needs to be also packaged in NixPkgs ahead-of-time, since users would be required to locally run that tool each time they add or update one that is not pregenerated in NixPkgs)*. In addition that tool does not appear to actually support merging dependency derivations, that code would still need to be written regardless.

So based on this – while it certainly is a nicer solution than the current giant-vendor-directory approach – it seems the tool is mostly orthogonal to the problems the Caddy packaging has.

* The blog post has a background on why – technically this could also be solved using import from derivation, but that’s forbidden for NixPkgs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
0.kind: enhancement Add something new
Projects
None yet
Development

No branches or pull requests