- Add option to deploy third party package CLI tools
- Fix bug in generation of tarball downloads - Added some documentation
This commit is contained in:
parent
da4e2d914a
commit
f9829e8b9e
5 changed files with 184 additions and 18 deletions
62
README.md
62
README.md
|
@ -10,10 +10,19 @@ In addition, generated Nix composer packages
|
||||||
support convenient integration of PHP applications with NixOS services, such as
|
support convenient integration of PHP applications with NixOS services, such as
|
||||||
NixOS' Apache HTTP service.
|
NixOS' Apache HTTP service.
|
||||||
|
|
||||||
|
Prerequisites
|
||||||
|
=============
|
||||||
|
This package requires the following packages to be installed:
|
||||||
|
|
||||||
|
* [Nix package manager](http://nixos.org/nix)
|
||||||
|
* The Nix prefetch scripts, e.g: `nix-env -f '<nixpkgs>' -iA nix-prefetch-scripts`
|
||||||
|
|
||||||
|
Consult the Nix documentation for the installation instructions.
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
=====
|
=====
|
||||||
You need a project providing both a `composer.json` and a `composer.lock`
|
You need a project providing a `composer.json` and (if applicable) a
|
||||||
configuration file.
|
`composer.lock` configuration file.
|
||||||
|
|
||||||
Running the following command generates Nix expressions from the composer
|
Running the following command generates Nix expressions from the composer
|
||||||
configuration files:
|
configuration files:
|
||||||
|
@ -117,9 +126,7 @@ in
|
||||||
|
|
||||||
We can deploy the above NixOS configuration as follows:
|
We can deploy the above NixOS configuration as follows:
|
||||||
|
|
||||||
```bash
|
$ nixos-rebuild switch
|
||||||
$ nixos-rebuild switch
|
|
||||||
```
|
|
||||||
|
|
||||||
If the above command succeeds, we have a running system with the Apache
|
If the above command succeeds, we have a running system with the Apache
|
||||||
webserver serving our web application.
|
webserver serving our web application.
|
||||||
|
@ -138,6 +145,51 @@ We can install the `composer2nix` executable in our Nix profile by running:
|
||||||
|
|
||||||
$ nix-env -f default.nix -i
|
$ nix-env -f default.nix -i
|
||||||
|
|
||||||
|
Deploying third-party end user packages
|
||||||
|
---------------------------------------
|
||||||
|
Aside from deploying development projects, we may also want to deploy third
|
||||||
|
party end-user packages, typically command-line tools.
|
||||||
|
|
||||||
|
We can use `composer2nix` to automatically generate expressions from a third
|
||||||
|
party package that comes from Packagist, such as `phpunit`:
|
||||||
|
|
||||||
|
$ composer2nix -p phpunit/phpunit
|
||||||
|
|
||||||
|
After generating the expressions, we can deploy `phpunit` in our Nix profile,
|
||||||
|
by running:
|
||||||
|
|
||||||
|
$ nix-env -f default.nix -iA phpunit-phpunit
|
||||||
|
|
||||||
|
And after installing the package with Nix, we should be able to run:
|
||||||
|
|
||||||
|
$ phpunit --version
|
||||||
|
|
||||||
|
By default, `composer2nix` attempts to download the latest version of a package.
|
||||||
|
We can also add a parameter that specifies the version we want to use:
|
||||||
|
|
||||||
|
$ composer2nix -p phpunit/phpunit --package-version 6.2.0
|
||||||
|
|
||||||
|
The above command-line instruction deploys `phpunit` version `6.2.0`.
|
||||||
|
|
||||||
|
The `--package-version` parameter supports any version specifier supported by
|
||||||
|
`composer`, including version ranges.
|
||||||
|
|
||||||
|
Advanced features
|
||||||
|
=================
|
||||||
|
`composer2nix` supports a number of less commonly used advanced features.
|
||||||
|
|
||||||
|
Symlinking dependencies
|
||||||
|
-----------------------
|
||||||
|
By default, `composer2nix` makes copies of all packages that end up in the
|
||||||
|
`vendor/` folder. This is the default option, because some packages load the
|
||||||
|
`autoload.php` relative from its resolved location, such as `phpunit` and may
|
||||||
|
not work properly if a dependency is a symlink.
|
||||||
|
|
||||||
|
It is also possible to symlink all dependencies as opposed to copying them which
|
||||||
|
makes deployments faster and more space efficient:
|
||||||
|
|
||||||
|
$ composer2nix --symlink-dependencies
|
||||||
|
|
||||||
Limitations
|
Limitations
|
||||||
===========
|
===========
|
||||||
Currently, the state of this tool is that it is just a proof on concept
|
Currently, the state of this tool is that it is just a proof on concept
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<?php
|
<?php
|
||||||
require_once(dirname(__FILE__)."/../vendor/autoload.php");
|
require_once(dirname(__FILE__)."/../vendor/autoload.php");
|
||||||
|
|
||||||
|
use Composer2Nix\Composer;
|
||||||
use Composer2Nix\Generator;
|
use Composer2Nix\Generator;
|
||||||
|
|
||||||
function displayHelp($command)
|
function displayHelp($command)
|
||||||
|
@ -17,6 +18,9 @@ Options:
|
||||||
composer.json)
|
composer.json)
|
||||||
--lock-file=FILE Path to the composer.lock file (defaults to:
|
--lock-file=FILE Path to the composer.lock file (defaults to:
|
||||||
composer.lock)
|
composer.lock)
|
||||||
|
-p, --package Package to deploy as a command-line utility
|
||||||
|
--package-version Preferred version of the package to deploy (defaults
|
||||||
|
to the latest version)
|
||||||
--output=FILE Path to the Nix expression containing the generated
|
--output=FILE Path to the Nix expression containing the generated
|
||||||
packages (defaults to: php-packages.nix)
|
packages (defaults to: php-packages.nix)
|
||||||
--composition=FILE Path to the Nix expression that composes the package
|
--composition=FILE Path to the Nix expression that composes the package
|
||||||
|
@ -33,6 +37,10 @@ Options:
|
||||||
--no-copy-composer-env
|
--no-copy-composer-env
|
||||||
Do not create a copy of the Nix expression that builds
|
Do not create a copy of the Nix expression that builds
|
||||||
composer packages
|
composer packages
|
||||||
|
--symlink-deps Symlink the dependencies as opposed to copying them.
|
||||||
|
This is more space efficient, but not all packages may
|
||||||
|
be able to load their dependencies if they depend on
|
||||||
|
symlink resolving (defaults to: false).
|
||||||
-h, --help Shows the usage of this command
|
-h, --help Shows the usage of this command
|
||||||
-v, --version Shows the version of this command
|
-v, --version Shows the version of this command
|
||||||
|
|
||||||
|
@ -46,9 +54,11 @@ function displayVersion($command)
|
||||||
|
|
||||||
/* Parse command line options */
|
/* Parse command line options */
|
||||||
|
|
||||||
$options = getopt("hv", array(
|
$options = getopt("p:hv", array(
|
||||||
"config-file:",
|
"config-file:",
|
||||||
"lock-file:",
|
"lock-file:",
|
||||||
|
"package:",
|
||||||
|
"package-version:",
|
||||||
"output:",
|
"output:",
|
||||||
"composition:",
|
"composition:",
|
||||||
"composer-env:",
|
"composer-env:",
|
||||||
|
@ -58,6 +68,7 @@ $options = getopt("hv", array(
|
||||||
"name:",
|
"name:",
|
||||||
"executable",
|
"executable",
|
||||||
"no-copy-composer-env",
|
"no-copy-composer-env",
|
||||||
|
"symlink-deps",
|
||||||
"help",
|
"help",
|
||||||
"version"
|
"version"
|
||||||
));
|
));
|
||||||
|
@ -108,6 +119,18 @@ if(array_key_exists("composer-env", $options))
|
||||||
else
|
else
|
||||||
$composerEnvFile = "composer-env.nix";
|
$composerEnvFile = "composer-env.nix";
|
||||||
|
|
||||||
|
if(array_key_exists("p", $options))
|
||||||
|
$package = $options["p"];
|
||||||
|
else if(array_key_exists("package", $options))
|
||||||
|
$package = $options["package"];
|
||||||
|
else
|
||||||
|
$package = null;
|
||||||
|
|
||||||
|
if(array_key_exists("package-version", $options))
|
||||||
|
$versionSpec = $options["package-version"];
|
||||||
|
else
|
||||||
|
$versionSpec = null;
|
||||||
|
|
||||||
$noCopyComposerEnv = array_key_exists("no-copy-composer-env", $options);
|
$noCopyComposerEnv = array_key_exists("no-copy-composer-env", $options);
|
||||||
|
|
||||||
$preferredInstall = "dist"; // TODO: consult composer.json's preferred-install property. defaults to: auto
|
$preferredInstall = "dist"; // TODO: consult composer.json's preferred-install property. defaults to: auto
|
||||||
|
@ -125,13 +148,21 @@ if(array_key_exists("name", $options))
|
||||||
else
|
else
|
||||||
$name = null;
|
$name = null;
|
||||||
|
|
||||||
|
$symlinkDependencies = array_key_exists("symlink-deps", $options);
|
||||||
|
|
||||||
$executable = array_key_exists("executable", $options);
|
$executable = array_key_exists("executable", $options);
|
||||||
|
|
||||||
/* Execute the generator */
|
/* Execute the generator */
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Generator::generateNixExpressions($name, $executable, $preferredInstall, $noDev, $configFile, $lockFile, $outputFile, $compositionFile, $composerEnvFile, $noCopyComposerEnv);
|
if($package === null)
|
||||||
|
Generator::generateNixExpressions($name, $executable, $preferredInstall, $noDev, $configFile, $lockFile, $outputFile, $compositionFile, $composerEnvFile, $noCopyComposerEnv, $symlinkDependencies);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Composer::composePackageFromDependency($package, $versionSpec, $preferredInstall, $noDev);
|
||||||
|
Generator::generateNixExpressions($package, true, $preferredInstall, $noDev, "composer.json", "composer.lock", $outputFile, $compositionFile, $composerEnvFile, $noCopyComposerEnv, $symlinkDependencies);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch(Exception $ex)
|
catch(Exception $ex)
|
||||||
{
|
{
|
||||||
|
|
74
src/Composer2Nix/Composer.php
Normal file
74
src/Composer2Nix/Composer.php
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
<?php
|
||||||
|
namespace Composer2Nix;
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class Composer
|
||||||
|
{
|
||||||
|
private static function rrmdir($dir)
|
||||||
|
{
|
||||||
|
if(is_dir($dir))
|
||||||
|
{
|
||||||
|
$objects = scandir($dir);
|
||||||
|
foreach($objects as $object)
|
||||||
|
{
|
||||||
|
if($object != "." && $object != "..")
|
||||||
|
{
|
||||||
|
if(is_dir($dir."/".$object))
|
||||||
|
Composer::rrmdir($dir."/".$object);
|
||||||
|
else
|
||||||
|
unlink($dir."/".$object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rmdir($dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function composePackageFromDependency($dependencyName, $versionSpec, $preferredInstall, $noDev)
|
||||||
|
{
|
||||||
|
/* Generate a composer.json file with only the requested dependency */
|
||||||
|
$handle = fopen("composer.json", "w");
|
||||||
|
|
||||||
|
if($handle === false)
|
||||||
|
throw new Exception("Cannot write to: composer.json");
|
||||||
|
|
||||||
|
fwrite($handle, "{\n");
|
||||||
|
fwrite($handle, ' "require": {'."\n");
|
||||||
|
|
||||||
|
if($versionSpec === null)
|
||||||
|
$versionSpec = "*";
|
||||||
|
|
||||||
|
fwrite($handle, ' "'.$dependencyName.'": "'.$versionSpec.'"'."\n");
|
||||||
|
fwrite($handle, " }\n");
|
||||||
|
fwrite($handle, "}\n");
|
||||||
|
|
||||||
|
fclose($handle);
|
||||||
|
|
||||||
|
/* Run composer to get a lock file showing the dependencies */
|
||||||
|
if($noDev)
|
||||||
|
$params = "--no-dev";
|
||||||
|
else
|
||||||
|
$params = "";
|
||||||
|
|
||||||
|
if($preferredInstall == "source")
|
||||||
|
$params .= " --prefer-source";
|
||||||
|
else if($preferredInstall == "dist")
|
||||||
|
$params .= " --prefer-dist";
|
||||||
|
|
||||||
|
$composerPath = shell_exec("nix-build --no-out-link -E 'let pkgs = import <nixpkgs> {}; composerEnv = import ".__DIR__."/composer-env.nix { inherit (pkgs) stdenv writeTextFile fetchurl php unzip; }; in composerEnv.composer'");
|
||||||
|
if($composerPath === false)
|
||||||
|
throw new Exception("Cannot deploy the composer Nix package!");
|
||||||
|
|
||||||
|
$composerExecutable = substr($composerPath, 0, -1)."/bin/composer";
|
||||||
|
|
||||||
|
$result = shell_exec($composerExecutable." ".$params." install");
|
||||||
|
if($result === false)
|
||||||
|
throw new Exception("Failed running composer!\n");
|
||||||
|
|
||||||
|
print($result."\n");
|
||||||
|
|
||||||
|
/* Remove vendor folder */
|
||||||
|
Composer::rrmdir("vendor");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
|
@ -46,7 +46,7 @@ class Generator
|
||||||
return "./".$target;
|
return "./".$target;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function generatePackagesExpression($outputFile, $name, $preferredInstall, array $packages, $executable)
|
private static function generatePackagesExpression($outputFile, $name, $preferredInstall, array $packages, $executable, $symlinkDependencies)
|
||||||
{
|
{
|
||||||
$handle = fopen($outputFile, "w");
|
$handle = fopen($outputFile, "w");
|
||||||
|
|
||||||
|
@ -89,10 +89,10 @@ class Generator
|
||||||
else
|
else
|
||||||
$src = new NixFile($sourceObj['url']);
|
$src = new NixFile($sourceObj['url']);
|
||||||
|
|
||||||
$dependency["src"] = new NixFunInvocation(new NixExpression("composerEnv.buildZipPackage", array(
|
$dependency["src"] = new NixFunInvocation(new NixExpression("composerEnv.buildZipPackage"), array(
|
||||||
"name" => strtr($package["name"], "/", "-").$reference,
|
"name" => strtr($package["name"], "/", "-").$reference,
|
||||||
"src" => $src
|
"src" => $src
|
||||||
)));
|
));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "git":
|
case "git":
|
||||||
|
@ -148,7 +148,8 @@ class Generator
|
||||||
"name" => $name,
|
"name" => $name,
|
||||||
"src" => new NixFile("./."),
|
"src" => new NixFile("./."),
|
||||||
"executable" => $executable,
|
"executable" => $executable,
|
||||||
"dependencies" => new NixInherit()
|
"dependencies" => new NixInherit(),
|
||||||
|
"symlinkDependencies" => $symlinkDependencies
|
||||||
))));
|
))));
|
||||||
|
|
||||||
$exprStr = NixGenerator::phpToNix($expr, true);
|
$exprStr = NixGenerator::phpToNix($expr, true);
|
||||||
|
@ -189,7 +190,7 @@ class Generator
|
||||||
fclose($handle);
|
fclose($handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function generateNixExpressions($name, $executable, $preferredInstall, $noDev, $configFile, $lockFile, $outputFile, $compositionFile, $composerEnvFile, $noCopyComposerEnv)
|
public static function generateNixExpressions($name, $executable, $preferredInstall, $noDev, $configFile, $lockFile, $outputFile, $compositionFile, $composerEnvFile, $noCopyComposerEnv, $symlinkDependencies)
|
||||||
{
|
{
|
||||||
/* Open the composer.json file and decode it */
|
/* Open the composer.json file and decode it */
|
||||||
$composerJSONStr = file_get_contents($configFile);
|
$composerJSONStr = file_get_contents($configFile);
|
||||||
|
@ -239,7 +240,7 @@ class Generator
|
||||||
$packages = array();
|
$packages = array();
|
||||||
|
|
||||||
/* Generate packages expression */
|
/* Generate packages expression */
|
||||||
Generator::generatePackagesExpression($outputFile, $name, $preferredInstall, $packages, $executable);
|
Generator::generatePackagesExpression($outputFile, $name, $preferredInstall, $packages, $executable, $symlinkDependencies);
|
||||||
|
|
||||||
/* Generate composition expression */
|
/* Generate composition expression */
|
||||||
Generator::generateCompositionExpression($compositionFile, $outputFile, $composerEnvFile);
|
Generator::generateCompositionExpression($compositionFile, $outputFile, $composerEnvFile);
|
||||||
|
|
|
@ -49,7 +49,7 @@ rec {
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
buildPackage = { name, src, dependencies ? [], executable ? false, removeComposerArtifacts ? false }:
|
buildPackage = { name, src, dependencies ? [], symlinkDependencies ? false, executable ? false, removeComposerArtifacts ? false }:
|
||||||
let
|
let
|
||||||
reconstructInstalled = writeTextFile {
|
reconstructInstalled = writeTextFile {
|
||||||
name = "reconstructinstalled.php";
|
name = "reconstructinstalled.php";
|
||||||
|
@ -150,11 +150,19 @@ rec {
|
||||||
${if dependency.targetDir == "" then ''
|
${if dependency.targetDir == "" then ''
|
||||||
vendorDir="$(dirname ${dependencyName})"
|
vendorDir="$(dirname ${dependencyName})"
|
||||||
mkdir -p "$vendorDir"
|
mkdir -p "$vendorDir"
|
||||||
ln -s "${dependency.src}" "$vendorDir/$(basename "${dependencyName}")"
|
${if symlinkDependencies then
|
||||||
|
''ln -s "${dependency.src}" "$vendorDir/$(basename "${dependencyName}")"''
|
||||||
|
else
|
||||||
|
''cp -av "${dependency.src}" "$vendorDir/$(basename "${dependencyName}")"''
|
||||||
|
}
|
||||||
'' else ''
|
'' else ''
|
||||||
namespaceDir="${dependencyName}/$(dirname "${dependency.targetDir}")"
|
namespaceDir="${dependencyName}/$(dirname "${dependency.targetDir}")"
|
||||||
mkdir -p "$namespaceDir"
|
mkdir -p "$namespaceDir"
|
||||||
ln -s "${dependency.src}" "$namespaceDir/$(basename "${dependency.targetDir}")"
|
${if symlinkDependencies then
|
||||||
|
''ln -s "${dependency.src}" "$namespaceDir/$(basename "${dependency.targetDir}")"''
|
||||||
|
else
|
||||||
|
''cp -av "${dependency.src}" "$namespaceDir/$(basename "${dependency.targetDir}")"''
|
||||||
|
}
|
||||||
''}
|
''}
|
||||||
'') (builtins.attrNames dependencies)}
|
'') (builtins.attrNames dependencies)}
|
||||||
cd ..
|
cd ..
|
||||||
|
|
Loading…
Reference in a new issue