1# callPackage args
2{
3 lib,
4 stdenv,
5 go,
6 xcaddy,
7 cacert,
8 git,
9 caddy,
10}:
11
12let
13 inherit (builtins) hashString;
14 inherit (lib)
15 assertMsg
16 concatMapStrings
17 elemAt
18 fakeHash
19 filter
20 hasInfix
21 length
22 lessThan
23 sort
24 toShellVar
25 ;
26in
27
28# pkgs.caddy.withPlugins args
29{
30 plugins,
31 hash ? fakeHash,
32 doInstallCheck ? true,
33}:
34
35let
36 pluginsSorted = sort lessThan plugins;
37 pluginsList = concatMapStrings (plugin: "${plugin}-") pluginsSorted;
38 pluginsHash = hashString "md5" pluginsList;
39 pluginsWithoutVersion = filter (p: !hasInfix "@" p) pluginsSorted;
40in
41
42# eval barrier: user provided plugins must have tags
43# the go module must either be tagged in upstream repo
44# or user must provide commit sha or a pseudo-version number
45# https://go.dev/doc/modules/version-numbers#pseudo-version-number
46assert assertMsg (
47 length pluginsWithoutVersion == 0
48) "Plugins must have tags present (e.g. ${elemAt pluginsWithoutVersion 0}@x.y.z)!";
49
50caddy.overrideAttrs (
51 finalAttrs: prevAttrs: {
52 vendorHash = null;
53 subPackages = [ "." ];
54
55 src = stdenv.mkDerivation {
56 pname = "caddy-src-with-plugins-${pluginsHash}";
57 version = finalAttrs.version;
58
59 nativeBuildInputs = [
60 go
61 xcaddy
62 cacert
63 git
64 ];
65 dontUnpack = true;
66 buildPhase =
67 let
68 withArgs = concatMapStrings (plugin: "--with ${plugin} ") pluginsSorted;
69 in
70 ''
71 export GOCACHE=$TMPDIR/go-cache
72 export GOPATH="$TMPDIR/go"
73 XCADDY_SKIP_BUILD=1 TMPDIR="$PWD" xcaddy build v${finalAttrs.version} ${withArgs}
74 (cd buildenv* && go mod vendor)
75 '';
76 installPhase = ''
77 mv buildenv* $out
78 '';
79
80 outputHashMode = "recursive";
81 outputHash = hash;
82 outputHashAlgo = "sha256";
83 };
84
85 # xcaddy built output always uses pseudo-version number
86 # we enforce user provided plugins are present and have matching tags here
87 inherit doInstallCheck;
88 installCheckPhase = ''
89 runHook preInstallCheck
90
91 declare -A modules errors
92 ${toShellVar "notfound" pluginsSorted}
93
94 # put build info that we care about into `modules` list
95 while read -r kind module version _; do
96 case "$kind" in
97 'dep'|'=>')
98 modules[$module]=$version
99 ;;
100 *)
101 # we only care about 'dep' and '=>' directives for now
102 ;;
103 esac
104 done < <($out/bin/caddy build-info)
105
106 # compare build-time (Nix side) against runtime (Caddy side)
107 for spec in "''${notfound[@]}"; do
108 if [[ $spec == *=* ]]; then
109 # orig=repl_mod@repl_ver
110 orig=''${spec%%=*}
111 repl=''${spec#*=}
112 repl_mod=''${repl%@*}
113 repl_ver=''${repl#*@}
114
115 if [[ -z ''${modules[$orig]} ]]; then
116 errors[$spec]="plugin \"$spec\" with replacement not found in build info:\n reason: \"$orig\" missing"
117 elif [[ -z ''${modules[$repl_mod]} ]]; then
118 errors[$spec]="plugin \"$spec\" with replacement not found in build info:\n reason: \"$repl_mod\" missing"
119 elif [[ "''${modules[$repl_mod]}" != "$repl_ver" ]]; then
120 errors[$spec]="plugin \"$spec\" have incorrect tag:\n specified: \"$spec\"\n got: \"$orig=$repl_mod@''${modules[$repl_mod]}\""
121 fi
122 else
123 # mod@ver
124 mod=''${spec%@*}
125 ver=''${spec#*@}
126
127 if [[ -z ''${modules[$mod]} ]]; then
128 errors[$spec]="plugin \"$spec\" not found in build info"
129 elif [[ "''${modules[$mod]}" != "$ver" ]]; then
130 errors[$spec]="plugin \"$spec\" have incorrect tag:\n specified: \"$spec\"\n got: \"$mod@''${modules[$mod]}\""
131 fi
132 fi
133 done
134
135 # print errors if any
136 if [[ ''${#errors[@]} -gt 0 ]]; then
137 for spec in "''${!errors[@]}"; do
138 printf "Error: ''${errors[$spec]}\n" >&2
139 done
140
141 echo "Tips:"
142 echo "If:"
143 echo " - you are using module replacement (e.g. \`plugin1=plugin2@version\`)"
144 echo " - the provided Caddy plugin is under a repository's subdirectory, and \`go.{mod,sum}\` files are not in that subdirectory"
145 echo " - you have custom build logic or other advanced use cases"
146 echo "Please consider:"
147 echo " - set \`doInstallCheck = false\`"
148 echo " - write your own \`installCheckPhase\` and override the default script"
149 echo "If you are sure this error is caused by packaging, or by caddy/xcaddy, raise an issue with upstream or nixpkgs"
150
151 exit 1
152 fi
153
154 runHook postInstallCheck
155 '';
156 }
157)