From 214a7d8d342287b2cc7c6d0a73d4a3fe706b6a8b Mon Sep 17 00:00:00 2001 From: Jan Tojnar Date: Mon, 31 May 2021 05:46:27 +0200 Subject: [PATCH 1/2] nix: add selfoss Nix package Build client part using napalm and fetch dependencies for the server part using C4. --- flake.lock | 37 +++++++++++++++++ flake.nix | 51 ++++++++++++++++++++++- utils/selfoss.nix | 100 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 186 insertions(+), 2 deletions(-) create mode 100644 utils/selfoss.nix diff --git a/flake.lock b/flake.lock index 4a0ba9d828..660c85d4b0 100644 --- a/flake.lock +++ b/flake.lock @@ -1,5 +1,20 @@ { "nodes": { + "c4": { + "locked": { + "lastModified": 1622472819, + "narHash": "sha256-DtqsBJsWsFj6AoQJczJqYit3mByuw0cdqkZ7mDhhLoI=", + "owner": "fossar", + "repo": "composition-c4", + "rev": "5cd0e71a256ff20934965c5a1302f784e0c6cb0f", + "type": "github" + }, + "original": { + "owner": "fossar", + "repo": "composition-c4", + "type": "github" + } + }, "flake-compat": { "flake": false, "locked": { @@ -32,6 +47,26 @@ "type": "github" } }, + "napalm": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1604997796, + "narHash": "sha256-XxRQvE8FbmaUn5F0VFRt9x9Yg/dJJx8O2ZkeGtUsHTg=", + "owner": "nmattia", + "repo": "napalm", + "rev": "7c0c62207b6e70168dab91ba85d7d2568c3e0157", + "type": "github" + }, + "original": { + "owner": "nmattia", + "repo": "napalm", + "type": "github" + } + }, "nixpkgs": { "locked": { "lastModified": 1622059058, @@ -86,7 +121,9 @@ }, "root": { "inputs": { + "c4": "c4", "flake-compat": "flake-compat", + "napalm": "napalm", "nixpkgs": "nixpkgs", "phps": "phps", "utils": "utils_2" diff --git a/flake.nix b/flake.nix index 5ea988b4d8..27819292ae 100644 --- a/flake.nix +++ b/flake.nix @@ -2,6 +2,9 @@ description = "selfoss feed reader and aggregator"; inputs = { + # Tool for downloading Composer dependencies using Nix. + c4.url = "github:fossar/composition-c4"; + # Shim to make flake.nix work with stable Nix. flake-compat = { url = "github:edolstra/flake-compat"; @@ -11,13 +14,18 @@ # Repository with software packages. nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + napalm = { + url = "github:nmattia/napalm"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + utils.url = "github:numtide/flake-utils"; # Package expression for old PHP versions. phps.url = "github:fossar/nix-phps"; }; - outputs = { self, flake-compat, nixpkgs, phps, utils }: + outputs = { self, c4, flake-compat, napalm, nixpkgs, phps, utils }: let # Configure the development shell here (e.g. for CI). @@ -28,7 +36,16 @@ utils.lib.eachDefaultSystem (system: let # Get Nixpkgs packages for current platform. - pkgs = nixpkgs.legacyPackages.${system}; + pkgs = import nixpkgs { + inherit system; + overlays = [ + # Include c4 tool. + c4.overlay + + # Include napalm tool. + napalm.overlay + ]; + }; # Create a PHP package from the selected PHP package, with some extra extensions enabled. php = phps.packages.${system}.${matrix.phpPackage}.withExtensions ({ enabled, all }: with all; enabled ++ [ @@ -69,6 +86,36 @@ # node-gyp wants some locales, let’s make them available through an environment variable. LOCALE_ARCHIVE = "${pkgs.glibcLocales}/lib/locale/locale-archive"; }; + + packages = { + selfoss = + let + filteredSrc = builtins.path { + path = ./.; + filter = + path: + type: + !builtins.elem (builtins.baseNameOf path) [ + # These should not be part of the source code for packages built by Nix. + # Otherwise, iterating on Nix files will trigger a rebuild all the time since the source will have changed. + "flake.nix" + "flake.lock" + "utils" + # CI changes should not affect it either. + ".github" + ]; + # Unfortunately, it still triggers a rebuild since any change will cause the flake to be re-cloned. + # https://github.com/NixOS/nix/issues/3732 + }; + + # Due to Nix bug, we cannot actually use the output directly and need to copy it to a new derivation. + # https://github.com/NixOS/nix/issues/3234 + src = pkgs.runCommand "selfoss-src" {} "cp -r '${filteredSrc}' $out"; + in + pkgs.callPackage ./utils/selfoss.nix rec { + inherit src; + }; + }; } ); } diff --git a/utils/selfoss.nix b/utils/selfoss.nix new file mode 100644 index 0000000000..d5f7e829ee --- /dev/null +++ b/utils/selfoss.nix @@ -0,0 +1,100 @@ +{ + stdenv, + napalm, + runCommand, + jq, + lib, + src, + c4, + php, +}: + +let + version = (lib.importJSON (src + "/package.json")).ver; + + client-src = + runCommand "client-src" { + nativeBuildInputs = [ + jq + ]; + } '' + cp -r "${src}/assets" "$out" + chmod +w "$out" + + # napalm requires version for all packages. + # And npm needs it to follow semver. + jq '. += { "name": "selfoss-client", "version": "0.0.0+${version}" }' "$out/package-lock.json" > "$out/package-lock.json.tmp" + mv "$out/package-lock.json.tmp" "$out/package-lock.json" + + jq '. += { "name": "selfoss-client", "version": "0.0.0+${version}" }' "$out/package.json" > "$out/package.json.tmp" + mv "$out/package.json.tmp" "$out/package.json" + ''; + + stopNpmCallingHome = '' + # Do not try to find npm in napalm-registry – + # it is not there and checking will slow down the build. + npm config set update-notifier false + + # Same for security auditing, it does not make sense in the sandbox. + npm config set audit false + ''; + + client-assets = napalm.buildPackage "${client-src}" { + npmCommands = [ + # Just download and unpack all the npm packages, + # we need napalm to patch shebangs before we can run install scripts. + "npm install --loglevel verbose --ignore-scripts" + # Let’s install again, this time running scripts. + "npm install --loglevel verbose" + + # napalm only patches shebangs for scripts in bin directories + "patchShebangs node_modules/parcel/lib/bin.js" + + # Build the front-end. + "npm run build" + ]; + + postConfigure = stopNpmCallingHome; + + installPhase = '' + runHook preInstall + + mv ../public $out + + runHook postInstall + ''; + }; +in +stdenv.mkDerivation { + pname = "selfoss"; + inherit version; + + inherit src; + + composerDeps = c4.fetchComposerDeps { + inherit src; + }; + + nativeBuildInputs = [ + c4.composerSetupHook + php.packages.composer + ]; + + buildPhase = '' + runHook preBuild + + cp -r ${client-assets} public + composer install --no-dev --optimize-autoloader + + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + + mkdir -p $out + cp -r . $out + + runHook postInstall + ''; +} From 9c6e43e24fdd6cf16f71e992feceab1c3b44e0fc Mon Sep 17 00:00:00 2001 From: Jan Tojnar Date: Mon, 31 May 2021 05:49:21 +0200 Subject: [PATCH 2/2] nix: Add Docker image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Docker image has been requested for ages so let’s add it. --- flake.nix | 5 +++ utils/docker.nix | 86 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 utils/docker.nix diff --git a/flake.nix b/flake.nix index 27819292ae..8c9272e199 100644 --- a/flake.nix +++ b/flake.nix @@ -88,6 +88,11 @@ }; packages = { + selfoss-docker = pkgs.callPackage ./utils/docker.nix { + inherit (nixpkgs.lib) nixosSystem; + targetPlatform = system; + }; + selfoss = let filteredSrc = builtins.path { diff --git a/utils/docker.nix b/utils/docker.nix new file mode 100644 index 0000000000..5caf7100a4 --- /dev/null +++ b/utils/docker.nix @@ -0,0 +1,86 @@ +{ + coreutils, + bash, + util-linux, + dockerTools, + unzip, + lib, + nixosSystem, + targetPlatform, +}: + +# We are going to abuse the NixOS module for Apache to create configuration files. +# Then we will extract them from the instantiated system and add them to the image. + +let + # Configuration for NixOS system. + systemConfiguration = { config, lib, pkgs, ... }: { + services.httpd = { + enable = true; + + # TODO: make this overridable? Or hidden? + adminAddr = "admin@selfoss"; + + # TODO: is root okay? + user = "root"; + group = "root"; + + virtualHosts."selfoss" = { + documentRoot = "/var/www"; + locations."/" = { + index = "index.php index.html"; + }; + }; + + phpPackage = pkgs.php; + enablePHP = true; + }; + }; + + # Instantiate the NixOS configuration. + system = nixosSystem { + system = targetPlatform; + + modules = [ + systemConfiguration + ]; + }; + + apacheHttpd = system.config.services.httpd.package; + +in +dockerTools.buildLayeredImage { + name = "selfoss"; + tag = "latest"; + + contents = [ + apacheHttpd + + # TODO: remove, only for debugging + coreutils + bash + util-linux + ]; + + # Cargo-culted from https://sandervanderburg.blogspot.com/2020/07/on-using-nix-and-docker-as-deployment.html + maxLayers = 100; + + extraCommands = '' + mkdir -p var/log/httpd var/cache/httpd var/www etc/httpd + ${selfoss} -d var/www + cp ${system.config.environment.etc."httpd/httpd.conf".source} etc/httpd/httpd.conf + ''; + + config = { + Cmd = [ "${apacheHttpd}/bin/apachectl" "-D" "FOREGROUND" ]; + Expose = { + "80/tcp" = {}; + }; + }; +} + +# TODO: add devenv image +# NOTE: Can be run: +# nix build -L .#selfoss-docker +# docker load < result +# docker run -p 8080:80/tcp -it selfoss:latest