{ description = "Healthchecks"; inputs = { nixpkgs = { url = "nixpkgs/nixos-unstable-small"; }; }; outputs = { self, nixpkgs, }: let system = "x86_64-linux"; pkgs = import nixpkgs { inherit system; }; py = pkgs.python312.override { packageOverrides = final: prev: { django = prev.django_5; }; }; baseImage = { tag = "latest"; maxLayers = 2; contents = [ pkgs.bash pkgs.coreutils-full ]; config = { User = "5000:5000"; Volumes = { "/tmp" = {}; }; WorkingDir = "${self.packages.${system}.healthchecks}/app"; Env = [ "LANG=en_US.UTF-8" "PYTHONPATH=${self.packages.${system}.healthchecks.pythonPath}" "PYTHONUNBUFFERED=1" "SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt" "STATIC_ROOT=${self.packages.${system}.healthchecks.static}" ]; }; }; in { packages.${system} = { healthchecks = let pname = "healthchecks"; version = "3.8"; hash = "sha256-RVmIaobts6GT1I4pc5/kZblbo6CwwQ33m3xBruSXZss="; secrets = [ "DB_PASSWORD" "DISCORD_CLIENT_SECRET" "EMAIL_HOST_PASSWORD" "LINENOTIFY_CLIENT_SECRET" "MATRIX_ACCESS_TOKEN" "PD_APP_ID" "PUSHBULLET_CLIENT_SECRET" "PUSHOVER_API_TOKEN" "S3_SECRET_KEY" "SECRET_KEY" "SLACK_CLIENT_SECRET" "TELEGRAM_TOKEN" "TRELLO_APP_KEY" "TWILIO_AUTH" ]; localSettings = pkgs.writeText "local_settings.py" '' import os CSRF_TRUSTED_HOSTS = os.getenv("CSRF_TRUSTED_HOSTS", "").split(",") STATIC_ROOT = os.getenv("STATIC_ROOT") STATICFILES_DIRS = [ ] ${pkgs.lib.concatLines (map (secret: '' ${secret}_FILE = os.getenv("${secret}_FILE") if ${secret}_FILE: with open(${secret}_FILE, "r") as file: ${secret} = file.readline() '') secrets)} ''; propagatedBuildInputs = with py.pkgs; [ aiosmtpd apprise cronsim django django-compressor django-stubs-ext fido2 minio oncalendar psycopg2 pycurl pydantic pyotp segno statsd whitenoise ]; in py.pkgs.buildPythonApplication { inherit pname version; format = "other"; outputs = ["out" "static" "doc"]; src = pkgs.fetchFromGitHub { owner = "healthchecks"; repo = pname; rev = "refs/tags/v${version}"; hash = hash; }; inherit propagatedBuildInputs; dontPatch = true; dontConfigure = true; dontBuild = true; doCheck = false; installPhase = '' mkdir -p $out/app cp -r CHANGELOG.md hc manage.py templates $out/app chmod +x $out/app/manage.py mkdir -p $doc cp -r *.md LICENSE $doc mkdir -p $static DEBUG=False SECRET_KEY=build-key STATIC_ROOT=$static ${py}/bin/python ./manage.py collectstatic --noinput DEBUG=False SECRET_KEY=build-key STATIC_ROOT=$static ${py}/bin/python ./manage.py compress cp -r static-collected/* $static cp ${localSettings} $out/app/hc/local_settings.py ''; passthru = { # PYTHONPATH of all dependencies used by the package pythonPath = py.pkgs.makePythonPath propagatedBuildInputs; tests = { inherit (pkgs.nixosTests) healthchecks; }; }; meta = with pkgs.lib; { homepage = "https://github.com/healthchecks/healthchecks"; description = "A cron monitoring tool written in Python & Django "; license = licenses.bsd3; }; }; smtpd = pkgs.dockerTools.buildLayeredImage ( pkgs.lib.attrsets.recursiveUpdate baseImage { name = "healthchecks-smtpd"; config = { Cmd = [ "${self.packages.${system}.healthchecks}/app/manage.py" "smtpd" "--port" "2525" ]; ExposedPorts = { "2525/tcp" = {}; }; }; } ); sendalerts = pkgs.dockerTools.buildLayeredImage ( pkgs.lib.attrsets.recursiveUpdate baseImage { name = "healthchecks-sendalerts"; config = { Cmd = [ "${self.packages.${system}.healthchecks}/app/manage.py" "sendalerts" ]; ExposedPorts = {}; }; } ); sendreports = pkgs.dockerTools.buildLayeredImage ( pkgs.lib.attrsets.recursiveUpdate baseImage { name = "healthchecks-sendreports"; config = { Cmd = [ "${self.packages.${system}.healthchecks}/app/manage.py" "sendreports" "--loop" ]; ExposedPorts = {}; }; } ); maintenance = let script = pkgs.writeTextFile { name = "maintenance"; text = pkgs.lib.concatStringsSep "\n" [ "#!${pkgs.nushell}/bin/nu" "" "alias manage = ${self.packages.${system}.healthchecks}/app/manage.py" "" (builtins.readFile ./maintenance.nu) ]; executable = true; destination = "/bin/maintenance"; }; in pkgs.dockerTools.buildLayeredImage ( pkgs.lib.attrsets.recursiveUpdate baseImage { name = "healthchecks-maintenance"; config = { Cmd = [ "${script}/bin/maintenance" ]; ExposedPorts = {}; }; } ); migrate = pkgs.dockerTools.buildLayeredImage ( pkgs.lib.attrsets.recursiveUpdate baseImage { name = "healthchecks-migrate"; config = { Cmd = [ "${self.packages.${system}.healthchecks}/app/manage.py" "migrate" ]; ExposedPorts = {}; }; } ); webserver = let uwsgi = pkgs.uwsgi.override { plugins = ["python3"]; }; uwsgi-ini = pkgs.writeTextFile { name = "uwsgi.ini"; text = '' [uwsgi] buffer-size = 32768 chdir = ${self.packages.${system}.healthchecks}/app die-on-term disable-write-exception enable-threads harakiri = 10 http-socket = :8000 master max-fd = 10000 mime-file = ${pkgs.mailcap}/etc/nginx/mime.types module = hc.wsgi:application plugins = python3 post-buffering = 16192 processes = 4 threads = 1 thunder-lock ''; }; in pkgs.dockerTools.buildLayeredImage ( pkgs.lib.attrsets.recursiveUpdate baseImage { name = "healthchecks-webserver"; fakeRootCommands = '' mkdir /tmp chmod 0777 /tmp ''; config = { Cmd = [ "${uwsgi}/bin/uwsgi" "${uwsgi-ini}" ]; ExposedPorts = { "8000/tcp" = {}; }; }; } ); static = pkgs.dockerTools.buildLayeredImage { name = "healthchecks-static"; tag = "latest"; maxLayers = 2; config = { User = "5000:5000"; Cmd = [ "${pkgs.static-web-server}/bin/static-web-server" "--port=8000" "--root=${self.packages.${system}.healthchecks.static}" ]; ExposedPorts = { "8000/tcp" = {}; }; }; }; }; checks.${system} = { webserver = self.packages.${system}.webserver; smtpd = self.packages.${system}.smtpd; sendalerts = self.packages.${system}.sendalerts; sendreports = self.packages.${system}.sendreports; maintenance = self.packages.${system}.maintenance; migrate = self.packages.${system}.migrate; static = self.packages.${system}.static; }; }; }