• Operand
  • hole: deep pi.

gram: build

> ./nixos/gram/elixir.nix

Lenses
(coming soon!)


{ channel, base, domain, codebase, name, ... }:
  # run_command ? "elixir --erl '-detached' -S mix phx.server",
{ pkgs, edge, ... }:
let
  command = nd "_build/prod/rel/${name}/bin/${name}";
  cron = "(sh -c 'date -Is' | str trim)";
  place = "~/run/${name}";

  nd = cmd: ''cd ${place}/run; print "\n\n(${base})> ${cmd}\n"; nix develop --command ${cmd}'';
  nd-place = node: cmd: ''cd ${place}/run; cd ${node}; print "\n\n(${base})~> ${cmd}\n"; nix develop --command ${cmd}'';

  nu = name: body: pkgs.writeScriptBin name ''
    #!${edge.nushell}/bin/nu
    ${body}
  '';

  run = with measures; nu "run-${name}" ''
   ${clone}
   ${load}
   ${prepare}
   ${cycle}
  '';

  error = pkgs.writeText "error.html" ''
<style>
body { color: #00aa66; background-color: #003366; }
html, body { height: 100vh; width: 100vw; }
a, a:visited { color: #00aa66; }
</style>
<h1>oooops;<br/>s-lip.</h1>
<span>⡢  ⡢  ⡢ <br/><br/>
learn braille, use
<a href="https://www.unicode.org/charts/nameslist/c_2800.html">://unicode.org</a><br/>
</span>
  '';

  measures = {
    clone = ''
      # securely lock in hash here ----v
      let source = $"${place}/${cron}/*-source"
      mkdir ${place}
      ${pkgs.rsync}/bin/rsync -a ${codebase} ($source | path dirname)
      let node = (ls ($source | into glob)).name.0
      tree -dafix --noreport $node | lines | each {|n| try { chmod 777 $n } }
      try { rm ${place}/run }; ln -s $node ${place}/run
      mkdir ${place}/deps
      mkdir ${place}/node_modules
      ln -s ${place}/deps ${place}/run/deps
      ln -s ${place}/node_modules ${place}/run/assets/node_modules
      ln -s ${place}/share ${place}/run/share
    '';

    load = ''
    try { cp ~/gram/${name}.call ${place}/run/.call }
    '';

    prepare = ''
      ${nd "mix deps.get"}
      ${nd "mix compile"}
      chmod 664 ${place}/run/assets/yarn.lock
      ${nd-place "assets" "yarn" }
      chmod 664 ${place}/run/assets/yarn.lock
      tree -afix --noreport ${place}/run/priv/static | lines | where { ($in | path type) == file } | each { chmod 664 $in }
      ${nd "mix assets.deploy"}
    '';

    cycle = ''
      ${nd "mix release"}
      ${measures.close}
      ${measures.do}/bin/do-${name} daemon
    '';

    close = "try { ${close_channel channel} }";

    do = nu "do-${name}" ''
    def main --wrapped [ ...call: string ] {
      ${measures.load}
      ${command} ...($call)
    }'';

  #     echo "Run more ops, using:"
  #     echo "  do-${name} <call>"
  #     echo "Possible ops:"
  #     echo "  > do-${name} start       # Begin in local session"
  #     echo "  > do-${name} start_iex   # local session command line"
  #     echo "  > do-${name} daemon      # background process, handled by scheduler
  #     echo "  > do-${name} daemon_iex  # local debugger paired alongside a background process"
  #     echo "  > do-${name} eval [EXPR] # Run a command in a compiled and non-running codespace"
  #     echo "  > do-${name} rpc [EXPR]  # Run a command in a running relay"
  #     echo "  > do-${name} remote      # Open a shell inside a running relay"
  #     echo "  > do-${name} restart     # Relaunches a relay"
  #     echo "  > do-${name} stop        # Drops a relay"
  #     echo "  > do-${name} pid         # Display relay's process number"
  #     echo "  > do-${name} version     # Display release name and grade-number"
  };

  # mix = nu "mix-${name}" ''
  #   ${measures.load}
  #   ${nd "mix ...($call)"}
  # '';

  # ${nd "mix phx.gen.release"}
  # ${nd "print $env"}
  # ${nd run_command}
  # nd "${command} daemon"
  # nd "${command} daemon_iex"

  # ${measures.load}
  # try { ${close_channel channel} }

  close_channel = channel: ''
  let process = (
      netstat -tunlp | tail -n+3 | lines |
      split column -r ' +' |
      where column7 =~ $"/("beam")" |
      where column4 =~ ":${toString channel}" |
      get column7 | split row "/" | get 0 | into int
    ); kill $process
  '';

in {
  environment.systemPackages = with pkgs; with measures; [ run do
    (nu "close-${name}" measures.close)
    (nu "cycle-${name}" measures.cycle)
    (nu "load-${name}" measures.load)
    (nu "prepare-${name}" measures.prepare)
    # (nu "release-${name}" measures.release)
  ]; networking.firewall.allowedTCPPorts = [ 80 443 channel ];

  services.caddy = { enable = true; dataDir = "/var/lib/caddy";
    virtualHosts.${domain}.extraConfig = ''
    reverse_proxy 127.0.0.1:${toString channel}
    log {
      output file /var/lib/caddy/${name}.log {
        roll_size     200MiB
        roll_local_time
        roll_keep     1440
        roll_keep_for 1440d
      }
    }
    handle_errors {
      rewrite * ${error}
      file_server
    }''; };

  services.postgresql = {
    enable = true; enableTCPIP = true; settings.port = 5432;
    authentication = pkgs.lib.mkOverride 10 ''
    local all all trust
    host  all all 127.0.0.1/32   trust
    host  all all ::1/128        trust
    ''; };

#   systemd.services."${name}@elixir" = {

#     enable = false;
#     description = "${name}, running locally on :${toString channel} and securely proxied on ${domain}";
#     wantedBy = ["multi-user.target"];
#     unitConfig = { After = "local-fs.target"; Wants = "local-fs.target"; };

#     environment = {
#       PHX_HOST = domain; DATABASE_URL = "postgres://postgres@127.0.0.1:5432/${name}";
#       PORT = toString channel; MIX_ENV = "prod"; PHX_SERVER = "y";
#       HOME = "~/run"; NO_VHOST = "0";
#     };

# # environment.PATH = lib.mkForce "/run/current-system/sw/bin/";
#     serviceConfig = { Type = "forking"; KillMode = "mixed"; TimeoutSec = 0;
#       ExecStart = "${run}/bin/run-${name}"; };
#   };
}