aboutsummaryrefslogtreecommitdiff
path: root/flake.nix
blob: 7314190b1c40d5894a22652e65dc71658396fe40 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# UGE / L2 / Intro to relational databases / Python project prototype
# Author: Pacien TRAN-GIRARD
# Licence: EUPL-1.2

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-21.11";
    flake-utils.url = "github:numtide/flake-utils";
  };

  outputs = { self, nixpkgs, flake-utils }:
  flake-utils.lib.eachDefaultSystem (system:
  with import nixpkgs { inherit system; };
  let

    python = python39;

    pythonWithDependencies = python.withPackages (ps: with ps; [
      uvicorn           # server for the web app
      fastapi           # simple Python framework to build web apps
      aiofiles          # to let fastapi serve static resources (CSS, JS, ...)
      python-multipart  # to let fastapi handle form submissions
      jinja2            # HTML templating engine
      passlib           # for account password hashing
      psycopg2          # PostgreSQL driver for Python
      embrace           # bridges raw SQL queries to Python functions
    ]);

    develPackagesAndScripts = [
      postgresql_13        # PostgreSQL server with the standard admin tools.
      python.pkgs.ipython  # Interactive Python REPL for experimenting.
      heroku               # CLI for the Heroku hosting platform.
      skopeo               # Docker container upload utility.
      pwgen                # Simple random token generator.

      # More pleasant alternative to psql, with colours and auto-completion.
      # Custom configuration to suppress irrelevant warnings and messages.
      (writeShellScriptBin "pgcli" ''
        ${pgcli}/bin/pgcli --pgclirc "${writeText "pgclirc" ''
          [main]
          keyring = False
          less_chatty = True
        ''}" "$@"
      '')

      # Script for initialising an independent development database.
      # This creates a default empty database name "postgres" and owned by the
      # current user. Data are stored in ./development_database/pgdata.
      (writeShellScriptBin "dev-initdb" ''
        initdb \
          --no-locale \
          --encoding UTF8 \
          --auth-host reject \
          --auth-local peer \
          "$@"
      '')

      # Script for starting an independent development posgresql server.
      # Accepts connections only through a local UNIX-domain socket.
      (writeShellScriptBin "dev-postgres" ''
        postgres \
          -h "" \
          -k "$PGHOST" \
          -d 2 \
          "$@"
      '')

      # Script for starting the Uvicorn local development server.
      # `--reload-dir` arguments necessary to prevent the database directory
      # from triggering automatic application reload.
      # See: https://github.com/encode/uvicorn/issues/984
      (writeShellScriptBin "dev-serve" ''
        uvicorn \
          --reload-dir app \
          --reload-dir templates \
          --reload \
          --app-dir app \
          app:main \
          "$@"
      '')
    ];

    exportEnvVar = k: v: ''export ${k}="${v}"; echo ${k}=\"${v}\"'';
    exportDevelEnvVars = lib.mapAttrsToList exportEnvVar develEnvVars;
    develEnvVars = rec {
      PGDATA = "$PWD/development_database/pgdata";
      PGHOST = "$PWD/development_database";
      PGPORT = "5432";
      PGDATABASE = "app";
      DATABASE_URL = "postgresql:///${PGDATABASE}?host=${PGHOST}";
      COOKIE_SECRET_KEY = "insecure for development";
    };

  in {

    packages = rec {
      # Minimal production server.
      # This includes only application files tracked by git.
      # Using `gunicorn` on top of `uvicorn` is recommended for bigger loads.
      server = writeShellScript "server" ''
        cd ${./.}
        ${pythonWithDependencies}/bin/uvicorn --app-dir app app:main "$@"
      '';

      # Minimal docker image.
      # The Heroku hosting service assigns the `$PORT` dynamically.
      docker = dockerTools.streamLayeredImage {
        maxLayers = 2;
        name = "app-docker";
        config.EntryPoint = writeShellScript "run.sh" ''
          ${server} --host 0.0.0.0 --port $PORT
        '';
      };
    };

    devShell = mkShell rec {
      buildInputs = [ pythonWithDependencies ] ++ develPackagesAndScripts;

      shellHook = ''
        echo -e "\nDEVSHELL ENVIRONMENT VARIABLES:"
        ${lib.concatStringsSep "\n" exportDevelEnvVars}

        echo -e "\nDEVSHELL COMMANDS:"
        ls "${symlinkJoin { name = "env"; paths = buildInputs; }}/bin"

        # Use the default user shell instead of Bash
        $(${finger_bsd}/bin/finger $USER \
          | ${gnugrep}/bin/grep -oP 'Shell: \K.*')

        exit $?
      '';
    };

  });
}