From 988e5ee5e60acb88b1a8ee9beb6dc76dcadcf4e3 Mon Sep 17 00:00:00 2001 From: Oliver Davies Date: Fri, 15 Aug 2025 16:20:55 +0100 Subject: [PATCH] Run `composer2nix` --- nix/composer2nix/.gitignore | 1 + nix/composer2nix/composer-env.nix | 244 ++++++++++++++++++++++++++++++ nix/composer2nix/default.nix | 14 ++ nix/composer2nix/notes.txt | 0 nix/composer2nix/php-packages.nix | 105 +++++++++++++ 5 files changed, 364 insertions(+) create mode 100644 nix/composer2nix/composer-env.nix create mode 100644 nix/composer2nix/default.nix create mode 100644 nix/composer2nix/notes.txt create mode 100644 nix/composer2nix/php-packages.nix diff --git a/nix/composer2nix/.gitignore b/nix/composer2nix/.gitignore index 57872d0..ab1ab4b 100644 --- a/nix/composer2nix/.gitignore +++ b/nix/composer2nix/.gitignore @@ -1 +1,2 @@ +/result /vendor/ diff --git a/nix/composer2nix/composer-env.nix b/nix/composer2nix/composer-env.nix new file mode 100644 index 0000000..71714b7 --- /dev/null +++ b/nix/composer2nix/composer-env.nix @@ -0,0 +1,244 @@ +# This file originates from composer2nix + +{ stdenv, lib, writeTextFile, fetchurl, php, unzip, phpPackages }: + +let + inherit (phpPackages) composer; + + filterSrc = src: + builtins.filterSource (path: type: type != "directory" || (baseNameOf path != ".git" && baseNameOf path != ".git" && baseNameOf path != ".svn")) src; + + buildZipPackage = { name, src }: + stdenv.mkDerivation { + inherit name src; + nativeBuildInputs = [ unzip ]; + buildCommand = '' + shopt -s dotglob + unzip $src + baseDir=$(find . -type d -mindepth 1 -maxdepth 1) + cd $baseDir + mkdir -p $out + mv * $out + ''; + }; + + buildPackage = + { name + , src + , packages ? {} + , devPackages ? {} + , buildInputs ? [] + , symlinkDependencies ? false + , executable ? false + , removeComposerArtifacts ? false + , postInstall ? "" + , noDev ? false + , composerExtraArgs ? "" + , unpackPhase ? "true" + , buildPhase ? "true" + , ...}@args: + + let + reconstructInstalled = writeTextFile { + name = "reconstructinstalled.php"; + executable = true; + text = '' + #! ${php}/bin/php + + ''; + }; + + constructBin = writeTextFile { + name = "constructbin.php"; + executable = true; + text = '' + #! ${php}/bin/php + + ''; + }; + + bundleDependencies = dependencies: + lib.concatMapStrings (dependencyName: + let + dependency = dependencies.${dependencyName}; + in + '' + ${if dependency.targetDir == "" then '' + vendorDir="$(dirname ${dependencyName})" + mkdir -p "$vendorDir" + ${if symlinkDependencies then + ''ln -s "${dependency.src}" "$vendorDir/$(basename "${dependencyName}")"'' + else + ''cp -av "${dependency.src}" "$vendorDir/$(basename "${dependencyName}")"'' + } + '' else '' + namespaceDir="${dependencyName}/$(dirname "${dependency.targetDir}")" + mkdir -p "$namespaceDir" + ${if symlinkDependencies then + ''ln -s "${dependency.src}" "$namespaceDir/$(basename "${dependency.targetDir}")"'' + else + ''cp -av "${dependency.src}" "$namespaceDir/$(basename "${dependency.targetDir}")"'' + } + ''} + '') (builtins.attrNames dependencies); + + extraArgs = removeAttrs args [ "packages" "devPackages" "buildInputs" ]; + in + stdenv.mkDerivation ({ + buildInputs = [ php composer ] ++ buildInputs; + + inherit unpackPhase buildPhase; + + installPhase = '' + ${if executable then '' + mkdir -p $out/share/php + cp -av $src $out/share/php/$name + chmod -R u+w $out/share/php/$name + cd $out/share/php/$name + '' else '' + cp -av $src $out + chmod -R u+w $out + cd $out + ''} + + # Remove unwanted files + rm -f *.nix + + export HOME=$TMPDIR + + # Remove the provided vendor folder if it exists + rm -Rf vendor + + # If there is no composer.lock file, compose a dummy file. + # Otherwise, composer attempts to download the package.json file from + # the registry which we do not want. + if [ ! -f composer.lock ] + then + cat > composer.lock < vendor/composer/installed.json + + # Copy or symlink the provided dependencies + cd vendor + ${bundleDependencies packages} + ${lib.optionalString (!noDev) (bundleDependencies devPackages)} + cd .. + + # Reconstruct autoload scripts + # We use the optimize feature because Nix packages cannot change after they have been built + # Using the dynamic loader for a Nix package is useless since there is nothing to dynamically reload. + composer dump-autoload --optimize ${lib.optionalString noDev "--no-dev"} ${composerExtraArgs} + + # Run the install step as a validation to confirm that everything works out as expected + composer install --optimize-autoloader ${lib.optionalString noDev "--no-dev"} ${composerExtraArgs} + + ${lib.optionalString executable '' + # Reconstruct the bin/ folder if we deploy an executable project + ${php}/bin/php ${constructBin} composer.json + ln -s $(pwd)/vendor/bin $out/bin + ''} + + ${lib.optionalString (!symlinkDependencies) '' + # Patch the shebangs if possible + if [ -d $(pwd)/vendor/bin ] + then + # Look for all executables in bin/ + for i in $(pwd)/vendor/bin/* + do + # Look for their location + realFile=$(readlink -f "$i") + + # Restore write permissions + chmod u+wx "$(dirname "$realFile")" + chmod u+w "$realFile" + + # Patch shebang + sed -e "s|#!/usr/bin/php|#!${php}/bin/php|" \ + -e "s|#!/usr/bin/env php|#!${php}/bin/php|" \ + "$realFile" > tmp + mv tmp "$realFile" + chmod u+x "$realFile" + done + fi + ''} + + if [ "$removeComposerArtifacts" = "1" ] + then + # Remove composer stuff + rm -f composer.json composer.lock + fi + + # Execute post install hook + runHook postInstall + ''; + } // extraArgs); +in +{ + inherit filterSrc; + composer = lib.makeOverridable composer; + buildZipPackage = lib.makeOverridable buildZipPackage; + buildPackage = lib.makeOverridable buildPackage; +} diff --git a/nix/composer2nix/default.nix b/nix/composer2nix/default.nix new file mode 100644 index 0000000..2519bd8 --- /dev/null +++ b/nix/composer2nix/default.nix @@ -0,0 +1,14 @@ +{pkgs ? import { + inherit system; + }, system ? builtins.currentSystem, noDev ? false, php ? pkgs.php, phpPackages ? pkgs.phpPackages}: + +let + composerEnv = import ./composer-env.nix { + inherit (pkgs) stdenv lib writeTextFile fetchurl unzip; + inherit php phpPackages; + }; +in +import ./php-packages.nix { + inherit composerEnv noDev; + inherit (pkgs) fetchurl fetchgit fetchhg fetchsvn; +} diff --git a/nix/composer2nix/notes.txt b/nix/composer2nix/notes.txt new file mode 100644 index 0000000..e69de29 diff --git a/nix/composer2nix/php-packages.nix b/nix/composer2nix/php-packages.nix new file mode 100644 index 0000000..f534a3d --- /dev/null +++ b/nix/composer2nix/php-packages.nix @@ -0,0 +1,105 @@ +{composerEnv, fetchurl, fetchgit ? null, fetchhg ? null, fetchsvn ? null, noDev ? false}: + +let + packages = { + "psr/container" = { + targetDir = ""; + src = composerEnv.buildZipPackage { + name = "psr-container-c71ecc56dfe541dbd90c5360474fbc405f8d5963"; + src = fetchurl { + url = "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963"; + sha256 = "1mvan38yb65hwk68hl0p7jymwzr4zfnaxmwjbw7nj3rsknvga49i"; + }; + }; + }; + "symfony/console" = { + targetDir = ""; + src = composerEnv.buildZipPackage { + name = "symfony-console-5f360ebc65c55265a74d23d7fe27f957870158a1"; + src = fetchurl { + url = "https://api.github.com/repos/symfony/console/zipball/5f360ebc65c55265a74d23d7fe27f957870158a1"; + sha256 = "0y4lq8hd71pg2jwfkrmzhcy85z2rpc05wk43x9sq20pdw312fg0d"; + }; + }; + }; + "symfony/deprecation-contracts" = { + targetDir = ""; + src = composerEnv.buildZipPackage { + name = "symfony-deprecation-contracts-63afe740e99a13ba87ec199bb07bbdee937a5b62"; + src = fetchurl { + url = "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62"; + sha256 = "1blzjsmk38b36l15khbx2qs3c6xqmfp32l9xxq3305ifshw7ldby"; + }; + }; + }; + "symfony/polyfill-ctype" = { + targetDir = ""; + src = composerEnv.buildZipPackage { + name = "symfony-polyfill-ctype-a3cc8b044a6ea513310cbd48ef7333b384945638"; + src = fetchurl { + url = "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638"; + sha256 = "1gwalz2r31bfqldkqhw8cbxybmc1pkg74kvg07binkhk531gjqdn"; + }; + }; + }; + "symfony/polyfill-intl-grapheme" = { + targetDir = ""; + src = composerEnv.buildZipPackage { + name = "symfony-polyfill-intl-grapheme-b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe"; + src = fetchurl { + url = "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe"; + sha256 = "06kbz2rqp0kyxpry055pciv02ihl7vgygigqhdqqy6q7aphg9i9a"; + }; + }; + }; + "symfony/polyfill-intl-normalizer" = { + targetDir = ""; + src = composerEnv.buildZipPackage { + name = "symfony-polyfill-intl-normalizer-3833d7255cc303546435cb650316bff708a1c75c"; + src = fetchurl { + url = "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c"; + sha256 = "0qrq26nw97xfcl0p8x4ria46lk47k73vjjyqiklpw8w5cbibsfxj"; + }; + }; + }; + "symfony/polyfill-mbstring" = { + targetDir = ""; + src = composerEnv.buildZipPackage { + name = "symfony-polyfill-mbstring-6d857f4d76bd4b343eac26d6b539585d2bc56493"; + src = fetchurl { + url = "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493"; + sha256 = "0g9a4jdc0gf7vilvz1yfyzj83bisaqa6j4sz0j9arwjzlc1p2708"; + }; + }; + }; + "symfony/service-contracts" = { + targetDir = ""; + src = composerEnv.buildZipPackage { + name = "symfony-service-contracts-f021b05a130d35510bd6b25fe9053c2a8a15d5d4"; + src = fetchurl { + url = "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4"; + sha256 = "0gg6vg2x75gn5krzm99bkmk2xpc3n2i2xsi45gf3crgdyrh7mb37"; + }; + }; + }; + "symfony/string" = { + targetDir = ""; + src = composerEnv.buildZipPackage { + name = "symfony-string-42f505aff654e62ac7ac2ce21033818297ca89ca"; + src = fetchurl { + url = "https://api.github.com/repos/symfony/string/zipball/42f505aff654e62ac7ac2ce21033818297ca89ca"; + sha256 = "02cji47gy7rhrhvmbsg2gvis4875di6d2w89kbz8n2g8w8v2yhay"; + }; + }; + }; + }; + devPackages = {}; +in +composerEnv.buildPackage { + inherit packages devPackages noDev; + name = "composer2nix-example"; + src = composerEnv.filterSrc ./.; + executable = false; + symlinkDependencies = false; + meta = {}; +}