Loading nixos/modules/module-list.nix +1 −0 Original line number Diff line number Diff line Loading @@ -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 Loading nixos/modules/services/web-apps/coder.nix 0 → 100644 +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; }; }; }; } nixos/tests/all-tests.nix +1 −0 Original line number Diff line number Diff line Loading @@ -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 {}; Loading 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") ''; }) pkgs/development/tools/coder/default.nix +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"; Loading @@ -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; { Loading Loading
nixos/modules/module-list.nix +1 −0 Original line number Diff line number Diff line Loading @@ -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 Loading
nixos/modules/services/web-apps/coder.nix 0 → 100644 +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; }; }; }; }
nixos/tests/all-tests.nix +1 −0 Original line number Diff line number Diff line Loading @@ -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 {}; Loading
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") ''; })
pkgs/development/tools/coder/default.nix +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"; Loading @@ -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; { Loading