nix: add initial flake

This commit is contained in:
happysalada 2021-05-29 11:49:47 +09:00
parent 9ea952d101
commit b8859a7147
8 changed files with 317 additions and 11 deletions

1
.envrc Normal file
View file

@ -0,0 +1 @@
use_flake

5
.gitignore vendored
View file

@ -55,3 +55,8 @@ deps.path*
# Dev artifacts
.elixir_ls
db/
.nix-hex/
.nix-mix/
.direnv/
/result

View file

@ -5,17 +5,17 @@
import Config
if config_env() == :prod do
host = System.get_env("HOSTNAME", "localhost")
port = String.to_integer(System.get_env("PORT", "4000"))
System.get_env("RELEASING") || System.get_env("DATABASE_URL") || (System.get_env("POSTGRES_DB") && System.get_env("POSTGRES_PASSWORD")) ||
raise """
Environment variables for database are missing.
For example: DATABASE_URL=ecto://USER:PASS@HOST/DATABASE
You can also set POSTGRES_DB and POSTGRES_PASSWORD (required),
and POSTGRES_USER (default: postgres) and POSTGRES_HOST (default: localhost)
"""
System.get_env("RELEASING") || System.get_env("DATABASE_URL") ||
(System.get_env("POSTGRES_DB") && System.get_env("POSTGRES_PASSWORD")) ||
raise """
Environment variables for database are missing.
For example: DATABASE_URL=ecto://USER:PASS@HOST/DATABASE
You can also set POSTGRES_DB and POSTGRES_PASSWORD (required),
and POSTGRES_USER (default: postgres) and POSTGRES_HOST (default: localhost)
"""
if System.get_env("DATABASE_URL") do
config :bonfire, Bonfire.Repo,
@ -28,6 +28,7 @@ if config_env() == :prod do
password: System.get_env("POSTGRES_PASSWORD", "postgres"),
database: System.get_env("POSTGRES_DB", "bonfire"),
hostname: System.get_env("POSTGRES_HOST", "localhost"),
socket_dir: System.get_env("POSTGRES_SOCKET_DIR"),
pool_size: String.to_integer(System.get_env("POOL_SIZE", "10"))
end
@ -38,12 +39,14 @@ if config_env() == :prod do
You can generate one by calling: mix phx.gen.secret
"""
signing_salt = System.get_env("RELEASING") || System.get_env("SIGNING_SALT") ||
signing_salt =
System.get_env("RELEASING") || System.get_env("SIGNING_SALT") ||
raise """
environment variable SIGNING_SALT is missing.
"""
encryption_salt = System.get_env("RELEASING") || System.get_env("ENCRYPTION_SALT") ||
encryption_salt =
System.get_env("RELEASING") || System.get_env("ENCRYPTION_SALT") ||
raise """
environment variable ENCRYPTION_SALT is missing.
"""
@ -73,5 +76,6 @@ if config_env() == :prod do
#
# Then you can assemble a release by calling `mix release`.
# See `mix help release` for more information.
end
end # end prod-only config
# end prod-only config

25
flake.lock Normal file
View file

@ -0,0 +1,25 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1622253036,
"narHash": "sha256-HSVusps0KHjVayUkWvFPiIZe1JKxT+GeDQBzcpw+MFE=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "25bca77c48ddb0bacdb46e7398d948a348d06119",
"type": "github"
},
"original": {
"id": "nixpkgs",
"type": "indirect"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

33
flake.nix Normal file
View file

@ -0,0 +1,33 @@
{
description = "Bonfire!";
outputs = { self, nixpkgs }:
let
# taken from https://github.com/ngi-nix/project-template/blob/master/flake.nix
# System types to support.
supportedSystems = [ "x86_64-linux" "x86_64-darwin" ];
# Helper function to generate an attrset '{ x86_64-linux = f "x86_64-linux"; ... }'.
forAllSystems = f: nixpkgs.lib.genAttrs supportedSystems (system: f system);
# Nixpkgs instantiated for supported system types.
nixpkgsFor = forAllSystems (system: import nixpkgs { inherit system; overlays = [ self.overlay ]; });
in
{
overlay = final: prev: {
bonfire = import ./nix/package.nix { pkgs = final; inherit self; };
};
packages = forAllSystems (system:
{
inherit (nixpkgsFor.${system}) bonfire;
});
defaultPackage = forAllSystems (system: self.packages.${system}.bonfire);
nixosModules.bonfire = import ./nix/module.nix;
devShell = forAllSystems
(system:
import ./nix/shell.nix {
pkgs = nixpkgsFor.${system};
}
);
};
}

102
nix/module.nix Normal file
View file

@ -0,0 +1,102 @@
{ pkgs, config, lib, ... }:
with lib;
let
bonfireConfig = config.services.bonfire;
in
{
options.services.bonfire = {
port = mkOption {
type = types.port;
default = 4000;
description = "port to run the instance on";
};
package = mkOption {
type = types.package;
description = "package to run the instance with";
};
hostname = mkOption {
type = types.str;
default = "bonfire.cafe";
example = "bonfire.cafe";
description = ''
hostname for which the service will be run
'';
};
dbName = mkOption {
type = types.str;
default = "bonfire";
description = ''
name of the database you want to connect to
'';
};
dbSocketDir = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
If this is defined, bonfire will connect to postgres
with a unix socket and not TCP/IP
'';
};
environmentFile = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
environment file for secret environment variables
should contain
SECRET_KEY_BASE
SIGNING_SALT
ENCRYPTION_SALT
RELEASE_COOKIE
'';
};
};
config = with bonfireConfig; {
services.postgresql = {
extraPlugins = with pkgs.postgresql_13.pkgs; [ postgis ];
ensureDatabases = [ "bonfire" ];
ensureUsers = [{
# Same name as the unix user is needed
name = "bonfire";
ensurePermissions = { "DATABASE bonfire" = "ALL PRIVILEGES"; };
}];
};
systemd.services.bonfire = {
wantedBy = [ "multi-user.target" ];
after = [ "postgresql.service" ];
requires = [ "postgresql.service" ];
description = "Bonfire!";
serviceConfig = {
Type = "exec";
Restart = "on-failure";
RestartSec = 5;
ExecStartPre = "${package}/bin/bonfire eval Bonfire.Repo.ReleaseTasks.migrate";
ExecStart = "${package}/bin/bonfire start";
ExecStop = "${package}/bin/bonfire stop";
DynamicUser = true;
StateDirectory = "bonfire";
EnvironmentFile = environmentFile;
PrivateTmp = true;
ProtectSystem = "full";
NoNewPrivileges = true;
ReadWritePaths = "${if dbSocketDir == null then "" else dbSocketDir} /var/lib/bonfire";
};
environment = {
RELEASE_TMP = "/tmp";
TZDATA_DIR = "/var/lib/bonfire";
LANG = "en_US.UTF-8";
PORT = toString port;
POSTGRES_USER = "bonfire";
POSTGRES_PASSWORD = "unused";
POSTGRES_DB = dbName;
POSTGRES_SOCKET_DIR = lib.mkIf (dbSocketDir != null) dbSocketDir;
HOSTNAME = hostname;
};
};
};
}

71
nix/package.nix Normal file
View file

@ -0,0 +1,71 @@
{ pkgs, self }:
let
beamPackages = with pkgs; beam.packagesWith beam.interpreters.erlang;
in
beamPackages.mixRelease rec {
pname = "bonfire";
version = "1.0.0";
mixEnv = "prod";
src = self;
mixFodDeps = beamPackages.fetchMixDeps {
pname = "mix-deps-${pname}";
inherit src mixEnv version;
LANG = "en_US.UTF-8";
# override needed here since bonfire dependencies rely on git
installPhase = ''
runHook preInstall
mix deps.get --only ${mixEnv}
cp -r --no-preserve=mode,ownership,timestamps $TEMPDIR/deps $out
runHook postInstall
'';
# TODO add sha256
# since I didn't know exactly what dependencies where being pulled
# I went for the quick hack of not checking for dependency integrity
# This has the downside of triggering a rebuild on every deployment
sha256 = null;
};
frontendAssets = with pkgs; stdenvNoCC.mkDerivation {
pname = "frontend-assets-${pname}";
nativeBuildInputs = [ nodejs cacert git ];
inherit version src;
configurePhase = ''
export HOME=$(mktemp -d)
cp -r ${mixFodDeps} ./deps
cd assets
npm install --quiet
'';
buildPhase = ''
npm run deploy
cd ..
'';
installPhase = ''
cp -r priv/static $out
'';
outputHashAlgo = "sha256";
outputHashMode = "recursive";
# TODO add sha256
# since I didn't know exactly what dependencies where being pulled
# I went for the quick hack of not checking for dependency integrity
# This has the downside of triggering a rebuild on every deployment
outputHash = null;
impureEnvVars = lib.fetchers.proxyImpureEnvVars;
};
nativeBuildInputs = with pkgs; [ rustc cargo gcc ]; # for NIFs
postBuild = ''
cp -r ${frontendAssets} priv/static
# digest needs to write files
chmod -R u+w priv/static
mix phx.digest
'';
}

65
nix/shell.nix Normal file
View file

@ -0,0 +1,65 @@
{ pkgs ? import <nixpkgs> { } }:
with pkgs;
let
messctl = rustPlatform.buildRustPackage rec {
pname = "messctl";
version = "0.0.1";
src = fetchFromGitHub {
owner = "bonfire-networks";
repo = pname;
rev = "8421d5ee91b120f1fe78fe8b123fc0fdf59609ff";
sha256 = "sha256-MniXkng8v30xzSC+cIZ+K6DWeJLCFDieXZioAQFU4/s=";
};
cargoSha256 = "sha256-K4Wq949DK3STwKo0MgaGNsu3r+qg8OqqXK3O4g4FpR0=";
};
# define packages to install with special handling for OSX
shellBasePackages = [
git
beam.packages.erlang.elixir_1_12
nodejs-16_x
postgresql_13
messctl
# for NIFs
rustfmt
clippy
];
shellBuildInputs = shellBasePackages ++ lib.optional stdenv.isLinux inotify-tools
++ lib.optionals stdenv.isDarwin
(with darwin.apple_sdk.frameworks; [ CoreFoundation CoreServices ]);
# define shell startup command
shellHooks = ''
# this allows mix to work on the local directory
mkdir -p $PWD/.nix-mix
mkdir -p $PWD/.nix-hex
export MIX_HOME=$PWD/.nix-mix
export HEX_HOME=$PWD/.nix-mix
export PATH=$MIX_HOME/bin:$PATH
export PATH=$HEX_HOME/bin:$PATH
mix local.hex --force
export LANG=en_US.UTF-8
export ERL_AFLAGS="-kernel shell_history enabled"
# postges related
export PGDATA="$PWD/db"
# elixir
export MIX_ENV=dev
export FORK=./forks
'';
in
mkShell
{
nativeBuildInputs = [ rustc cargo gcc ]; # for NIFs
buildInputs = shellBuildInputs;
shellHook = shellHooks;
RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}";
}