Unverified Commit 6a231a6e authored by Domen Kožar's avatar Domen Kožar Committed by GitHub
Browse files

Merge pull request #197613 from shyim/add-coder

coder: fix web frontend building
parents d95021fe 56ecab70
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -1129,6 +1129,7 @@
  ./services/web-apps/baget.nix
  ./services/web-apps/bookstack.nix
  ./services/web-apps/calibre-web.nix
  ./services/web-apps/coder.nix
  ./services/web-apps/changedetection-io.nix
  ./services/web-apps/cloudlog.nix
  ./services/web-apps/code-server.nix
+217 −0
Original line number Diff line number Diff line
{ config, lib, options, pkgs, ... }:

with lib;

let
  cfg = config.services.coder;
  name = "coder";
in {
  options = {
    services.coder = {
      enable = mkEnableOption (lib.mdDoc "Coder service");

      user = mkOption {
        type = types.str;
        default = "coder";
        description = lib.mdDoc ''
          User under which the coder service runs.

          ::: {.note}
          If left as the default value this user will automatically be created
          on system activation, otherwise it needs to be configured manually.
          :::
        '';
      };

      group = mkOption {
        type = types.str;
        default = "coder";
        description = lib.mdDoc ''
          Group under which the coder service runs.

          ::: {.note}
          If left as the default value this group will automatically be created
          on system activation, otherwise it needs to be configured manually.
          :::
        '';
      };

      package = mkOption {
        type = types.package;
        default = pkgs.coder;
        description = lib.mdDoc ''
          Package to use for the service.
        '';
        defaultText = literalExpression "pkgs.coder";
      };

      homeDir = mkOption {
        type = types.str;
        description = lib.mdDoc ''
          Home directory for coder user.
        '';
        default = "/var/lib/coder";
      };

      listenAddress = mkOption {
        type = types.str;
        description = lib.mdDoc ''
          Listen address.
        '';
        default = "127.0.0.1:3000";
      };

      accessUrl = mkOption {
        type = types.nullOr types.str;
        description = lib.mdDoc ''
          Access URL should be a external IP address or domain with DNS records pointing to Coder.
        '';
        default = null;
        example = "https://coder.example.com";
      };

      wildcardAccessUrl = mkOption {
        type = types.nullOr types.str;
        description = lib.mdDoc ''
          If you are providing TLS certificates directly to the Coder server, you must use a single certificate for the root and wildcard domains.
        '';
        default = null;
        example = "*.coder.example.com";
      };

      database = {
        createLocally = mkOption {
          type = types.bool;
          default = true;
          description = lib.mdDoc ''
            Create the database and database user locally.
          '';
        };

        host = mkOption {
          type = types.str;
          default = "/run/postgresql";
          description = lib.mdDoc ''
            Hostname hosting the database.
          '';
        };

        database = mkOption {
          type = types.str;
          default = "coder";
          description = lib.mdDoc ''
            Name of database.
          '';
        };

        username = mkOption {
          type = types.str;
          default = "coder";
          description = lib.mdDoc ''
            Username for accessing the database.
          '';
        };

        password = mkOption {
          type = types.nullOr types.str;
          default = null;
          description = lib.mdDoc ''
            Password for accessing the database.
          '';
        };

        sslmode = mkOption {
          type = types.nullOr types.str;
          default = "disable";
          description = lib.mdDoc ''
            Password for accessing the database.
          '';
        };
      };

      tlsCert = mkOption {
        type = types.nullOr types.path;
        description = lib.mdDoc ''
          The path to the TLS certificate.
        '';
        default = null;
      };

      tlsKey = mkOption {
        type = types.nullOr types.path;
        description = lib.mdDoc ''
          The path to the TLS key.
        '';
        default = null;
      };
    };
  };

  config = mkIf cfg.enable {
    assertions = [
      { assertion = cfg.database.createLocally -> cfg.database.username == name;
        message = "services.coder.database.username must be set to ${user} if services.coder.database.createLocally is set true";
      }
    ];

    systemd.services.coder = {
      description = "Coder - Self-hosted developer workspaces on your infra";
      after = [ "network.target" ];
      wantedBy = [ "multi-user.target" ];

      environment = {
        CODER_ACCESS_URL = cfg.accessUrl;
        CODER_WILDCARD_ACCESS_URL = cfg.wildcardAccessUrl;
        CODER_PG_CONNECTION_URL = "user=${cfg.database.username} ${optionalString (cfg.database.password != null) "password=${cfg.database.password}"} database=${cfg.database.database} host=${cfg.database.host} ${optionalString (cfg.database.sslmode != null) "sslmode=${cfg.database.sslmode}"}";
        CODER_ADDRESS = cfg.listenAddress;
        CODER_TLS_ENABLE = optionalString (cfg.tlsCert != null) "1";
        CODER_TLS_CERT_FILE = cfg.tlsCert;
        CODER_TLS_KEY_FILE = cfg.tlsKey;
      };

      serviceConfig = {
        ProtectSystem = "full";
        PrivateTmp = "yes";
        PrivateDevices = "yes";
        SecureBits = "keep-caps";
        AmbientCapabilities = "CAP_IPC_LOCK CAP_NET_BIND_SERVICE";
        CacheDirectory = "coder";
        CapabilityBoundingSet = "CAP_SYSLOG CAP_IPC_LOCK CAP_NET_BIND_SERVICE";
        KillSignal = "SIGINT";
        KillMode = "mixed";
        NoNewPrivileges = "yes";
        Restart = "on-failure";
        ExecStart = "${cfg.package}/bin/coder server";
        User = cfg.user;
        Group = cfg.group;
      };
    };

    services.postgresql = lib.mkIf cfg.database.createLocally {
      enable = true;
      ensureDatabases = [
        cfg.database.database
      ];
      ensureUsers = [{
        name = cfg.database.username;
        ensurePermissions = {
          "DATABASE \"${cfg.database.database}\"" = "ALL PRIVILEGES";
        };
        }
      ];
    };

    users.groups = optionalAttrs (cfg.group == name) {
      "${cfg.group}" = {};
    };
    users.users = optionalAttrs (cfg.user == name) {
      ${name} = {
        description = "Coder service user";
        group = cfg.group;
        home = cfg.homeDir;
        createHome = true;
        isSystemUser = true;
      };
    };
  };
}
+1 −0
Original line number Diff line number Diff line
@@ -137,6 +137,7 @@ in {
  cntr = handleTestOn ["aarch64-linux" "x86_64-linux"] ./cntr.nix {};
  cockpit = handleTest ./cockpit.nix {};
  cockroachdb = handleTestOn ["x86_64-linux"] ./cockroachdb.nix {};
  coder = handleTest ./coder.nix {};
  collectd = handleTest ./collectd.nix {};
  connman = handleTest ./connman.nix {};
  consul = handleTest ./consul.nix {};

nixos/tests/coder.nix

0 → 100644
+24 −0
Original line number Diff line number Diff line
import ./make-test-python.nix ({ pkgs, ... }: {
  name = "coder";
  meta = with pkgs.lib.maintainers; {
    maintainers = [ shyim ghuntley ];
  };

  nodes.machine =
    { pkgs, ... }:
    {
      services.coder = {
        enable = true;
        accessUrl = "http://localhost:3000";
      };
    };

  testScript = ''
    machine.start()
    machine.wait_for_unit("postgresql.service")
    machine.wait_for_unit("coder.service")
    machine.wait_for_open_port(3000)

    machine.succeed("curl --fail http://localhost:3000")
  '';
})
+55 −4
Original line number Diff line number Diff line
{ buildGoModule
{ lib
, fetchFromGitHub
, installShellFiles
, lib
, makeWrapper
, buildGoModule
, fetchYarnDeps
, fixup_yarn_lock
, pkg-config
, nodejs
, yarn
, nodePackages
, python3
, terraform
}:

buildGoModule rec {
  pname = "coder";
  version = "0.17.1";
@@ -14,18 +24,59 @@ buildGoModule rec {
    hash = "sha256-FHBaefwSGZXwn1jdU7zK8WhwjarknvyeUJTlhmk/hPM=";
  };

  offlineCache = fetchYarnDeps {
    yarnLock = src + "/site/yarn.lock";
    hash = "sha256-4GbM7GNZ3wHIZJIJuHw1v/SwjUNc1vi8IHRGaGwPGZQ=";
  };

  subPackages = [ "cmd/..." ];

  vendorHash = "sha256-+AvmJkZCFovE2+5Lg98tUvA7f2kBHUMzhl5IyrEGuy8=";

  # integration tests require network access
  doCheck = false;

  vendorHash = "sha256-+AvmJkZCFovE2+5Lg98tUvA7f2kBHUMzhl5IyrEGuy8=";
  ldflags = [
    "-s"
    "-w"
    "-X github.com/coder/coder/buildinfo.tag=${version}"
  ];

  preBuild = ''
    export HOME=$TEMPDIR

  nativeBuildInputs = [ installShellFiles ];
    pushd site
    yarn config --offline set yarn-offline-mirror ${offlineCache}
    fixup_yarn_lock yarn.lock

    # node-gyp tries to download always the headers and fails: https://github.com/NixOS/nixpkgs/issues/195404
    yarn remove --offline jest-canvas-mock canvas

    NODE_ENV=production node node_modules/.bin/vite build

    popd
  '';

  tags = [ "embed" ];

  nativeBuildInputs = [
    fixup_yarn_lock
    installShellFiles
    makeWrapper
    nodePackages.node-pre-gyp
    nodejs
    pkg-config
    python3
    yarn
  ];

  postInstall = ''
    installShellCompletion --cmd coder \
      --bash <($out/bin/coder completion bash) \
      --fish <($out/bin/coder completion fish) \
      --zsh <($out/bin/coder completion zsh)

    wrapProgram $out/bin/coder --prefix PATH : ${lib.makeBinPath [ terraform ]}
  '';

  meta = with lib; {