- Split generator function
- Add hg dependency support - Add example case
This commit is contained in:
parent
e66636acb9
commit
c46bf0a777
5 changed files with 244 additions and 101 deletions
93
README.md
93
README.md
|
@ -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
|
||||
=======
|
||||
|
|
|
@ -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)
|
||||
{
|
|
@ -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!");
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue