Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Experimental Docker image using Nix #1271

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

56 changes: 54 additions & 2 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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).

Expand All @@ -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 ++ [
Expand Down Expand Up @@ -69,6 +86,41 @@
# node-gyp wants some locales, let’s make them available through an environment variable.
LOCALE_ARCHIVE = "${pkgs.glibcLocales}/lib/locale/locale-archive";
};

packages = {
selfoss-docker = pkgs.callPackage ./utils/docker.nix {
inherit (nixpkgs.lib) nixosSystem;
targetPlatform = system;
};

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;
};
};
}
);
}
86 changes: 86 additions & 0 deletions utils/docker.nix
Original file line number Diff line number Diff line change
@@ -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
100 changes: 100 additions & 0 deletions utils/selfoss.nix
Original file line number Diff line number Diff line change
@@ -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
'';
}