Add gerrit
Gerrit is a service which provides code review via "changes" (commits) which you
can continually amend, viewing the diffs between them in a very similar way to
<https://graphite.dev/> (which is awesome but too expensive for Clicks to
justify). We hope it'll provide some better structure than the GitHub workflow
we have been using
Still TODO (in a followup change):
- The bazel build for the oauth module is horrible and introduces tips from
<https://zimbatm.com/notes/nix-packaging-the-heretic-way>. This is generally
considered a bad thing. We should change this
- Gerrit cannot yet send emails
Change-Id: I1393b2ae5a1efe049ea2170de46070d8789a2e3a
diff --git a/default/configuration.nix b/default/configuration.nix
index ad95778..0a91276 100644
--- a/default/configuration.nix
+++ b/default/configuration.nix
@@ -133,7 +133,7 @@
nix.settings.experimental-features = [ "nix-command" "flakes" ];
# Open ports in the firewall.
- networking.firewall.allowedTCPPorts = [ 80 443 25 465 587 110 995 143 993 ];
+ networking.firewall.allowedTCPPorts = [ 80 443 25 465 587 110 995 143 993 29418 ];
# networking.firewall.allowedUDPPorts = [ ... ];
# Or disable the firewall altogether.
networking.firewall.enable = true;
diff --git a/flake.nix b/flake.nix
index 838808d..11b4c88 100644
--- a/flake.nix
+++ b/flake.nix
@@ -58,6 +58,7 @@
./modules/ecryptfs.nix
./modules/fail2ban.nix
./modules/fuck.nix
+ ./modules/gerrit.nix
./modules/git.nix
./modules/grafana.nix
./modules/home-manager-users.nix
@@ -82,7 +83,7 @@
users.mutableUsers = false;
}
];
- specialArgs = { base = null; drive_paths = import ./variables/drive_paths.nix; };
+ specialArgs = { base = null; drive_paths = import ./variables/drive_paths.nix; inherit system; };
};
in
base.extendModules {
diff --git a/modules/caddy/caddyfile.nix b/modules/caddy/caddyfile.nix
index 0b2acf4..4334549 100644
--- a/modules/caddy/caddyfile.nix
+++ b/modules/caddy/caddyfile.nix
@@ -377,6 +377,10 @@
"syncthing.thecoded.prof"
"syncthing.hopescaramels.com"
] [ "localhost:8384" ])
+ (HTTPReverseProxyRoute [
+ "git.clicks.codes"
+ "gerrit.clicks.codes"
+ ] [ "127.0.0.255:1000" ])
(PHPRoute
[ "paste.clicks.codes" "paste.coded.codes" ]
"${pkgs.privatebin}/share/privatebin"
diff --git a/modules/gerrit.nix b/modules/gerrit.nix
new file mode 100644
index 0000000..962fb2b
--- /dev/null
+++ b/modules/gerrit.nix
@@ -0,0 +1,202 @@
+{ pkgs, config, lib, base, system, ... }: let
+ cfg = config.services.gerrit;
+in lib.recursiveUpdate
+{
+ sops.secrets.clicks_gerrit_db_password = {
+ mode = lib.mkForce "0440";
+ group = lib.mkForce "gerrit";
+ };
+
+ users.users.gerrit = {
+ isSystemUser = true;
+ createHome = true;
+ home = "/var/lib/gerrit";
+ group = config.users.groups.gerrit.name;
+ shell = pkgs.bashInteractive;
+ };
+ users.groups.gerrit = {};
+
+ systemd.services.gerrit.serviceConfig.User = "gerrit";
+ systemd.services.gerrit.serviceConfig.Group = "gerrit";
+ systemd.services.gerrit.serviceConfig.DynamicUser = lib.mkForce false;
+
+ services.gerrit = {
+ enable = true;
+
+ /* jvmOpts = [
+ "-Djava.class.path=${pkgs.postgresql_jdbc}/share/java"
+ ]; */
+
+ settings = {
+ # accountPatchReviewDb.url = "postgresql://localhost:${toString config.services.postgresql.port}/gerrit?user=gerrit&password=!!gerrit_database_password!!";
+ accounts = {
+ visibility = "SAME_GROUP";
+ defaultDisplayName = "USERNAME";
+ };
+ addReviewer = {
+ maxWithoutConfirmation = 3;
+ maxAllowed = 10;
+ };
+ auth = {
+ type = "OAUTH";
+ registerEmailPrivateKey = "!!gerrit_email_private_key!!";
+ userNameCaseInsensitive = true;
+ gitBasicAuthPolicy = "HTTP";
+ };
+ plugin."gerrit-oauth-provider-keycloak-oauth" = {
+ root-url = "https://login.clicks.codes";
+ realm = "clicks";
+ client-id = "git";
+ client-secret = "!!gerrit_oauth_client_secret!!";
+ use-preferred-username = true;
+ };
+ change = {
+ topicLimit = 0;
+ mergeabilityComputationBehavior = "API_REF_UPDATED_AND_CHANGE_REINDEX";
+ sendNewPatchsetEmails = false;
+ showAssigneeInChangesTable = true;
+ submitWholeTopic = true;
+ diff3ConflictView = true;
+ };
+ changeCleanup = {
+ abandonAfter = "3 weeks";
+ abandonMessage = "This change was abandoned due to 3 weeks of inactivity. If you still want it, please restore it";
+ startTime = "00:00";
+ interval = "1 day";
+ };
+ attentionSet = {
+ readdAfter = "1 week";
+ readdMessage = "I've given the owner a *ping* as nothing has happened for a week. If in two weeks time the change is still inactive, I'll abandon it for you. If you still want it, please do something before then";
+ startTime = "00:00";
+ interval = "1 day";
+ };
+ commentlink.gerrit = {
+ match = "(I[0-9a-f]{8,40})";
+ link = "/q/$1";
+ };
+ gc = {
+ aggressive = true;
+ startTime = "Sun 00:00";
+ interval = "1 week";
+ };
+ gerrit = {
+ basePath = "/var/lib/gerrit/repos";
+ defaultBranch = "refs/heads/main";
+ canonicalWebUrl = "https://git.clicks.codes/";
+ canonicalGitUrl = "ssh://ssh.clicks.codes/";
+ gitHttpUrl = "https://git.clicks.codes/";
+ reportBugUrl = "https://discord.gg/bPaNnxe"; # TODO: kinda obnoxious, better to setup bugzilla/similar
+ enablePeerIPInReflogRecord = true;
+ instanceId = "a1d1";
+ instanceName = "a1d1.clicks";
+ };
+ mimetype = lib.pipe [ "image/*" "video/*" "application/pdf" ] [
+ (map (name: { inherit name; value.safe = true; }))
+ builtins.listToAttrs
+ ];
+ receive.enableSignedPush = true;
+ sendemail.enable = false; # TODO: add credentials to git@clicks.codes
+ sshd.advertisedAddress = "ssh.clicks.codes:29418";
+ user = {
+ name = "Clicks Gerrit";
+ email = "git@clicks.codes";
+ anonymousCoward = "Anonymous";
+ };
+ httpd.listenUrl = "proxy-https://${cfg.listenAddress}";
+ };
+
+ plugins = [ (
+ derivation {
+ name = "oauth.jar"; # HACK: wrapping a derivation in a derivation to rename it seems like a bad hack... but bazel would not build if I didn't (I think because it didn't like the .jar extension...) check why though?
+ src = (
+ pkgs.buildBazelPackage {
+ __noChroot = true; # FIXME: terrible, horrible, no good, very bad
+ # name = "gerrit-oauth-provider.jar";
+ pname = "gerrit-oauth-provider.jar";
+ version = "unstable-2023-10-08";
+ src = pkgs.fetchgit {
+ url = "https://gerrit.googlesource.com/plugins/oauth";
+ rev = "1b3cc407cb2571d08601ab852e6e01f82d27160f";
+ hash = "sha256-yC/8qnkDbfIujl+Cvamr+EQSwto1DcIUWXh5cwDEZHo=";
+ deepClone = true; # FIXME: this bazel build uses some git stuff, maybe we should try replacing with fakegit?
+ };
+ bazelTargets = [ "oauth" ];
+ bazel = pkgs.bazel_4;
+ buildAttrs = {};
+ fetchAttrs.sha256 = "sha256-i5wOTn2NqqgJf4TCIqaCucpXu+5Vm5C84UPrGYFMSzc=";
+
+ postUnpack = ''
+ echo "4.2.2" > */.bazelversion # nixpkgs only has certain bazel versions, so let's upgrade the patch of this one
+ '';
+
+ buildInputs = with pkgs; [
+ git
+ curl
+ jdk11
+ ];
+
+ postInstall = ''
+ cp bazel-bin/oauth.jar $out
+ '';
+ }
+ );
+ builder = "/bin/sh";
+ args = [ "-c" "${pkgs.coreutils}/bin/cp $src $out" ];
+ inherit system;
+ }
+ ) ];
+ builtinPlugins = [ "codemirror-editor" "commit-message-length-validator" "delete-project" "download-commands" "gitiles" "hooks" "reviewnotes" "singleusergroup" "webhooks" ];
+ serverId = "45f277d0-fce7-43b7-9eb3-2e3234e0110f";
+
+ listenAddress = "127.0.0.255:1000";
+ };
+
+ nix.settings.sandbox = "relaxed"; # FIXME: terrible, horrible, no good, very bad, here to support buildBazelPackage's use of cURL
+
+ sops.secrets = {
+ gerrit_email_private_key = {
+ mode = "0400";
+ owner = config.users.users.root.name;
+ group = config.users.users.nobody.group;
+ sopsFile = ../secrets/gerrit.json;
+ format = "json";
+ };
+ gerrit_oauth_client_secret = {
+ mode = "0400";
+ owner = config.users.users.root.name;
+ group = config.users.users.nobody.group;
+ sopsFile = ../secrets/gerrit.json;
+ format = "json";
+ };
+ };
+}
+ (
+ let
+ isDerived = base != null;
+ in
+ if isDerived
+ then
+ let
+ gerrit_cfgfile = pkgs.writeText "gerrit.conf" (
+ lib.generators.toGitINI cfg.settings
+ );
+ in
+ {
+ scalpel.trafos."gerrit.conf" = {
+ source = toString gerrit_cfgfile;
+ matchers."gerrit_email_private_key".secret =
+ config.sops.secrets.gerrit_email_private_key.path;
+ matchers."gerrit_oauth_client_secret".secret =
+ config.sops.secrets.gerrit_oauth_client_secret.path;
+ owner = config.users.users.nobody.name;
+ group = "gerrit";
+ mode = "0040";
+ };
+
+ systemd.services.gerrit.preStart = base.config.systemd.services.gerrit.preStart + ''
+ rm etc/gerrit.config
+ ln -sfv ${config.scalpel.trafos."gerrit.conf".destination} etc/gerrit.config
+ '';
+ }
+ else {}
+ )
diff --git a/modules/git.nix b/modules/git.nix
index 747f686..d086cfd 100644
--- a/modules/git.nix
+++ b/modules/git.nix
@@ -1,56 +1,3 @@
{ config, pkgs, ... }: {
- environment.systemPackages = with pkgs; [ gh git ];
-
- services.gitea = {
- enable = false;
- settings.mailer = {
- ENABLED = true;
- FROM = "git@clicks.codes";
- PROTOCOL = "smtps";
- SMTP_ADDR = "smtp.coded.codes";
- SMTP_PORT = "465";
- USER = "git@clicks.codes";
- PASSWD = "ilIfASM@U5Z4XOEoH99gA8jPvGiOiEdx";
- HELO_HOSTNAME = "git.clicks.codes";
- };
- settings.service = {
- REGISTER_EMAIL_CONFIG = false;
- ENABLE_NOTIFY_MAIL = false;
- DISABLE_REGISTRATION = true;
- ENABLE_CAPTCHA = false;
- REQUIRE_SIGNIN_VIEW = false;
- DEFAULT_KEEP_EMAIL_PRIVATE = false;
- DEFAULT_ENABLE_TIMETRACKING = true;
- };
- settings.server = {
- ROOT_URL = "https://git.clicks.codes/";
- HTTP_PORT = 6064;
- SSH_DOMAIN = "ssh.clicks.codes";
- DOMAIN = "localhost";
- DISABLE_SSH = false;
- OFFLINE_MODE = false;
- };
- settings.openid.ENABLE_OPENID_SIGNIN = true;
- settings.log = {
- MODE = "console";
- LEVEL = "Info";
- ROUTER = "console";
- };
- settings.repository = {
- ENABLE_PUSH_CREATE_USER = true;
- ENABLE_PUSH_CREATE_ORG = true;
- };
- settings."repository.pull-request".DEFAULT_MERGE_STYLE = "merge";
- settings."repository.signing".DEFAULT_TRUST_MODEL = "committer";
- settings.security = {
- INSTALL_LOCK = true;
- PASSWORD_HASH_ALGO = "pbkdf2";
- };
- settings.indexer = {
- REPO_INDEXER_ENABLED = true;
- UPDATE_BUFFER_LEN = 20;
- MAX_FILE_SIZE = 1048576;
- };
- settings.session.PROVIDER = "file";
- };
+ environment.systemPackages = with pkgs; [ gh git git-review ];
}
diff --git a/modules/postgres.nix b/modules/postgres.nix
index d2844c1..d1f8a31 100644
--- a/modules/postgres.nix
+++ b/modules/postgres.nix
@@ -12,8 +12,10 @@
ensureDatabases = [
"vaultwarden"
+ "gerrit"
"privatebin"
"keycloak"
+ "nextcloud"
];
ensureUsers = [
@@ -37,6 +39,12 @@
};
}
{
+ name = "gerrit";
+ ensurePermissions = {
+ "DATABASE gerrit" = "ALL PRIVILEGES";
+ };
+ }
+ {
name = "vaultwarden";
ensurePermissions = {
"DATABASE vaultwarden" = "ALL PRIVILEGES";
@@ -48,6 +56,12 @@
"DATABASE privatebin" = "ALL PRIVILEGES";
};
}
+ {
+ name = "nextcloud";
+ ensurePermissions = {
+ "DATABASE nextcloud" = "ALL PRIVILEGES";
+ };
+ }
] ++ (map
(name: (
{
@@ -80,6 +94,7 @@
(lib.mkAfter (lib.pipe [
{ user = "clicks_grafana"; passwordFile = config.sops.secrets.clicks_grafana_db_password.path; }
{ user = "keycloak"; passwordFile = config.sops.secrets.clicks_keycloak_db_password.path; }
+ { user = "gerrit"; passwordFile = config.sops.secrets.clicks_gerrit_db_password.path; }
{ user = "vaultwarden"; passwordFile = config.sops.secrets.clicks_bitwarden_db_password.path; }
{ user = "privatebin"; passwordFile = config.sops.secrets.clicks_privatebin_db_password.path; }
] [
@@ -93,6 +108,7 @@
sops.secrets = lib.pipe [
"clicks_grafana_db_password"
"clicks_keycloak_db_password"
+ "clicks_gerrit_db_password"
"clicks_bitwarden_db_password"
"clicks_privatebin_db_password"
] [
diff --git a/secrets/gerrit.json b/secrets/gerrit.json
new file mode 100644
index 0000000..5af3821
--- /dev/null
+++ b/secrets/gerrit.json
@@ -0,0 +1,29 @@
+{
+ "gerrit_email_private_key": "ENC[AES256_GCM,data:SmQwCqV9yfMLeXz9GJKSUEhJXyk8xmfvBcjNusnXQexm15OavlKq1bE9K8xnt0XZ5RWEP/NKk7RKfEC77myqUaQeuoIP3Yb1pEj8jcHokWoc/f9spP2g/tlG8yf51RDC1YafGNBzoASnEnMaI0F7i/CtrdTDO8lBQ566dHOBa5U=,iv:TxzBnbdzMKuK0SC770/sq3O2JYbaik7RPuo9uWII5XM=,tag:H3Ni6Xk26x/hR42V4LYEkQ==,type:str]",
+ "gerrit_oauth_client_secret": "ENC[AES256_GCM,data:F48NTbAAP9naO/xg0QZA9BQQhOTBP4GiPoQMCW7HADo=,iv:tUw4P6lYSjNBC2lDPOlXsJKM45bkA8QTZQy6fY88GOU=,tag:6sCOI9T8cJe5h+TGZ5l5uQ==,type:str]",
+ "sops": {
+ "kms": null,
+ "gcp_kms": null,
+ "azure_kv": null,
+ "hc_vault": null,
+ "age": [
+ {
+ "recipient": "age15mv77dpnh5762gk5rsw2u79uza4tg8cu6r3nlwjudlzmdqqck3ss6mg9dy",
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSArWW9DWWgwL1lPYXpQUUts\ndUsrVlprT1p0NEZwdjdtRVRUeDVlWjJ4Y1RRCm91MXJEUjJuQ3RSS3NUa0JlZnRl\nYVVwQ0I1NytYU1JhTXBBek1zSVRhbHcKLS0tIHZrTGx3MWNxWmVxN1gvcDNCOWN2\naEhxS04rTXZNK3VkT2ovaVVhQ0Z4VzQKOc8Jptj+QHcSAoI1oVZzytbMEm8rmRRx\nr/TxROAYfD2iN+ppFNctXNIw0DrESW3fOaK3kzLr40F9TacHBEIRig==\n-----END AGE ENCRYPTED FILE-----\n"
+ },
+ {
+ "recipient": "age1m7k864feyuezllp2hj4edkccn36rthrvfw969j6f0l3c0mhh5emsnfx6pd",
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBrMGk2b0Myb3BDN3ZZeHNC\ncXprZG5vbjlnc3lmZ0FORzM4bkpZS2tnUWx3ClcvcmFHaDlJaE83V0ZIM0R2OTJ6\nbllUbjNESS9KQlMyaFZWQk1KY0VCeW8KLS0tIFF4bm5GenlNNUpYbytQaG1ndUxD\na1pnbzIvL1BLYWdieWk1Y2RnSlpPWWsKOXs73Z3Qg1D0yic2w57zZUdcYyLPfwCM\nbBSOEEYl3XgHfCNUBP9MjjekcgWZ6/aOfr+vs8ywt8/qPFvdc3bNEw==\n-----END AGE ENCRYPTED FILE-----\n"
+ },
+ {
+ "recipient": "age1fxxnmkeuqhhct93c43pwkzhuzzq8857s5hye6pgfpku70kjn4ecqtamfqr",
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBBMHUxMXVkMTJKaXMrYkFO\nS0hSSFptRFFHUDMxaTNwdldXVkhaT1FiVXdvCkt2YmtlbzBxaFNERnN1MkVRd3k0\nTzVObEpSM3JwcHpaWHdBZkNFb1ZMN2MKLS0tIGpleWI4ZDdOUXVXUnhLNUtianRF\nNXdIZjVXZ1BOdm9nNjRNTHJXRUJGWDAK0LFyd/uQWlExs0xnh/9EQimY9GX+BrFa\nHqQw9MEf2sXquLN+JOUQJFB3apIHP1V330j3dAGHuK4CVtfAd7UwNQ==\n-----END AGE ENCRYPTED FILE-----\n"
+ }
+ ],
+ "lastmodified": "2023-10-08T20:27:38Z",
+ "mac": "ENC[AES256_GCM,data:757DUB58m953xGaNUJSezST1iXn/mOdh4RW/anjikBesJqgjToLwMD6AZHDQq+MgJMVdjOFhRAyQ+6UjuiWJYWCRiNUxy8dI9j6V4T7AnGDDkO9Pp3KfAVkIarT28BRqgO4MSr2YoB0yrQYPJScVPVjulaKOHEJeq3TSvgefaxo=,iv:ss8f3v6Z/9hiI1Ju/KTLSW91NSaoYNZ8opuKLDQnqvg=,tag:LmPKGcI8v9tq9exfZQxwXA==,type:str]",
+ "pgp": null,
+ "unencrypted_suffix": "_unencrypted",
+ "version": "3.7.3"
+ }
+}
\ No newline at end of file