- Split generator function

- Add hg dependency support
- Add example case
This commit is contained in:
Sander van der Burg 2017-07-11 20:24:44 +02:00
parent e66636acb9
commit c46bf0a777
5 changed files with 244 additions and 101 deletions

View file

@ -29,11 +29,102 @@ including its dependencies:
$ nix-build
An example use case scenario
============================
We can use `composer2nix` to automate the deployment of a web application as
part of a NixOS configuration.
For example, we can create the following trivial PHP web application
(`index.php`) that uses the [dompdf](http://dompdf.github.io/) library to
generate a PDF file from an HTML page:
```php
<?php
require 'vendor/autoload.php';
use Dompdf\Dompdf;
$dompdf = new Dompdf();
$dompdf->loadHtml('hello world');
$dompdf->setPaper('A4', 'landscape');
$dompdf->render();
$dompdf->stream();
?>
```
We can write the following `composer.json` configuration file to configure the
`dompdf` dependency:
```json
{
"name": "exampleapp/exampleapp",
"require": {
"dompdf/dompdf": "^0.8.0"
}
}
```
With the following commmand we can let `composer` deploy the dependencies (and
pinpoint the used versions in a `composer.lock` file):
$ composer install
Instead, we can also use `composer2nix`:
$ composer2nix
The above command generates Nix expressions that can be used to deploy the web
application and its dependencies.
We can use Nix to build a bundle of our web application including its
dependencies:
$ nix-build
$ ls result/
index.php vendor/
(As may be observed, the `vendor/` folder contains all dependency artifacts).
We can attach the generated package to a document root of the Apache server in
a NixOS configuration:
```nix
{pkgs, config, ...}:
let
myexampleapp = import /home/sander/myexampleapp {
inherit pkgs;
};
in
{
services.httpd = {
enable = true;
adminAddr = "admin@localhost";
extraModules = [
{ name = "php7"; path = "${pkgs.php}/modules/libphp7.so"; }
];
documentRoot = myexampleapp;
};
...
}
```
We can deploy the above NixOS configuration as follows:
$ nixos-rebuild switch
If the above command succeeds, we have a running system with the Apache
webserver serving our web application.
Limitations
===========
Currently, the state of this tool is that it is just a proof on concept
implementation. As a result, it is lacking many features and probably buggy.
Most importantly, only the `zip` and `git` dependencies are supported.
Most importantly, only the `zip`, `git`, and `hg` dependencies are supported.
License
=======

View file

@ -25,6 +25,7 @@ Options:
packages (defaults to: composer-env.nix)
--prefer-source Forces installation from package sources when possible
--prefer-dist Forces installation from package dist
--no-dev Do not install the development packages
--name Name of the generated package (defaults to the name
provided in the composer.json file)
--no-copy-composer-env
@ -51,6 +52,7 @@ $options = getopt("hv", array(
"composer-env:",
"prefer-source",
"prefer-dist",
"no-dev",
"name:",
"no-copy-composer-env",
"help",
@ -105,13 +107,18 @@ else
$noCopyComposerEnv = array_key_exists("no-copy-composer-env", $options);
$installType = "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
if(array_key_exists("prefer-source", $options))
$installType = "source";
$preferredInstall = "source";
if(array_key_exists("prefer-dist", $options))
$installType = "dist";
$preferredInstall = "dist";
if(array_key_exists("no-dev", $options))
$noDev = true;
else
$noDev = false;
if(array_key_exists("name", $options))
$name = $options["name"];
@ -122,7 +129,7 @@ else
try
{
Generator::generateNixExpressions($name, $installType, $configFile, $lockFile, $outputFile, $compositionFile, $composerEnvFile, $noCopyComposerEnv);
Generator::generateNixExpressions($name, $preferredInstall, $noDev, $configFile, $lockFile, $outputFile, $compositionFile, $composerEnvFile, $noCopyComposerEnv);
}
catch(Exception $ex)
{

View file

@ -4,15 +4,15 @@
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Sander van der Burg",
"email": "svanderburg@gmail.com",
"homepage": "http://sandervanderburg.nl"
}
{
"name": "Sander van der Burg",
"email": "svanderburg@gmail.com",
"homepage": "http://sandervanderburg.nl"
}
],
"autoload": {
"psr-4": { "Composer2Nix\\": "src/Composer2Nix" }
"psr-4": { "Composer2Nix\\": "src/Composer2Nix" }
},
"bin": [ "bin/composer2nix" ]

View file

@ -4,7 +4,125 @@ use Exception;
class Generator
{
function generateNixExpressions($name, $installType, $configFile, $lockFile, $outputFile, $compositionFile, $composerEnvFile, $noCopyComposerEnv)
private static function composeNixFilePath($path)
{
if((strlen($path) > 0 && substr($path, 0, 1) === "/") || (strlen($path) > 1 && substr($path, 0, 2) === "./"))
return $path;
else
return "./".$path;
}
private static function selectSourceObject($preferredInstall, $package)
{
if($preferredInstall === "source")
{
// Use the source unless no source has been provided
if(array_key_exists("source", $package))
return $package["source"];
else if(array_key_exists("dist", $package))
return $package["dist"];
else
throw new Exception("Encountered a dangling package reference");
}
else
{
if(array_key_exists("dist", $package))
return $package["dist"];
else
throw new Exception("Encountered a dangling package reference");
}
}
private static function generatePackagesExpression($outputFile, $name, $preferredInstall, array $packages)
{
$handle = fopen($outputFile, "w");
if($handle === false)
throw new Exception("Cannot write to: ".$outputFile);
fwrite($handle, "{composerEnv, fetchgit ? null}:\n\n");
fwrite($handle, "let\n");
fwrite($handle, " dependencies = {\n");
foreach($packages as $package)
{
$sourceObj = Generator::selectSourceObject($preferredInstall, $package);
switch($sourceObj["type"])
{
case "zip":
$hash = shell_exec('nix-prefetch-url "'.$sourceObj['url'].'"');
fwrite($handle, ' "'.$package["name"].'" = composerEnv.buildZipPackage {'."\n");
fwrite($handle, ' name = "'.strtr($package["name"], "/", "-").'-'.$sourceObj["reference"].'";'."\n");
fwrite($handle, ' url = "'.$sourceObj["url"].'";'."\n");
fwrite($handle, ' sha256 = "'.substr($hash, 0, -1).'";'."\n");
break;
case "git":
$outputStr = shell_exec('nix-prefetch-git "'.$sourceObj['url'].'" '.$sourceObj["reference"]);
$output = json_decode($outputStr, true);
$hash = $output["sha256"];
fwrite($handle, ' "'.$package["name"].'" = fetchgit {'."\n");
fwrite($handle, ' name = "'.strtr($package["name"], "/", "-").'-'.$sourceObj["reference"].'";'."\n");
fwrite($handle, ' url = "'.$sourceObj["url"].'";'."\n");
fwrite($handle, ' rev = "'.$sourceObj["reference"].'";'."\n");
fwrite($handle, ' sha256 = "'.$hash.'";'."\n");
break;
case "hg":
$outputStr = shell_exec('nix-prefetch-hg "'.$sourceObj['url'].'" '.$sourceObj["reference"]);
$output = json_decode($outputStr, true);
$hash = $output["sha256"];
fwrite($handle, ' "'.$package["name"].'" = fetchhg {'."\n");
fwrite($handle, ' name = "'.strtr($package["name"], "/", "-").'-'.$sourceObj["reference"].'";'."\n");
fwrite($handle, ' url = "'.$sourceObj["url"].'";'."\n");
fwrite($handle, ' rev = "'.$sourceObj["reference"].'";'."\n");
fwrite($handle, ' sha256 = "'.$hash.'";'."\n");
default:
throw new Exception("Cannot convert dependency of type: ".$sourceObj["type"]);
}
fwrite($handle, " };\n");
}
fwrite($handle, " };\n");
fwrite($handle, "in\n");
fwrite($handle, "composerEnv.buildPackage {\n");
fwrite($handle, ' name = "'.$name.'";'."\n");
fwrite($handle, " src = ./.;\n");
fwrite($handle, " inherit dependencies;\n");
fwrite($handle, "}\n");
fclose($handle);
}
private static function generateCompositionExpression($compositionFile, $outputFile, $composerEnvFile)
{
$handle = fopen($compositionFile, "w");
if($handle === false)
throw new Exception("Cannot write to: ".$compositionFile);
fwrite($handle, "{ pkgs ? import <nixpkgs> { inherit system; }\n");
fwrite($handle, ", system ? builtins.currentSystem\n");
fwrite($handle, "}:\n\n");
fwrite($handle, "let\n");
fwrite($handle, " composerEnv = import ".Generator::composeNixFilePath($composerEnvFile)." {\n");
fwrite($handle, " inherit (pkgs) stdenv writeTextFile fetchurl php unzip;\n");
fwrite($handle, " };\n");
fwrite($handle, "in\n");
fwrite($handle, "import ".Generator::composeNixFilePath($outputFile)." {\n");
fwrite($handle, " inherit composerEnv;\n");
fwrite($handle, " inherit (pkgs) fetchgit;\n");
fwrite($handle, "}\n");
fclose($handle);
}
public static function generateNixExpressions($name, $preferredInstall, $noDev, $configFile, $lockFile, $outputFile, $compositionFile, $composerEnvFile, $noCopyComposerEnv)
{
/* Open the composer.json file and decode it */
$composerJSONStr = file_get_contents($configFile);
@ -38,103 +156,30 @@ class Generator
throw new Exception("Cannot open contents of: ".$lockFile);
$lockConfig = json_decode($composerLockStr, true);
if(array_key_exists("packages", $lockConfig))
$packages = $lockConfig["packages"];
else
$packages = array();
if(!$noDev && array_key_exists("packages-dev", $lockConfig))
{
foreach($lockConfig["packages-dev"] as $identifier => $devPackage)
$packages[$identifier] = $devPackage;
}
}
else
$lockConfig = null;
$packages = array();
/* Generate packages expression */
$handle = fopen($outputFile, "w");
if($handle === false)
throw new Exception("Cannot write to: ".$outputFile);
if($lockConfig === null)
$packages = array();
else
$packages = $lockConfig["packages"];
fwrite($handle, "{composerEnv, fetchgit ? null}:\n\n");
fwrite($handle, "let\n");
fwrite($handle, " dependencies = {\n");
foreach($packages as $package)
{
$sourceObj = $package[$installType];
switch($sourceObj["type"])
{
case "zip":
$hash = shell_exec('nix-prefetch-url "'.$sourceObj['url'].'"');
fwrite($handle, ' "'.$package["name"].'" = composerEnv.buildZipPackage {'."\n");
fwrite($handle, ' name = "'.strtr($package["name"], "/", "-").'-'.$sourceObj["reference"].'";'."\n");
fwrite($handle, ' url = "'.$sourceObj["url"].'";'."\n");
fwrite($handle, ' sha256 = "'.substr($hash, 0, -1).'";'."\n");
break;
case "git":
$outputStr = shell_exec('nix-prefetch-git "'.$sourceObj['url'].'" '.$sourceObj["reference"]);
$output = json_decode($outputStr, true);
$hash = $output["sha256"];
fwrite($handle, ' "'.$package["name"].'" = fetchgit {'."\n");
fwrite($handle, ' name = "'.strtr($package["name"], "/", "-").'-'.$sourceObj["reference"].'";'."\n");
fwrite($handle, ' url = "'.$sourceObj["url"].'";'."\n");
fwrite($handle, ' rev = "'.$sourceObj["reference"].'";'."\n");
fwrite($handle, ' sha256 = "'.$hash.'";'."\n");
break;
default:
throw new Exception("Cannot convert dependency of type: ".$sourceObj["type"]);
}
fwrite($handle, " };\n");
}
fwrite($handle, " };\n");
fwrite($handle, "in\n");
fwrite($handle, "composerEnv.buildPackage {\n");
fwrite($handle, ' name = "'.$name.'";'."\n");
fwrite($handle, " src = ./.;\n");
fwrite($handle, " inherit dependencies;\n");
fwrite($handle, "}\n");
fclose($handle);
Generator::generatePackagesExpression($outputFile, $name, $preferredInstall, $packages);
/* Generate composition expression */
$handle = fopen($compositionFile, "w");
if($handle === false)
throw new Exception("Cannot write to: ".$compositionFile);
function composeNixFilePath($path)
{
if((strlen($path) > 0 && substr($path, 0, 1) === "/") || (strlen($path) > 1 && substr($path, 0, 2) === "./"))
return $path;
else
return "./".$path;
}
fwrite($handle, "{ pkgs ? import <nixpkgs> { inherit system; }\n");
fwrite($handle, ", system ? builtins.currentSystem\n");
fwrite($handle, "}:\n\n");
fwrite($handle, "let\n");
fwrite($handle, " composerEnv = import ".composeNixFilePath($composerEnvFile)." {\n");
fwrite($handle, " inherit (pkgs) stdenv writeTextFile fetchurl php unzip;\n");
fwrite($handle, " };\n");
fwrite($handle, "in\n");
fwrite($handle, "import ".composeNixFilePath($outputFile)." {\n");
fwrite($handle, " inherit composerEnv;\n");
fwrite($handle, " inherit (pkgs) fetchgit;\n");
fwrite($handle, "}\n");
fclose($handle);
Generator::generateCompositionExpression($compositionFile, $outputFile, $composerEnvFile);
/* Copy composer-env.nix */
if(!$noCopyComposerEnv && !copy(dirname(__FILE__)."/composer-env.nix", $composerEnvFile))
throw new Exception("Cannot copy node-env.nix!");
throw new Exception("Cannot copy composer-env.nix!");
}
}
?>

View file

@ -64,7 +64,7 @@ rec {
if($composerLockStr === false)
{
print("Cannot open composer.lock contents");
fwrite(STDERR, "Cannot open composer.lock contents\n");
exit(1);
}
else