]> git.immae.eu Git - perso/Immae/Config/Nix.git/blame - virtual/modules/websites/commons/composer-env.nix
Purify Florian's website
[perso/Immae/Config/Nix.git] / virtual / modules / websites / commons / composer-env.nix
CommitLineData
d78128ec
IB
1# This file originates from composer2nix
2
3{ stdenv, writeTextFile, fetchurl, php, unzip }:
4
5let
6 composer = stdenv.mkDerivation {
7 name = "composer-1.8.0";
8 src = fetchurl {
9 url = https://github.com/composer/composer/releases/download/1.8.0/composer.phar;
10 sha256 = "19pg9ip2mpyf5cyq34fld7qwl77mshqw3c4nif7sxmpnar6sh089";
11 };
12 buildInputs = [ php ];
13
14 # We must wrap the composer.phar because of the impure shebang.
15 # We cannot use patchShebangs because the executable verifies its own integrity and will detect that somebody has tampered with it.
16
17 buildCommand = ''
18 # Copy phar file
19 mkdir -p $out/share/php
20 cp $src $out/share/php/composer.phar
21 chmod 755 $out/share/php/composer.phar
22
23 # Create wrapper executable
24 mkdir -p $out/bin
25 cat > $out/bin/composer <<EOF
26 #! ${stdenv.shell} -e
27 exec ${php}/bin/php $out/share/php/composer.phar "\$@"
28 EOF
29 chmod +x $out/bin/composer
30 '';
31 meta = {
32 description = "Dependency Manager for PHP";
33 #license = stdenv.licenses.mit;
34 maintainers = [ stdenv.lib.maintainers.sander ];
35 platforms = stdenv.lib.platforms.unix;
36 };
37 };
38
39 buildZipPackage = { name, src }:
40 stdenv.mkDerivation {
41 inherit name src;
42 buildInputs = [ unzip ];
43 buildCommand = ''
44 unzip $src
45 baseDir=$(find . -type d -mindepth 1 -maxdepth 1)
46 cd $baseDir
47 mkdir -p $out
48 mv * $out
49 '';
50 };
51
52 buildPackage =
53 { name
54 , src
55 , packages ? {}
56 , devPackages ? {}
57 , buildInputs ? []
58 , symlinkDependencies ? false
59 , executable ? false
60 , removeComposerArtifacts ? false
61 , postInstall ? ""
62 , preInstall ? ""
63 , noDev ? false
64 , unpackPhase ? "true"
65 , buildPhase ? "true"
66 , ...}@args:
67
68 let
69 reconstructInstalled = writeTextFile {
70 name = "reconstructinstalled.php";
71 executable = true;
72 text = ''
73 #! ${php}/bin/php
74 <?php
75 if(file_exists($argv[1]))
76 {
77 $composerLockStr = file_get_contents($argv[1]);
78
79 if($composerLockStr === false)
80 {
81 fwrite(STDERR, "Cannot open composer.lock contents\n");
82 exit(1);
83 }
84 else
85 {
86 $config = json_decode($composerLockStr, true);
87
88 if(array_key_exists("packages", $config))
89 $allPackages = $config["packages"];
90 else
91 $allPackages = array();
92
93 ${stdenv.lib.optionalString (!noDev) ''
94 if(array_key_exists("packages-dev", $config))
95 $allPackages = array_merge($allPackages, $config["packages-dev"]);
96 ''}
97
98 $packagesStr = json_encode($allPackages, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
99 print($packagesStr);
100 }
101 }
102 else
103 print("[]");
104 ?>
105 '';
106 };
107
108 constructBin = writeTextFile {
109 name = "constructbin.php";
110 executable = true;
111 text = ''
112 #! ${php}/bin/php
113 <?php
114 $composerJSONStr = file_get_contents($argv[1]);
115
116 if($composerJSONStr === false)
117 {
118 fwrite(STDERR, "Cannot open composer.json contents\n");
119 exit(1);
120 }
121 else
122 {
123 $config = json_decode($composerJSONStr, true);
124
125 if(array_key_exists("bin-dir", $config))
126 $binDir = $config["bin-dir"];
127 else
128 $binDir = "bin";
129
130 if(array_key_exists("bin", $config))
131 {
132 if(!file_exists("vendor/".$binDir))
133 mkdir("vendor/".$binDir);
134
135 foreach($config["bin"] as $bin)
136 symlink("../../".$bin, "vendor/".$binDir."/".basename($bin));
137 }
138 }
139 ?>
140 '';
141 };
142
143 bundleDependencies = dependencies:
144 stdenv.lib.concatMapStrings (dependencyName:
145 let
146 dependency = dependencies.${dependencyName};
147 in
148 ''
149 ${if dependency.targetDir == "" then ''
150 vendorDir="$(dirname ${dependencyName})"
151 mkdir -p "$vendorDir"
152 ${if symlinkDependencies then
153 ''ln -s "${dependency.src}" "$vendorDir/$(basename "${dependencyName}")"''
154 else
155 ''cp -av "${dependency.src}" "$vendorDir/$(basename "${dependencyName}")"''
156 }
157 '' else ''
158 namespaceDir="${dependencyName}/$(dirname "${dependency.targetDir}")"
159 mkdir -p "$namespaceDir"
160 ${if symlinkDependencies then
161 ''ln -s "${dependency.src}" "$namespaceDir/$(basename "${dependency.targetDir}")"''
162 else
163 ''cp -av "${dependency.src}" "$namespaceDir/$(basename "${dependency.targetDir}")"''
164 }
165 ''}
166 '') (builtins.attrNames dependencies);
167
168 extraArgs = removeAttrs args [ "name" "packages" "devPackages" "buildInputs" ];
169 in
170 stdenv.mkDerivation ({
171 name = "composer-${name}";
172 buildInputs = [ php composer ] ++ buildInputs;
173
174 inherit unpackPhase buildPhase;
175
176 installPhase = ''
177 ${if executable then ''
178 mkdir -p $out/share/php
179 cp -av $src $out/share/php/$name
180 chmod -R u+w $out/share/php/$name
181 cd $out/share/php/$name
182 '' else ''
183 cp -av $src $out
184 chmod -R u+w $out
185 cd $out
186 ''}
187
188 # Execute pre install hook
189 runHook preInstall
190
191 # Remove unwanted files
192 rm -f *.nix
193
194 export HOME=$TMPDIR
195
196 # Remove the provided vendor folder if it exists
197 rm -Rf vendor
198
199 # If there is no composer.lock file, compose a dummy file.
200 # Otherwise, composer attempts to download the package.json file from
201 # the registry which we do not want.
202 if [ ! -f composer.lock ]
203 then
204 cat > composer.lock <<EOF
205 {
206 "packages": []
207 }
208 EOF
209 fi
210
211 # Reconstruct the installed.json file from the lock file
212 mkdir -p vendor/composer
213 ${reconstructInstalled} composer.lock > vendor/composer/installed.json
214
215 # Copy or symlink the provided dependencies
216 cd vendor
217 ${bundleDependencies packages}
218 ${stdenv.lib.optionalString (!noDev) (bundleDependencies devPackages)}
219 cd ..
220
221 # Reconstruct autoload scripts
222 # We use the optimize feature because Nix packages cannot change after they have been built
223 # Using the dynamic loader for a Nix package is useless since there is nothing to dynamically reload.
224 composer dump-autoload --optimize ${stdenv.lib.optionalString noDev "--no-dev"}
225
226 # Run the install step as a validation to confirm that everything works out as expected
227 composer install --optimize-autoloader ${stdenv.lib.optionalString noDev "--no-dev"}
228
229 ${stdenv.lib.optionalString executable ''
230 # Reconstruct the bin/ folder if we deploy an executable project
231 ${constructBin} composer.json
232 ln -s $(pwd)/vendor/bin $out/bin
233 ''}
234
235 ${stdenv.lib.optionalString (!symlinkDependencies) ''
236 # Patch the shebangs if possible
237 if [ -d $(pwd)/vendor/bin ]
238 then
239 # Look for all executables in bin/
240 for i in $(pwd)/vendor/bin/*
241 do
242 # Look for their location
243 realFile=$(readlink -f "$i")
244
245 # Restore write permissions
246 chmod u+wx "$(dirname "$realFile")"
247 chmod u+w "$realFile"
248
249 # Patch shebang
250 sed -e "s|#!/usr/bin/php|#!${php}/bin/php|" \
251 -e "s|#!/usr/bin/env php|#!${php}/bin/php|" \
252 "$realFile" > tmp
253 mv tmp "$realFile"
254 chmod u+x "$realFile"
255 done
256 fi
257 ''}
258
259 if [ "$removeComposerArtifacts" = "1" ]
260 then
261 # Remove composer stuff
262 rm -f composer.json composer.lock
263 fi
264
265 # Execute post install hook
266 runHook postInstall
267 '';
268 } // extraArgs);
269in
270{
271 composer = stdenv.lib.makeOverridable composer;
272 buildZipPackage = stdenv.lib.makeOverridable buildZipPackage;
273 buildPackage = stdenv.lib.makeOverridable buildPackage;
274}