Compare commits

...

12 commits

Author SHA1 Message Date
Ilya K ab50b0958f Set up alertmanager-hookshot-adapter 2024-07-09 12:10:28 +03:00
Ilya K c382b3b282 Set up admins + DNS for hookshot 2024-07-09 12:10:17 +03:00
Ilya K 87725c53e2 Add Grapevine Matrix server and matrix-hookshot
It doesn't want to work.
2024-07-09 10:58:06 +03:00
raito 3828721e4f services/netbox: enable OIDC via Lix SSO
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-07-09 02:45:58 +02:00
Luke Granger-Brown 8a9ff8c40d services/gerrit: migrate to Gerrit from the-distro/nix-gerrit flake 2024-07-08 23:30:59 +01:00
raito 48579e8818 feat: add gdb to sysadmin tooling
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-07-08 22:10:06 +00:00
raito 8fe33b4e46 feat: add perf, pwru and various sysadmin tools to bagel-box
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-07-08 22:10:06 +00:00
Luke Granger-Brown d4e9dcc2a6 admins: provision lukegb
hello I can be trusted with your infrastructure
2024-07-08 21:55:41 +00:00
Pierre Bourdon 7f46e5d9a4
services: add ofborg, currently running rabbitmq only 2024-07-08 23:55:11 +02:00
raito 512cfdb43e fix: downgrade mina sshd due to broken PQC algorithm
https://cl.tvl.fyi/c/depot/+/11965

This breaks it with "ssh_dispatch_run_fatal: Connection to
2a01:4f8:242:5b21:0:feed:edef:beef port 29418: incorrect signature"

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-07-08 15:59:31 +02:00
raito 82395ec8ce Merge pull request 'pkgs/gerrit: update to 3.10.0' (#34) from upgrade-gerrit-differently into main
Reviewed-on: the-distro/bagel-infra#34
2024-07-08 12:21:21 +00:00
Luke Granger-Brown dd6ee53bfe pkgs/gerrit: update to 3.10.0
This does a bit more than advertised, since this also switches to a
different set of Bazel package building infrastructure that I'm hoping
will be more extensible than buildBazelPackage as it exists in nixpkgs
today.

In any case, the FOD here _seems_ to be much more stable than that
previously produced by the old approach, but no promises :)
2024-07-08 02:44:05 +01:00
33 changed files with 625 additions and 1114 deletions

View file

@ -7,6 +7,7 @@ in {
keys.users.raito ++ keys.users.raito ++
keys.users.maxine ++ keys.users.maxine ++
keys.users.jade ++ keys.users.jade ++
keys.users.lukegb ++
[ [
# more raito # more raito
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDcEkYM1r8QVNM/G5CxJInEdoBCWjEHHDdHlzDYNSUIdHHsn04QY+XI67AdMCm8w30GZnLUIj5RiJEWXREUApby0GrfxGGcy8otforygfgtmuUKAUEHdU2MMwrQI7RtTZ8oQ0USRGuqvmegxz3l5caVU7qGvBllJ4NUHXrkZSja2/51vq80RF4MKkDGiz7xUTixI2UcBwQBCA/kQedKV9G28EH+1XfvePqmMivZjl+7VyHsgUVj9eRGA1XWFw59UPZG8a7VkxO/Eb3K9NF297HUAcFMcbY6cPFi9AaBgu3VC4eetDnoN/+xT1owiHi7BReQhGAy/6cdf7C/my5ehZwD" "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDcEkYM1r8QVNM/G5CxJInEdoBCWjEHHDdHlzDYNSUIdHHsn04QY+XI67AdMCm8w30GZnLUIj5RiJEWXREUApby0GrfxGGcy8otforygfgtmuUKAUEHdU2MMwrQI7RtTZ8oQ0USRGuqvmegxz3l5caVU7qGvBllJ4NUHXrkZSja2/51vq80RF4MKkDGiz7xUTixI2UcBwQBCA/kQedKV9G28EH+1XfvePqmMivZjl+7VyHsgUVj9eRGA1XWFw59UPZG8a7VkxO/Eb3K9NF297HUAcFMcbY6cPFi9AaBgu3VC4eetDnoN/+xT1owiHi7BReQhGAy/6cdf7C/my5ehZwD"

View file

@ -16,5 +16,6 @@
"sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIKYljH8iPMrH00lOb3ETxRrZimdKzPPEdsJQ5D5ovtOwAAAACnNzaDpzc2hrZXk= ssh:sshkey" "sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIKYljH8iPMrH00lOb3ETxRrZimdKzPPEdsJQ5D5ovtOwAAAACnNzaDpzc2hrZXk= ssh:sshkey"
"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBO4idMfdJxDJuBNOid60d4I+qxj09RHt+YkCYV2eXt6tGrEXg+S8hTQusy/SqooiXUH9pt4tea2RuBPN9+UwrH0= type-a yubikey slot 9a" "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBO4idMfdJxDJuBNOid60d4I+qxj09RHt+YkCYV2eXt6tGrEXg+S8hTQusy/SqooiXUH9pt4tea2RuBPN9+UwrH0= type-a yubikey slot 9a"
]; ];
lukegb = [ ''cert-authority,principals="lukegb" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEqNOwlR7Qa8cbGpDfSCOweDPbAGQOZIcoRgh6s/J8DR'' ];
}; };
} }

View file

@ -20,6 +20,8 @@ in
bcc bcc
tcpdump tcpdump
ncdu ncdu
# Useful to invoke `coredumpctl gdb`
gdb
] ++ lib.optional (lib.hasAttr "pwru" pkgs) pkgs.pwru; ] ++ lib.optional (lib.hasAttr "pwru" pkgs) pkgs.pwru;
}; };
} }

View file

@ -23,6 +23,29 @@
"type": "github" "type": "github"
} }
}, },
"attic": {
"inputs": {
"crane": "crane",
"flake-compat": "flake-compat_2",
"flake-utils": "flake-utils_2",
"nixpkgs": "nixpkgs",
"nixpkgs-stable": "nixpkgs-stable"
},
"locked": {
"lastModified": 1711742460,
"narHash": "sha256-0O4v6e4a1toxXZ2gf5INhg4WPE5C5T+SVvsBt+45Mcc=",
"owner": "zhaofengli",
"repo": "attic",
"rev": "4dbdbee45728d8ce5788db6461aaaa89d98081f0",
"type": "github"
},
"original": {
"owner": "zhaofengli",
"ref": "main",
"repo": "attic",
"type": "github"
}
},
"bats-assert": { "bats-assert": {
"flake": false, "flake": false,
"locked": { "locked": {
@ -78,6 +101,50 @@
"type": "github" "type": "github"
} }
}, },
"crane": {
"inputs": {
"nixpkgs": [
"grapevine",
"attic",
"nixpkgs"
]
},
"locked": {
"lastModified": 1702918879,
"narHash": "sha256-tWJqzajIvYcaRWxn+cLUB9L9Pv4dQ3Bfit/YjU5ze3g=",
"owner": "ipetkov",
"repo": "crane",
"rev": "7195c00c272fdd92fc74e7d5a0a2844b9fadb2fb",
"type": "github"
},
"original": {
"owner": "ipetkov",
"repo": "crane",
"type": "github"
}
},
"crane_2": {
"inputs": {
"nixpkgs": [
"grapevine",
"nixpkgs"
]
},
"locked": {
"lastModified": 1716569590,
"narHash": "sha256-5eDbq8TuXFGGO3mqJFzhUbt5zHVTf5zilQoyW5jnJwo=",
"owner": "ipetkov",
"repo": "crane",
"rev": "109987da061a1bf452f435f1653c47511587d919",
"type": "github"
},
"original": {
"owner": "ipetkov",
"ref": "master",
"repo": "crane",
"type": "github"
}
},
"darwin": { "darwin": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
@ -100,6 +167,29 @@
"type": "github" "type": "github"
} }
}, },
"fenix": {
"inputs": {
"nixpkgs": [
"grapevine",
"nixpkgs"
],
"rust-analyzer-src": "rust-analyzer-src"
},
"locked": {
"lastModified": 1716359173,
"narHash": "sha256-pYcjP6Gy7i6jPWrjiWAVV0BCQp+DdmGaI/k65lBb/kM=",
"owner": "nix-community",
"repo": "fenix",
"rev": "b6fc5035b28e36a98370d0eac44f4ef3fd323df6",
"type": "github"
},
"original": {
"owner": "nix-community",
"ref": "main",
"repo": "fenix",
"type": "github"
}
},
"flake-compat": { "flake-compat": {
"flake": false, "flake": false,
"locked": { "locked": {
@ -117,6 +207,39 @@
} }
}, },
"flake-compat_2": { "flake-compat_2": {
"flake": false,
"locked": {
"lastModified": 1673956053,
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-compat_3": {
"flake": false,
"locked": {
"lastModified": 1696426674,
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"type": "github"
},
"original": {
"owner": "edolstra",
"ref": "master",
"repo": "flake-compat",
"type": "github"
}
},
"flake-compat_4": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1696426674, "lastModified": 1696426674,
@ -148,6 +271,40 @@
} }
}, },
"flake-utils_2": { "flake-utils_2": {
"locked": {
"lastModified": 1667395993,
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_3": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github"
},
"original": {
"owner": "numtide",
"ref": "main",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_4": {
"locked": { "locked": {
"lastModified": 1634851050, "lastModified": 1634851050,
"narHash": "sha256-N83GlSGPJJdcqhUxSCS/WwW5pksYf3VP1M13cDRTSVA=", "narHash": "sha256-N83GlSGPJJdcqhUxSCS/WwW5pksYf3VP1M13cDRTSVA=",
@ -162,6 +319,34 @@
"type": "github" "type": "github"
} }
}, },
"grapevine": {
"inputs": {
"attic": "attic",
"crane": "crane_2",
"fenix": "fenix",
"flake-compat": "flake-compat_3",
"flake-utils": "flake-utils_3",
"nix-filter": "nix-filter",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"host": "gitlab.computer.surgery",
"lastModified": 1719530301,
"narHash": "sha256-jteW09FEGmI0scXvs8naqbORoEo8TlzY1QjOZt3rdKY=",
"owner": "matrix",
"repo": "grapevine-fork",
"rev": "b05c91b13e215816da1d7b45862952c2c73b8d55",
"type": "gitlab"
},
"original": {
"host": "gitlab.computer.surgery",
"owner": "matrix",
"repo": "grapevine-fork",
"type": "gitlab"
}
},
"home-manager": { "home-manager": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
@ -206,7 +391,7 @@
}, },
"nix": { "nix": {
"inputs": { "inputs": {
"flake-compat": "flake-compat_2", "flake-compat": "flake-compat_4",
"nix2container": "nix2container", "nix2container": "nix2container",
"nixpkgs": [ "nixpkgs": [
"hydra", "hydra",
@ -229,6 +414,42 @@
"url": "https://git@git.lix.systems/lix-project/lix" "url": "https://git@git.lix.systems/lix-project/lix"
} }
}, },
"nix-filter": {
"locked": {
"lastModified": 1710156097,
"narHash": "sha256-1Wvk8UP7PXdf8bCCaEoMnOT1qe5/Duqgj+rL8sRQsSM=",
"owner": "numtide",
"repo": "nix-filter",
"rev": "3342559a24e85fc164b295c3444e8a139924675b",
"type": "github"
},
"original": {
"owner": "numtide",
"ref": "main",
"repo": "nix-filter",
"type": "github"
}
},
"nix-gerrit": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1720472191,
"narHash": "sha256-v42zXC8syDpAuF3cdpwCxU9DsbcDmYVKCfkDq4ZBufU=",
"ref": "refs/heads/main",
"rev": "eb589c659e494e5fcb5b47b75be9984ae87a500a",
"revCount": 4,
"type": "git",
"url": "https://git.lix.systems/the-distro/nix-gerrit.git"
},
"original": {
"type": "git",
"url": "https://git.lix.systems/the-distro/nix-gerrit.git"
}
},
"nix2container": { "nix2container": {
"flake": false, "flake": false,
"locked": { "locked": {
@ -247,11 +468,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1719082008, "lastModified": 1711401922,
"narHash": "sha256-jHJSUH619zBQ6WdC21fFAlDxHErKVDJ5fpN0Hgx4sjs=", "narHash": "sha256-QoQqXoj8ClGo0sqD/qWKFWezgEwUL0SUh37/vY2jNhc=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "9693852a2070b398ee123a329e68f0dab5526681", "rev": "07262b18b97000d16a4bdb003418bd2fb067a932",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -277,7 +498,39 @@
"type": "github" "type": "github"
} }
}, },
"nixpkgs-stable": {
"locked": {
"lastModified": 1711460390,
"narHash": "sha256-akSgjDZL6pVHEfSE6sz1DNSXuYX6hq+P/1Z5IoYWs7E=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "44733514b72e732bd49f5511bd0203dea9b9a434",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-23.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": { "nixpkgs_2": {
"locked": {
"lastModified": 1719082008,
"narHash": "sha256-jHJSUH619zBQ6WdC21fFAlDxHErKVDJ5fpN0Hgx4sjs=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "9693852a2070b398ee123a329e68f0dab5526681",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_3": {
"locked": { "locked": {
"lastModified": 1636823747, "lastModified": 1636823747,
"narHash": "sha256-oWo1nElRAOZqEf90Yek2ixdHyjD+gqtS/pAgwaQ9UhQ=", "narHash": "sha256-oWo1nElRAOZqEf90Yek2ixdHyjD+gqtS/pAgwaQ9UhQ=",
@ -312,15 +565,34 @@
"inputs": { "inputs": {
"agenix": "agenix", "agenix": "agenix",
"colmena": "colmena", "colmena": "colmena",
"grapevine": "grapevine",
"hydra": "hydra", "hydra": "hydra",
"lix": [ "lix": [
"hydra", "hydra",
"nix" "nix"
], ],
"nixpkgs": "nixpkgs", "nix-gerrit": "nix-gerrit",
"nixpkgs": "nixpkgs_2",
"terranix": "terranix" "terranix": "terranix"
} }
}, },
"rust-analyzer-src": {
"flake": false,
"locked": {
"lastModified": 1716107283,
"narHash": "sha256-NJgrwLiLGHDrCia5AeIvZUHUY7xYGVryee0/9D3Ir1I=",
"owner": "rust-lang",
"repo": "rust-analyzer",
"rev": "21ec8f523812b88418b2bfc64240c62b3dd967bd",
"type": "github"
},
"original": {
"owner": "rust-lang",
"ref": "nightly",
"repo": "rust-analyzer",
"type": "github"
}
},
"stable": { "stable": {
"locked": { "locked": {
"lastModified": 1696039360, "lastModified": 1696039360,
@ -352,12 +624,27 @@
"type": "github" "type": "github"
} }
}, },
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"terranix": { "terranix": {
"inputs": { "inputs": {
"bats-assert": "bats-assert", "bats-assert": "bats-assert",
"bats-support": "bats-support", "bats-support": "bats-support",
"flake-utils": "flake-utils_2", "flake-utils": "flake-utils_4",
"nixpkgs": "nixpkgs_2", "nixpkgs": "nixpkgs_3",
"terranix-examples": "terranix-examples" "terranix-examples": "terranix-examples"
}, },
"locked": { "locked": {

View file

@ -14,7 +14,18 @@
hydra.url = "git+https://git.lix.systems/lix-project/hydra.git"; hydra.url = "git+https://git.lix.systems/lix-project/hydra.git";
hydra.inputs.nixpkgs.follows = "nixpkgs"; hydra.inputs.nixpkgs.follows = "nixpkgs";
nix-gerrit.url = "git+https://git.lix.systems/the-distro/nix-gerrit.git";
nix-gerrit.inputs.nixpkgs.follows = "nixpkgs";
lix.follows = "hydra/nix"; lix.follows = "hydra/nix";
grapevine = {
type = "gitlab";
host = "gitlab.computer.surgery";
owner = "matrix";
repo = "grapevine-fork";
inputs.nixpkgs.follows = "nixpkgs";
};
}; };
outputs = { self, nixpkgs, terranix, ... } @ inputs: outputs = { self, nixpkgs, terranix, ... } @ inputs:
@ -25,6 +36,7 @@
overlays = [ overlays = [
inputs.hydra.overlays.default inputs.hydra.overlays.default
inputs.lix.overlays.default inputs.lix.overlays.default
inputs.nix-gerrit.overlays.default
]; ];
}; };
lib = pkgs.lib; lib = pkgs.lib;
@ -69,6 +81,7 @@
overlays = [ overlays = [
inputs.hydra.overlays.default inputs.hydra.overlays.default
inputs.lix.overlays.default inputs.lix.overlays.default
inputs.nix-gerrit.overlays.default
]; ];
}; };
meta.specialArgs.inputs = inputs; meta.specialArgs.inputs = inputs;

View file

@ -39,8 +39,12 @@
hydra.enable = true; hydra.enable = true;
hydra.dbi = "dbi:Pg:dbname=hydra;user=hydra"; hydra.dbi = "dbi:Pg:dbname=hydra;user=hydra";
ofborg.enable = true;
}; };
bagel.sysadmin.enable = true;
security.acme.acceptTerms = true; security.acme.acceptTerms = true;
security.acme.defaults.email = "infra@forkos.org"; security.acme.defaults.email = "infra@forkos.org";

View file

@ -24,6 +24,13 @@
bagel.services.prometheus.enable = true; bagel.services.prometheus.enable = true;
bagel.services.loki.enable = true; bagel.services.loki.enable = true;
bagel.services.grafana.enable = true; bagel.services.grafana.enable = true;
bagel.services.grapevine.enable = true;
bagel.services.hookshot = {
enable = true;
admins = [
"@k900:0upti.me"
];
};
i18n.defaultLocale = "fr_FR.UTF-8"; i18n.defaultLocale = "fr_FR.UTF-8";

View file

@ -1,3 +1 @@
[ []
(import ./gerrit.nix)
]

View file

@ -1,11 +0,0 @@
self: super: {
buildGerrit = self.callPackage ../pkgs/gerrit { };
gerrit = self.buildGerrit { };
buildGerritBazelPlugin = self.callPackage ../pkgs/gerrit_plugins/builder.nix {
inherit (self) buildGerrit;
};
gerritPlugins = {
code-owners = self.callPackage ../pkgs/gerrit_plugins/code-owners { };
oauth = self.callPackage ../pkgs/gerrit_plugins/oauth { };
};
}

View file

@ -1,37 +0,0 @@
From 084e4f92fb58f7cd85303ba602fb3c40133c8fcc Mon Sep 17 00:00:00 2001
From: Luke Granger-Brown <git@lukegb.com>
Date: Thu, 2 Jul 2020 23:02:32 +0100
Subject: [PATCH 1/3] Syntax highlight nix
---
.../app/embed/diff/gr-syntax-layer/gr-syntax-layer-worker.ts | 1 +
resources/com/google/gerrit/server/mime/mime-types.properties | 1 +
2 files changed, 2 insertions(+)
diff --git a/polygerrit-ui/app/embed/diff/gr-syntax-layer/gr-syntax-layer-worker.ts b/polygerrit-ui/app/embed/diff/gr-syntax-layer/gr-syntax-layer-worker.ts
index a9f88bdd81..385249f280 100644
--- a/polygerrit-ui/app/embed/diff/gr-syntax-layer/gr-syntax-layer-worker.ts
+++ b/polygerrit-ui/app/embed/diff/gr-syntax-layer/gr-syntax-layer-worker.ts
@@ -93,6 +93,7 @@ const LANGUAGE_MAP = new Map<string, string>([
['text/x-vhdl', 'vhdl'],
['text/x-yaml', 'yaml'],
['text/vbscript', 'vbscript'],
+ ['text/x-nix', 'nix'],
]);
const CLASS_PREFIX = 'gr-diff gr-syntax gr-syntax-';
diff --git a/resources/com/google/gerrit/server/mime/mime-types.properties b/resources/com/google/gerrit/server/mime/mime-types.properties
index 2f9561ba2e..739818ec05 100644
--- a/resources/com/google/gerrit/server/mime/mime-types.properties
+++ b/resources/com/google/gerrit/server/mime/mime-types.properties
@@ -149,6 +149,7 @@ mscin = text/x-mscgen
msgenny = text/x-msgenny
nb = text/x-mathematica
nginx.conf = text/x-nginx-conf
+nix = text/x-nix
nsh = text/x-nsis
nsi = text/x-nsis
nt = text/n-triples
--
2.37.3

View file

@ -1,37 +0,0 @@
From aedf8ac8fa5113843bcd83ff85e2d9f3bffdb16c Mon Sep 17 00:00:00 2001
From: Luke Granger-Brown <git@lukegb.com>
Date: Thu, 2 Jul 2020 23:02:43 +0100
Subject: [PATCH 2/3] Syntax highlight rules.pl
---
.../app/embed/diff/gr-syntax-layer/gr-syntax-layer-worker.ts | 1 +
resources/com/google/gerrit/server/mime/mime-types.properties | 1 +
2 files changed, 2 insertions(+)
diff --git a/polygerrit-ui/app/embed/diff/gr-syntax-layer/gr-syntax-layer-worker.ts b/polygerrit-ui/app/embed/diff/gr-syntax-layer/gr-syntax-layer-worker.ts
index 385249f280..7cb3068494 100644
--- a/polygerrit-ui/app/embed/diff/gr-syntax-layer/gr-syntax-layer-worker.ts
+++ b/polygerrit-ui/app/embed/diff/gr-syntax-layer/gr-syntax-layer-worker.ts
@@ -68,6 +68,7 @@ const LANGUAGE_MAP = new Map<string, string>([
['text/x-perl', 'perl'],
['text/x-pgsql', 'pgsql'], // postgresql
['text/x-php', 'php'],
+ ['text/x-prolog', 'prolog'],
['text/x-properties', 'properties'],
['text/x-protobuf', 'protobuf'],
['text/x-puppet', 'puppet'],
diff --git a/resources/com/google/gerrit/server/mime/mime-types.properties b/resources/com/google/gerrit/server/mime/mime-types.properties
index 739818ec05..58eb727bf9 100644
--- a/resources/com/google/gerrit/server/mime/mime-types.properties
+++ b/resources/com/google/gerrit/server/mime/mime-types.properties
@@ -200,6 +200,7 @@ rq = application/sparql-query
rs = text/x-rustsrc
rss = application/xml
rst = text/x-rst
+rules.pl = text/x-prolog
README.md = text/x-gfm
s = text/x-gas
sas = text/x-sas
--
2.37.3

View file

@ -1,215 +0,0 @@
From f49c50ca9a84ca374b7bd91c171bbea0457f2c7a Mon Sep 17 00:00:00 2001
From: Luke Granger-Brown <git@lukegb.com>
Date: Thu, 2 Jul 2020 23:03:02 +0100
Subject: [PATCH 3/3] Add titles to CLs over HTTP
---
.../gerrit/httpd/raw/IndexHtmlUtil.java | 13 +++-
.../google/gerrit/httpd/raw/IndexServlet.java | 8 ++-
.../google/gerrit/httpd/raw/StaticModule.java | 5 +-
.../gerrit/httpd/raw/TitleComputer.java | 67 +++++++++++++++++++
.../gerrit/httpd/raw/PolyGerritIndexHtml.soy | 4 +-
5 files changed, 89 insertions(+), 8 deletions(-)
create mode 100644 java/com/google/gerrit/httpd/raw/TitleComputer.java
diff --git a/java/com/google/gerrit/httpd/raw/IndexHtmlUtil.java b/java/com/google/gerrit/httpd/raw/IndexHtmlUtil.java
index 72bfe40c3b..439bd73b44 100644
--- a/java/com/google/gerrit/httpd/raw/IndexHtmlUtil.java
+++ b/java/com/google/gerrit/httpd/raw/IndexHtmlUtil.java
@@ -41,6 +41,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
@@ -62,13 +63,14 @@ public class IndexHtmlUtil {
String faviconPath,
Map<String, String[]> urlParameterMap,
Function<String, SanitizedContent> urlInScriptTagOrdainer,
- String requestedURL)
+ String requestedURL,
+ TitleComputer titleComputer)
throws URISyntaxException, RestApiException {
ImmutableMap.Builder<String, Object> data = ImmutableMap.builder();
data.putAll(
staticTemplateData(
canonicalURL, cdnPath, faviconPath, urlParameterMap, urlInScriptTagOrdainer))
- .putAll(dynamicTemplateData(gerritApi, requestedURL));
+ .putAll(dynamicTemplateData(gerritApi, requestedURL, titleComputer));
Set<String> enabledExperiments = new HashSet<>();
enabledExperiments.addAll(experimentFeatures.getEnabledExperimentFeatures());
// Add all experiments enabled through url
@@ -81,7 +83,8 @@ public class IndexHtmlUtil {
/** Returns dynamic parameters of {@code index.html}. */
public static ImmutableMap<String, Object> dynamicTemplateData(
- GerritApi gerritApi, String requestedURL) throws RestApiException, URISyntaxException {
+ GerritApi gerritApi, String requestedURL, TitleComputer titleComputer)
+ throws RestApiException, URISyntaxException {
ImmutableMap.Builder<String, Object> data = ImmutableMap.builder();
Map<String, SanitizedContent> initialData = new HashMap<>();
Server serverApi = gerritApi.config().server();
@@ -129,6 +132,10 @@ public class IndexHtmlUtil {
}
data.put("gerritInitialData", initialData);
+
+ Optional<String> title = titleComputer.computeTitle(requestedURL);
+ title.ifPresent(s -> data.put("title", s));
+
return data.build();
}
diff --git a/java/com/google/gerrit/httpd/raw/IndexServlet.java b/java/com/google/gerrit/httpd/raw/IndexServlet.java
index fcb821e5ae..e1464b992b 100644
--- a/java/com/google/gerrit/httpd/raw/IndexServlet.java
+++ b/java/com/google/gerrit/httpd/raw/IndexServlet.java
@@ -48,13 +48,15 @@ public class IndexServlet extends HttpServlet {
private final ExperimentFeatures experimentFeatures;
private final SoySauce soySauce;
private final Function<String, SanitizedContent> urlOrdainer;
+ private TitleComputer titleComputer;
IndexServlet(
@Nullable String canonicalUrl,
@Nullable String cdnPath,
@Nullable String faviconPath,
GerritApi gerritApi,
- ExperimentFeatures experimentFeatures) {
+ ExperimentFeatures experimentFeatures,
+ TitleComputer titleComputer) {
this.canonicalUrl = canonicalUrl;
this.cdnPath = cdnPath;
this.faviconPath = faviconPath;
@@ -69,6 +71,7 @@ public class IndexServlet extends HttpServlet {
(s) ->
UnsafeSanitizedContentOrdainer.ordainAsSafe(
s, SanitizedContent.ContentKind.TRUSTED_RESOURCE_URI);
+ this.titleComputer = titleComputer;
}
@Override
@@ -86,7 +89,8 @@ public class IndexServlet extends HttpServlet {
faviconPath,
parameterMap,
urlOrdainer,
- getRequestUrl(req));
+ getRequestUrl(req),
+ titleComputer);
renderer = soySauce.renderTemplate("com.google.gerrit.httpd.raw.Index").setData(templateData);
} catch (URISyntaxException | RestApiException e) {
throw new IOException(e);
diff --git a/java/com/google/gerrit/httpd/raw/StaticModule.java b/java/com/google/gerrit/httpd/raw/StaticModule.java
index 15dcf42e0e..9f56bf33ce 100644
--- a/java/com/google/gerrit/httpd/raw/StaticModule.java
+++ b/java/com/google/gerrit/httpd/raw/StaticModule.java
@@ -241,10 +241,11 @@ public class StaticModule extends ServletModule {
@CanonicalWebUrl @Nullable String canonicalUrl,
@GerritServerConfig Config cfg,
GerritApi gerritApi,
- ExperimentFeatures experimentFeatures) {
+ ExperimentFeatures experimentFeatures,
+ TitleComputer titleComputer) {
String cdnPath = options.devCdn().orElse(cfg.getString("gerrit", null, "cdnPath"));
String faviconPath = cfg.getString("gerrit", null, "faviconPath");
- return new IndexServlet(canonicalUrl, cdnPath, faviconPath, gerritApi, experimentFeatures);
+ return new IndexServlet(canonicalUrl, cdnPath, faviconPath, gerritApi, experimentFeatures, titleComputer);
}
@Provides
diff --git a/java/com/google/gerrit/httpd/raw/TitleComputer.java b/java/com/google/gerrit/httpd/raw/TitleComputer.java
new file mode 100644
index 0000000000..8fd2053ad0
--- /dev/null
+++ b/java/com/google/gerrit/httpd/raw/TitleComputer.java
@@ -0,0 +1,67 @@
+package com.google.gerrit.httpd.raw;
+
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.restapi.change.ChangesCollection;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Optional;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+@Singleton
+public class TitleComputer {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ @Inject
+ public TitleComputer(Provider<ChangesCollection> changes) {
+ this.changes = changes;
+ }
+
+ public Optional<String> computeTitle(String requestedURI) {
+ URL url = null;
+ try {
+ url = new URL(requestedURI);
+ } catch (MalformedURLException e) {
+ logger.atWarning().log("Failed to turn %s into a URL.", requestedURI);
+ return Optional.empty();
+ }
+
+ // Try to turn this into a change.
+ Optional<Change.Id> changeId = tryExtractChange(url.getPath());
+ if (changeId.isPresent()) {
+ return titleFromChangeId(changeId.get());
+ }
+
+ return Optional.empty();
+ }
+
+ private static final Pattern extractChangeIdRegex = Pattern.compile("^/(?:c/.*/\\+/)?(?<changeId>[0-9]+)(?:/[0-9]+)?(?:/.*)?$");
+ private final Provider<ChangesCollection> changes;
+
+ private Optional<Change.Id> tryExtractChange(String path) {
+ Matcher m = extractChangeIdRegex.matcher(path);
+ if (!m.matches()) {
+ return Optional.empty();
+ }
+ return Change.Id.tryParse(m.group("changeId"));
+ }
+
+ private Optional<String> titleFromChangeId(Change.Id changeId) {
+ ChangesCollection changesCollection = changes.get();
+ try {
+ ChangeResource changeResource = changesCollection.parse(changeId);
+ return Optional.of(changeResource.getChange().getSubject());
+ } catch (ResourceConflictException | ResourceNotFoundException | PermissionBackendException e) {
+ return Optional.empty();
+ }
+ }
+}
diff --git a/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy b/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
index dbfef44dfe..347ee75aab 100644
--- a/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
+++ b/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
@@ -33,10 +33,12 @@
{@param? defaultDashboardHex: ?}
{@param? dashboardQuery: ?}
{@param? userIsAuthenticated: ?}
+ {@param? title: ?}
<!DOCTYPE html>{\n}
<html lang="en">{\n}
<meta charset="utf-8">{\n}
- <meta name="description" content="Gerrit Code Review">{\n}
+ {if $title}<title>{$title} · Gerrit Code Review</title>{\n}{/if}
+ <meta name="description" content="{if $title}{$title} · {/if}Gerrit Code Review">{\n}
<meta name="referrer" content="never">{\n}
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">{\n}
--
2.37.3

View file

@ -1,152 +0,0 @@
{ buildFHSUserEnv, writeShellScriptBin, buildBazelPackage, fetchgit, unzip }:
{ name ? "gerrit-${version}", version ? "3.9.1", src ? (fetchgit {
url = "https://gerrit.googlesource.com/gerrit";
rev = "620a819cbf3c64fff7a66798822775ad42c91d8e";
branchName = "v${version}";
sha256 = "sha256:1mdxbgnx3mpxand4wq96ic38bb4yh45q271h40jrk7dk23sgmz02";
fetchSubmodules = true;
}), bazelTargets ? [ "release" "api-skip-javadoc" ]
}:
let
bazelRunScript = writeShellScriptBin "bazel-run" ''
yarn config set cache-folder "$bazelOut/external/yarn_cache"
export HOME="$bazelOut/external/home"
mkdir -p "$bazelOut/external/home"
exec /bin/bazel "$@"
'';
bazelTop = buildFHSUserEnv {
name = "bazel";
targetPkgs = pkgs: [
(pkgs.bazel_5.override { enableNixHacks = true; })
pkgs.jdk17_headless
pkgs.zlib
pkgs.python3
pkgs.curl
pkgs.nodejs
pkgs.yarn
pkgs.git
bazelRunScript
];
runScript = "/bin/bazel-run";
};
bazel = bazelTop // { override = x: bazelTop; };
in
buildBazelPackage {
inherit name version src;
patches = [
./0001-Syntax-highlight-nix.patch
./0002-Syntax-highlight-rules.pl.patch
./0003-Add-titles-to-CLs-over-HTTP.patch
];
inherit bazel bazelTargets;
bazelFlags = [
"--repository_cache="
"--disk_cache="
];
removeRulesCC = false;
fetchConfigured = true;
fetchAttrs = {
sha256 = "sha256-rsYQR6/RO5NM3/fnB3lEmbz876B59QWxWpE3M/Z4rK4=";
preBuild = ''
rm .bazelversion
'';
installPhase = ''
runHook preInstall
# Remove all built in external workspaces, Bazel will recreate them when building
rm -rf $bazelOut/external/{bazel_tools,\@bazel_tools.marker}
rm -rf $bazelOut/external/{embedded_jdk,\@embedded_jdk.marker}
rm -rf $bazelOut/external/{local_config_cc,\@local_config_cc.marker}
rm -rf $bazelOut/external/{local_*,\@local_*.marker}
# Clear markers
find $bazelOut/external -name '@*\.marker' -exec sh -c 'echo > {}' \;
# Remove all vcs files
rm -rf $(find $bazelOut/external -type d -name .git)
rm -rf $(find $bazelOut/external -type d -name .svn)
rm -rf $(find $bazelOut/external -type d -name .hg)
# Removing top-level symlinks along with their markers.
# This is needed because they sometimes point to temporary paths (?).
# For example, in Tensorflow-gpu build:
#sha256:06bmzbcb9717s4b016kcbn8nr9pgaz04i8bnzg7ybkbdwpl8vxvv"; platforms -> NIX_BUILD_TOP/tmp/install/35282f5123611afa742331368e9ae529/_embedded_binaries/platforms
find $bazelOut/external -maxdepth 1 -type l | while read symlink; do
name="$(basename "$symlink")"
rm -rf "$symlink" "$bazelOut/external/@$name.marker"
done
# Patching symlinks to remove build directory reference
find $bazelOut/external -type l | while read symlink; do
new_target="$(readlink "$symlink" | sed "s,$NIX_BUILD_TOP,NIX_BUILD_TOP,")"
rm "$symlink"
ln -sf "$new_target" "$symlink"
done
echo '${bazel.name}' > $bazelOut/external/.nix-bazel-version
# Gerrit fixups:
# Normalize permissions on .yarn-{tarball,metadata} files
test -d $bazelOut/external/yarn_cache && find $bazelOut/external/yarn_cache \( -name .yarn-tarball.tgz -or -name .yarn-metadata.json \) -exec chmod 644 {} +
mkdir $bazelOut/_bits/
find . -name node_modules -prune -print | while read d; do
echo "$d" "$(dirname $d)"
mkdir -p $bazelOut/_bits/$(dirname $d)
cp -R "$d" "$bazelOut/_bits/$(dirname $d)/node_modules"
done
(cd $bazelOut/ && tar czf $out --sort=name --mtime='@1' --owner=0 --group=0 --numeric-owner external/ _bits/)
runHook postInstall
'';
};
buildAttrs = {
preConfigure = ''
rm .bazelversion
[ "$(ls -A $bazelOut/_bits)" ] && cp -R $bazelOut/_bits/* ./ || true
'';
postPatch = ''
# Disable all errorprone checks, since we might be using a different version.
sed -i \
-e '/-Xep:/d' \
-e '/-XepExcludedPaths:/a "-XepDisableAllChecks",' \
tools/BUILD
'';
installPhase = ''
mkdir -p "$out"/webapps/ "$out"/share/api/
cp bazel-bin/release.war "$out"/webapps/gerrit-${version}.war
unzip bazel-bin/api-skip-javadoc.zip -d "$out"/share/api
'';
nativeBuildInputs = [
unzip
];
};
passthru = {
# A list of plugins that are part of the gerrit.war file.
# Use `java -jar gerrit.war ls | grep -Po '(?<=plugins/)[^.]+' | sed -e 's,^,",' -e 's,$,",' | sort` to generate that list.
plugins = [
"codemirror-editor"
"commit-message-length-validator"
"delete-project"
"download-commands"
"gitiles"
"hooks"
"plugin-manager"
"replication"
"reviewnotes"
"singleusergroup"
"webhooks"
];
};
}

View file

@ -1,97 +0,0 @@
package main
import (
"archive/zip"
"flag"
"fmt"
"io"
"log"
"os"
"path/filepath"
"sort"
"strings"
)
var (
exclude = flag.String("exclude", "", "comma-separated list of filenames to exclude (in any directory)")
)
func init() {
flag.Usage = func() {
fmt.Fprintf(flag.CommandLine.Output(), "Usage of %s [zip file] [directory]:\n", os.Args[0])
flag.PrintDefaults()
}
}
func listToMap(ss []string) map[string]bool {
m := make(map[string]bool)
for _, s := range ss {
m[s] = true
}
return m
}
func main() {
flag.Parse()
if flag.NArg() != 2 {
flag.Usage()
os.Exit(1)
}
outPath := flag.Arg(0)
dirPath := flag.Arg(1)
excludeFiles := listToMap(strings.Split(*exclude, ","))
// Aggregate all files first.
var files []string
filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
if excludeFiles[info.Name()] {
return nil
}
files = append(files, path)
return nil
})
// Create zip
outW, err := os.Create(outPath)
if err != nil {
log.Fatalf("Create(%q): %v", outPath, err)
}
zipW := zip.NewWriter(outW)
// Output files in alphabetical order
sort.Strings(files)
for _, f := range files {
fw, err := zipW.CreateHeader(&zip.FileHeader{
Name: f,
Method: zip.Store,
})
if err != nil {
log.Fatalf("creating %q in zip: %v", f, err)
}
ff, err := os.Open(f)
if err != nil {
log.Fatalf("opening %q: %v", f, err)
}
if _, err := io.Copy(fw, ff); err != nil {
log.Fatalf("copying %q to zip: %v", f, err)
}
ff.Close()
}
if err := zipW.Close(); err != nil {
log.Fatalf("writing ZIP central directory: %v", err)
}
if err := outW.Close(); err != nil {
log.Fatalf("closing ZIP file: %v", err)
}
}

View file

@ -1,36 +0,0 @@
{ buildGerrit, gerrit, runCommandLocal, lib }:
{ name
, src
, depsOutputHash
, overlayPluginCmd ? ''
cp -R "${src}" "$out/plugins/${name}"
''
, postPatch ? ""
, patches ? [ ]
}: (buildGerrit {
name = "${name}.jar";
src = runCommandLocal "${name}-src" { } ''
cp -R "${gerrit.src}" "$out"
chmod +w "$out/plugins"
${overlayPluginCmd}
'';
bazelTargets = [ "//plugins/${name}" ];
}).overrideAttrs (super: {
deps = super.deps.overrideAttrs (superDeps: {
outputHash = depsOutputHash;
});
installPhase = ''
cp "bazel-bin/plugins/${name}/${name}.jar" "$out"
'';
postPatch = ''
${super.postPatch or ""}
pushd "plugins/${name}"
${lib.concatMapStringsSep "\n" (patch: ''
patch -p1 < ${patch}
'') patches}
popd
${postPatch}
'';
})

View file

@ -1,14 +0,0 @@
{ fetchgit, buildGerritBazelPlugin, lib }:
buildGerritBazelPlugin {
name = "code-owners";
depsOutputHash = "sha256-Ee2n7R/vi91drR+dNYB0QnGiiqcmz9/pynHhV9yDxdE=";
src = fetchgit {
url = "https://gerrit.googlesource.com/plugins/code-owners";
rev = "e654ae5bda2085bce9a99942bec440e004a114f3";
sha256 = "sha256:14d3x3iqskgw16pvyaa0swh252agj84p9pzlf24l8lgx9d7y4biz";
};
patches = [
./using-usernames.patch
];
}

View file

@ -1,472 +0,0 @@
commit 29ace6c38ac513f7ec56ca425230d5712c081043
Author: Luke Granger-Brown <git@lukegb.com>
Date: Wed Sep 21 03:15:38 2022 +0100
Add support for usernames and groups
Change-Id: I3ba8527f66216d08e555a6ac4451fe0d1e090de5
diff --git a/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerResolver.java b/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerResolver.java
index 70009591..6dc596c9 100644
--- a/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerResolver.java
+++ b/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerResolver.java
@@ -17,6 +17,8 @@ package com.google.gerrit.plugins.codeowners.backend;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
+import static com.google.common.collect.ImmutableSetMultimap.flatteningToImmutableSetMultimap;
+import static com.google.common.collect.ImmutableSetMultimap.toImmutableSetMultimap;
import static com.google.gerrit.plugins.codeowners.backend.CodeOwnersInternalServerErrorException.newInternalServerError;
import static java.util.Objects.requireNonNull;
@@ -25,6 +27,7 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Streams;
import com.google.common.flogger.FluentLogger;
@@ -33,17 +36,24 @@ import com.google.gerrit.entities.Project;
import com.google.gerrit.metrics.Timer0;
import com.google.gerrit.plugins.codeowners.backend.config.CodeOwnersPluginConfiguration;
import com.google.gerrit.plugins.codeowners.metrics.CodeOwnerMetrics;
+import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountControl;
import com.google.gerrit.server.account.AccountState;
+import com.google.gerrit.server.account.GroupBackend;
+import com.google.gerrit.server.account.GroupBackends;
+import com.google.gerrit.server.account.InternalGroupBackend;
import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.server.account.externalids.ExternalIdCache;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.util.RequestContext;
+import com.google.gerrit.server.util.ThreadLocalRequestContext;
import com.google.inject.Inject;
+import com.google.inject.OutOfScopeException;
import com.google.inject.Provider;
import java.io.IOException;
import java.nio.file.Path;
@@ -102,6 +112,8 @@ public class CodeOwnerResolver {
@VisibleForTesting public static final String ALL_USERS_WILDCARD = "*";
+ public static final String GROUP_PREFIX = "group:";
+
private final CodeOwnersPluginConfiguration codeOwnersPluginConfiguration;
private final PermissionBackend permissionBackend;
private final Provider<CurrentUser> currentUser;
@@ -112,6 +124,8 @@ public class CodeOwnerResolver {
private final CodeOwnerMetrics codeOwnerMetrics;
private final UnresolvedImportFormatter unresolvedImportFormatter;
private final TransientCodeOwnerCache transientCodeOwnerCache;
+ private final InternalGroupBackend groupBackend;
+ private final ThreadLocalRequestContext context;
// Enforce visibility by default.
private boolean enforceVisibility = true;
@@ -132,7 +146,9 @@ public class CodeOwnerResolver {
PathCodeOwners.Factory pathCodeOwnersFactory,
CodeOwnerMetrics codeOwnerMetrics,
UnresolvedImportFormatter unresolvedImportFormatter,
- TransientCodeOwnerCache transientCodeOwnerCache) {
+ TransientCodeOwnerCache transientCodeOwnerCache,
+ InternalGroupBackend groupBackend,
+ ThreadLocalRequestContext context) {
this.codeOwnersPluginConfiguration = codeOwnersPluginConfiguration;
this.permissionBackend = permissionBackend;
this.currentUser = currentUser;
@@ -143,6 +159,8 @@ public class CodeOwnerResolver {
this.codeOwnerMetrics = codeOwnerMetrics;
this.unresolvedImportFormatter = unresolvedImportFormatter;
this.transientCodeOwnerCache = transientCodeOwnerCache;
+ this.groupBackend = groupBackend;
+ this.context = context;
}
/**
@@ -361,6 +379,12 @@ public class CodeOwnerResolver {
"cannot resolve code owner email %s: no account with this email exists",
CodeOwnerResolver.ALL_USERS_WILDCARD));
}
+ if (codeOwnerReference.email().startsWith(GROUP_PREFIX)) {
+ return OptionalResultWithMessages.createEmpty(
+ String.format(
+ "cannot resolve code owner email %s: this is a group",
+ codeOwnerReference.email()));
+ }
ImmutableList.Builder<String> messageBuilder = ImmutableList.builder();
AtomicBoolean ownedByAllUsers = new AtomicBoolean(false);
@@ -405,9 +429,53 @@ public class CodeOwnerResolver {
ImmutableMultimap<CodeOwnerReference, CodeOwnerAnnotation> annotations) {
requireNonNull(codeOwnerReferences, "codeOwnerReferences");
+ ImmutableSet<String> groupsToResolve =
+ codeOwnerReferences.stream()
+ .map(CodeOwnerReference::email)
+ .filter(ref -> ref.startsWith(GROUP_PREFIX))
+ .map(ref -> ref.substring(GROUP_PREFIX.length()))
+ .collect(toImmutableSet());
+
+ // When we call GroupBackends.findExactSuggestion we need to ensure that we
+ // have a user in context. This is because the suggestion backend is
+ // likely to want to try to check that we can actually see the group it's
+ // returning (which we also check for explicitly, because I have trust
+ // issues).
+ RequestContext oldCtx = context.getContext();
+ // Check if we have a user in the context at all...
+ try {
+ oldCtx.getUser();
+ } catch (OutOfScopeException | NullPointerException e) {
+ // Nope.
+ RequestContext newCtx = () -> {
+ return new AnonymousUser();
+ };
+ context.setContext(newCtx);
+ }
+ ImmutableSetMultimap<String, CodeOwner> resolvedGroups = null;
+ try {
+ resolvedGroups =
+ groupsToResolve.stream()
+ .map(groupName -> GroupBackends.findExactSuggestion(groupBackend, groupName))
+ .filter(groupRef -> groupRef != null)
+ .filter(groupRef -> groupBackend.isVisibleToAll(groupRef.getUUID()))
+ .map(groupRef -> groupBackend.get(groupRef.getUUID()))
+ .collect(flatteningToImmutableSetMultimap(
+ groupRef -> GROUP_PREFIX + groupRef.getName(),
+ groupRef -> accountCache
+ .get(ImmutableSet.copyOf(groupRef.getMembers()))
+ .values().stream()
+ .map(accountState -> CodeOwner.create(accountState.account().id()))));
+ } finally {
+ context.setContext(oldCtx);
+ }
+ ImmutableSetMultimap<CodeOwner, String> usersToGroups =
+ resolvedGroups.inverse();
+
ImmutableSet<String> emailsToResolve =
codeOwnerReferences.stream()
.map(CodeOwnerReference::email)
+ .filter(ref -> !ref.startsWith(GROUP_PREFIX))
.filter(filterOutAllUsersWildCard(ownedByAllUsers))
.collect(toImmutableSet());
@@ -442,7 +510,8 @@ public class CodeOwnerResolver {
ImmutableMap<String, CodeOwner> codeOwnersByEmail =
accountsByEmail.map(mapToCodeOwner()).collect(toImmutableMap(Pair::key, Pair::value));
- if (codeOwnersByEmail.keySet().size() < emailsToResolve.size()) {
+ if (codeOwnersByEmail.keySet().size() < emailsToResolve.size() ||
+ resolvedGroups.keySet().size() < groupsToResolve.size()) {
hasUnresolvedCodeOwners.set(true);
}
@@ -456,7 +525,9 @@ public class CodeOwnerResolver {
cachedCodeOwnersByEmail.entrySet().stream()
.filter(e -> e.getValue().isPresent())
.map(e -> Pair.of(e.getKey(), e.getValue().get()));
- Streams.concat(newlyResolvedCodeOwnersStream, cachedCodeOwnersStream)
+ Stream<Pair<String, CodeOwner>> resolvedGroupsCodeOwnersStream =
+ resolvedGroups.entries().stream().map(e -> Pair.of(e.getKey(), e.getValue()));
+ Streams.concat(Streams.concat(newlyResolvedCodeOwnersStream, cachedCodeOwnersStream), resolvedGroupsCodeOwnersStream)
.forEach(
p -> {
ImmutableSet.Builder<CodeOwnerAnnotation> annotationBuilder = ImmutableSet.builder();
@@ -467,6 +538,12 @@ public class CodeOwnerResolver {
annotationBuilder.addAll(
annotations.get(CodeOwnerReference.create(ALL_USERS_WILDCARD)));
+ // annotations for the groups this user is in apply as well
+ for (String group : usersToGroups.get(p.value())) {
+ annotationBuilder.addAll(
+ annotations.get(CodeOwnerReference.create(group)));
+ }
+
if (!codeOwnersWithAnnotations.containsKey(p.value())) {
codeOwnersWithAnnotations.put(p.value(), new HashSet<>());
}
@@ -570,7 +647,7 @@ public class CodeOwnerResolver {
}
messages.add(String.format("email %s has no domain", email));
- return false;
+ return true; // TVL: we allow domain-less strings which are treated as usernames.
}
/**
@@ -585,11 +662,29 @@ public class CodeOwnerResolver {
*/
private ImmutableMap<String, Collection<ExternalId>> lookupExternalIds(
ImmutableList.Builder<String> messages, ImmutableSet<String> emails) {
+ String[] actualEmails = emails.stream()
+ .filter(email -> email.contains("@"))
+ .toArray(String[]::new);
+ ImmutableSet<String> usernames = emails.stream()
+ .filter(email -> !email.contains("@"))
+ .collect(ImmutableSet.toImmutableSet());
try {
- ImmutableMap<String, Collection<ExternalId>> extIdsByEmail =
- externalIdCache.byEmails(emails.toArray(new String[0])).asMap();
+ ImmutableMap<String, Collection<ExternalId>> extIds =
+ new ImmutableMap.Builder<String, Collection<ExternalId>>()
+ .putAll(externalIdCache.byEmails(actualEmails).asMap())
+ .putAll(externalIdCache.allByAccount().entries().stream()
+ .map(entry -> entry.getValue())
+ .filter(externalId ->
+ externalId.key().scheme() != null &&
+ externalId.key().isScheme(ExternalId.SCHEME_USERNAME) &&
+ usernames.contains(externalId.key().id()))
+ .collect(toImmutableSetMultimap(
+ externalId -> externalId.key().id(),
+ externalId -> externalId))
+ .asMap())
+ .build();
emails.stream()
- .filter(email -> !extIdsByEmail.containsKey(email))
+ .filter(email -> !extIds.containsKey(email))
.forEach(
email -> {
transientCodeOwnerCache.cacheNonResolvable(email);
@@ -598,7 +693,7 @@ public class CodeOwnerResolver {
"cannot resolve code owner email %s: no account with this email exists",
email));
});
- return extIdsByEmail;
+ return extIds;
} catch (IOException e) {
throw newInternalServerError(
String.format("cannot resolve code owner emails: %s", emails), e);
@@ -815,6 +910,15 @@ public class CodeOwnerResolver {
user != null ? user.getLoggableName() : currentUser.get().getLoggableName()));
return true;
}
+ if (!email.contains("@")) {
+ // the email is the username of the account, or a group, or something else.
+ messages.add(
+ String.format(
+ "account %s is visible to user %s",
+ accountState.account().id(),
+ user != null ? user.getLoggableName() : currentUser.get().getLoggableName()));
+ return true;
+ }
if (user != null) {
if (user.hasEmailAddress(email)) {
diff --git a/java/com/google/gerrit/plugins/codeowners/backend/findowners/FindOwnersCodeOwnerConfigParser.java b/java/com/google/gerrit/plugins/codeowners/backend/findowners/FindOwnersCodeOwnerConfigParser.java
index 5f350998..7977ba55 100644
--- a/java/com/google/gerrit/plugins/codeowners/backend/findowners/FindOwnersCodeOwnerConfigParser.java
+++ b/java/com/google/gerrit/plugins/codeowners/backend/findowners/FindOwnersCodeOwnerConfigParser.java
@@ -149,7 +149,8 @@ public class FindOwnersCodeOwnerConfigParser implements CodeOwnerConfigParser {
private static final String EOL = "[\\s]*(#.*)?$"; // end-of-line
private static final String GLOB = "[^\\s,=]+"; // a file glob
- private static final String EMAIL_OR_STAR = "([^\\s<>@,]+@[^\\s<>@#,]+|\\*)";
+ // Also allows usernames, and group:$GROUP_NAME.
+ private static final String EMAIL_OR_STAR = "([^\\s<>@,]+@[^\\s<>@#,]+?|\\*|[a-zA-Z0-9_\\-]+|group:[a-zA-Z0-9_\\-]+)";
private static final String EMAIL_LIST =
"(" + EMAIL_OR_STAR + "(" + COMMA + EMAIL_OR_STAR + ")*)";
diff --git a/javatests/com/google/gerrit/plugins/codeowners/backend/AbstractFileBasedCodeOwnerBackendTest.java b/javatests/com/google/gerrit/plugins/codeowners/backend/AbstractFileBasedCodeOwnerBackendTest.java
index 7ec92959..59cf7e05 100644
--- a/javatests/com/google/gerrit/plugins/codeowners/backend/AbstractFileBasedCodeOwnerBackendTest.java
+++ b/javatests/com/google/gerrit/plugins/codeowners/backend/AbstractFileBasedCodeOwnerBackendTest.java
@@ -424,7 +424,7 @@ public abstract class AbstractFileBasedCodeOwnerBackendTest extends AbstractCode
.commit()
.parent(head)
.message("Add invalid test code owner config")
- .add(JgitPath.of(codeOwnerConfigKey.filePath(getFileName())).get(), "INVALID"));
+ .add(JgitPath.of(codeOwnerConfigKey.filePath(getFileName())).get(), "INVALID!"));
}
// Try to update the code owner config.
diff --git a/javatests/com/google/gerrit/plugins/codeowners/backend/CodeOwnerResolverTest.java b/javatests/com/google/gerrit/plugins/codeowners/backend/CodeOwnerResolverTest.java
index 6171aca9..37699012 100644
--- a/javatests/com/google/gerrit/plugins/codeowners/backend/CodeOwnerResolverTest.java
+++ b/javatests/com/google/gerrit/plugins/codeowners/backend/CodeOwnerResolverTest.java
@@ -24,8 +24,10 @@ import com.google.gerrit.acceptance.TestAccount;
import com.google.gerrit.acceptance.TestMetricMaker;
import com.google.gerrit.acceptance.config.GerritConfig;
import com.google.gerrit.acceptance.testsuite.account.AccountOperations;
+import com.google.gerrit.acceptance.testsuite.group.GroupOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.entities.Account;
+import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.plugins.codeowners.acceptance.AbstractCodeOwnersTest;
import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.account.AccountsUpdate;
@@ -51,6 +53,7 @@ public class CodeOwnerResolverTest extends AbstractCodeOwnersTest {
@Inject private RequestScopeOperations requestScopeOperations;
@Inject @ServerInitiated private Provider<AccountsUpdate> accountsUpdate;
@Inject private AccountOperations accountOperations;
+ @Inject private GroupOperations groupOperations;
@Inject private ExternalIdNotes.Factory externalIdNotesFactory;
@Inject private TestMetricMaker testMetricMaker;
@Inject private ExternalIdFactory externalIdFactory;
@@ -112,6 +115,18 @@ public class CodeOwnerResolverTest extends AbstractCodeOwnersTest {
.contains(String.format("account %s is visible to user %s", admin.id(), admin.username()));
}
+ @Test
+ public void resolveCodeOwnerReferenceForUsername() throws Exception {
+ OptionalResultWithMessages<CodeOwner> result =
+ codeOwnerResolverProvider
+ .get()
+ .resolveWithMessages(CodeOwnerReference.create(admin.username()));
+ assertThat(result.get()).hasAccountIdThat().isEqualTo(admin.id());
+ assertThat(result)
+ .hasMessagesThat()
+ .contains(String.format("account %s is visible to user %s", admin.id(), admin.username()));
+ }
+
@Test
public void cannotResolveCodeOwnerReferenceForStarAsEmail() throws Exception {
OptionalResultWithMessages<CodeOwner> result =
@@ -127,6 +142,18 @@ public class CodeOwnerResolverTest extends AbstractCodeOwnersTest {
CodeOwnerResolver.ALL_USERS_WILDCARD));
}
+ @Test
+ public void cannotResolveCodeOwnerReferenceForGroup() throws Exception {
+ OptionalResultWithMessages<CodeOwner> result =
+ codeOwnerResolverProvider
+ .get()
+ .resolveWithMessages(CodeOwnerReference.create("group:Administrators"));
+ assertThat(result).isEmpty();
+ assertThat(result)
+ .hasMessagesThat()
+ .contains("cannot resolve code owner email group:Administrators: this is a group");
+ }
+
@Test
public void resolveCodeOwnerReferenceForAmbiguousEmailIfOtherAccountIsInactive()
throws Exception {
@@ -397,6 +424,64 @@ public class CodeOwnerResolverTest extends AbstractCodeOwnersTest {
assertThat(result.hasUnresolvedCodeOwners()).isFalse();
}
+ @Test
+ public void resolvePathCodeOwnersWhenNonVisibleGroupIsUsed() throws Exception {
+ CodeOwnerConfig codeOwnerConfig =
+ CodeOwnerConfig.builder(CodeOwnerConfig.Key.create(project, "master", "/"), TEST_REVISION)
+ .addCodeOwnerSet(
+ CodeOwnerSet.createWithoutPathExpressions("group:Administrators"))
+ .build();
+
+ CodeOwnerResolverResult result =
+ codeOwnerResolverProvider
+ .get()
+ .resolvePathCodeOwners(codeOwnerConfig, Paths.get("/README.md"));
+ assertThat(result.codeOwnersAccountIds()).isEmpty();
+ assertThat(result.ownedByAllUsers()).isFalse();
+ assertThat(result.hasUnresolvedCodeOwners()).isTrue();
+ }
+
+ @Test
+ public void resolvePathCodeOwnersWhenVisibleGroupIsUsed() throws Exception {
+ AccountGroup.UUID createdGroupUUID = groupOperations
+ .newGroup()
+ .name("VisibleGroup")
+ .visibleToAll(true)
+ .addMember(admin.id())
+ .create();
+
+ CodeOwnerConfig codeOwnerConfig =
+ CodeOwnerConfig.builder(CodeOwnerConfig.Key.create(project, "master", "/"), TEST_REVISION)
+ .addCodeOwnerSet(
+ CodeOwnerSet.createWithoutPathExpressions("group:VisibleGroup"))
+ .build();
+
+ CodeOwnerResolverResult result =
+ codeOwnerResolverProvider
+ .get()
+ .resolvePathCodeOwners(codeOwnerConfig, Paths.get("/README.md"));
+ assertThat(result.codeOwnersAccountIds()).containsExactly(admin.id());
+ assertThat(result.ownedByAllUsers()).isFalse();
+ assertThat(result.hasUnresolvedCodeOwners()).isFalse();
+ }
+
+ @Test
+ public void resolvePathCodeOwnersWhenUsernameIsUsed() throws Exception {
+ CodeOwnerConfig codeOwnerConfig =
+ CodeOwnerConfig.builder(CodeOwnerConfig.Key.create(project, "master", "/"), TEST_REVISION)
+ .addCodeOwnerSet(
+ CodeOwnerSet.createWithoutPathExpressions(admin.username()))
+ .build();
+
+ CodeOwnerResolverResult result =
+ codeOwnerResolverProvider
+ .get()
+ .resolvePathCodeOwners(codeOwnerConfig, Paths.get("/README.md"));
+ assertThat(result.codeOwnersAccountIds()).containsExactly(admin.id());
+ assertThat(result.ownedByAllUsers()).isFalse();
+ assertThat(result.hasUnresolvedCodeOwners()).isFalse();
+ }
+
@Test
public void resolvePathCodeOwnersNonResolvableCodeOwnersAreFilteredOut() throws Exception {
CodeOwnerConfig codeOwnerConfig =
@@ -655,7 +740,7 @@ public class CodeOwnerResolverTest extends AbstractCodeOwnersTest {
"domain example.com of email foo@example.org@example.com is allowed");
assertIsEmailDomainAllowed(
"foo@example.org", false, "domain example.org of email foo@example.org is not allowed");
- assertIsEmailDomainAllowed("foo", false, "email foo has no domain");
+ assertIsEmailDomainAllowed("foo", true, "email foo has no domain");
assertIsEmailDomainAllowed(
"foo@example.com@example.org",
false,
diff --git a/javatests/com/google/gerrit/plugins/codeowners/backend/findowners/FindOwnersCodeOwnerConfigParserTest.java b/javatests/com/google/gerrit/plugins/codeowners/backend/findowners/FindOwnersCodeOwnerConfigParserTest.java
index 260e635e..7aab99d0 100644
--- a/javatests/com/google/gerrit/plugins/codeowners/backend/findowners/FindOwnersCodeOwnerConfigParserTest.java
+++ b/javatests/com/google/gerrit/plugins/codeowners/backend/findowners/FindOwnersCodeOwnerConfigParserTest.java
@@ -158,16 +158,42 @@ public class FindOwnersCodeOwnerConfigParserTest extends AbstractCodeOwnerConfig
codeOwnerConfigParser.parse(
TEST_REVISION,
CodeOwnerConfig.Key.create(project, "master", "/"),
- getCodeOwnerConfig(EMAIL_1, "INVALID", "NOT_AN_EMAIL", EMAIL_2)));
+ getCodeOwnerConfig(EMAIL_1, "INVALID!", "NOT!AN_EMAIL", EMAIL_2)));
assertThat(exception.getFullMessage(FindOwnersBackend.CODE_OWNER_CONFIG_FILE_NAME))
.isEqualTo(
String.format(
"invalid code owner config file '/OWNERS' (project = %s, branch = master):\n"
- + " invalid line: INVALID\n"
- + " invalid line: NOT_AN_EMAIL",
+ + " invalid line: INVALID!\n"
+ + " invalid line: NOT!AN_EMAIL",
project));
}
+ @Test
+ public void codeOwnerConfigWithUsernames() throws Exception {
+ assertParseAndFormat(
+ getCodeOwnerConfig(EMAIL_1, "USERNAME", EMAIL_2),
+ codeOwnerConfig ->
+ assertThat(codeOwnerConfig)
+ .hasCodeOwnerSetsThat()
+ .onlyElement()
+ .hasCodeOwnersEmailsThat()
+ .containsExactly(EMAIL_1, "USERNAME", EMAIL_2),
+ getCodeOwnerConfig(EMAIL_1, "USERNAME", EMAIL_2));
+ }
+
+ @Test
+ public void codeOwnerConfigWithGroups() throws Exception {
+ assertParseAndFormat(
+ getCodeOwnerConfig(EMAIL_1, "group:tvl-employees", EMAIL_2),
+ codeOwnerConfig ->
+ assertThat(codeOwnerConfig)
+ .hasCodeOwnerSetsThat()
+ .onlyElement()
+ .hasCodeOwnersEmailsThat()
+ .containsExactly(EMAIL_1, "group:tvl-employees", EMAIL_2),
+ getCodeOwnerConfig(EMAIL_1, "group:tvl-employees", EMAIL_2));
+ }
+
@Test
public void codeOwnerConfigWithComment() throws Exception {
assertParseAndFormat(

View file

@ -1,16 +0,0 @@
{ buildGerritBazelPlugin, fetchgit }:
buildGerritBazelPlugin rec {
name = "oauth";
depsOutputHash = "sha256-4/+E0BwkA+rYYCy7y3G9xF86DJj+CFzPZUNXC5HN5wc=";
src = fetchgit {
url = "https://gerrit.googlesource.com/plugins/oauth";
rev = "b27cf3ea820eec2ddd22d217fc839261692ccdb0";
sha256 = "1m654ibgzprrhcl0wpzqrmq8drpgx6rzlw0ha16l1fi2zv5idkk2";
};
overlayPluginCmd = ''
chmod +w "$out" "$out/plugins/external_plugin_deps.bzl"
cp -R "${src}" "$out/plugins/${name}"
cp "${src}/external_plugin_deps.bzl" "$out/plugins/external_plugin_deps.bzl"
'';
}

View file

@ -8,6 +8,7 @@ let
hydra-ssh-key-priv = [ machines.bagel-box ]; hydra-ssh-key-priv = [ machines.bagel-box ];
netbox-environment = [ machines.meta01 ]; netbox-environment = [ machines.meta01 ];
mimir-environment = [ machines.meta01 ]; mimir-environment = [ machines.meta01 ];
mimir-webhook-url = [ machines.meta01 ];
grafana-oauth-secret = [ machines.meta01 ]; grafana-oauth-secret = [ machines.meta01 ];
loki-environment = [ machines.meta01 ]; loki-environment = [ machines.meta01 ];

Binary file not shown.

Binary file not shown.

View file

@ -1,9 +1,11 @@
{ {
imports = [ imports = [
./hydra
./postgres
./netbox
./gerrit ./gerrit
./hydra
./matrix
./monitoring ./monitoring
./netbox
./ofborg
./postgres
]; ];
} }

View file

@ -6,6 +6,8 @@ let
inherit (lib) mkEnableOption mkIf mkOption types; inherit (lib) mkEnableOption mkIf mkOption types;
cfgGerrit = config.services.gerrit; cfgGerrit = config.services.gerrit;
cfg = config.bagel.services.gerrit; cfg = config.bagel.services.gerrit;
jdk = pkgs.openjdk21_headless;
in in
{ {
options.bagel.services.gerrit = { options.bagel.services.gerrit = {
@ -28,7 +30,7 @@ in
config = mkIf cfg.enable { config = mkIf cfg.enable {
networking.firewall.allowedTCPPorts = [ 29418 ]; networking.firewall.allowedTCPPorts = [ 29418 ];
environment.systemPackages = [ pkgs.openjdk17_headless ]; environment.systemPackages = [ jdk ];
fileSystems."/var/lib/gerrit" = mkIf (cfg.data != "/var/lib/gerrit") { fileSystems."/var/lib/gerrit" = mkIf (cfg.data != "/var/lib/gerrit") {
device = cfg.data; device = cfg.data;
@ -64,12 +66,7 @@ in
jvmHeapLimit = "32g"; jvmHeapLimit = "32g";
# In some NixOS channel bump, the default version of OpenJDK has jvmPackage = jdk;
# changed to one that is incompatible with our current version of
# Gerrit.
#
# TODO(tazjin): Update Gerrit and remove this when possible.
jvmPackage = pkgs.openjdk17_headless;
settings = { settings = {
# Performance settings # Performance settings

View file

@ -0,0 +1,68 @@
{
config,
lib,
inputs,
...
}:
let
cfg = config.bagel.services.grapevine;
inherit (lib) mkEnableOption mkIf;
in
{
imports = [
inputs.grapevine.nixosModules.default
./hookshot.nix
];
options.bagel.services.grapevine.enable = mkEnableOption "Grapevine";
config = mkIf cfg.enable {
services = {
grapevine = {
enable = true;
settings = {
listen = [
{
type = "tcp";
address = "127.0.0.1";
port = 6167;
}
];
server_name = "forkos.org";
database.backend = "rocksdb";
};
};
nginx = {
upstreams.grapevine.servers."127.0.0.1:6167" = { };
virtualHosts = {
"matrix.forkos.org" = {
forceSSL = true;
enableACME = true;
locations."/".proxyPass = "http://grapevine";
};
"forkos.org" = {
forceSSL = true;
enableACME = true;
locations = {
"= /.well-known/matrix/server".extraConfig = ''
add_header Content-Type application/json;
add_header Access-Control-Allow-Origin *;
return 200 '{"m.server": "matrix.forkos.org:443"}';
'';
"= /.well-known/matrix/client".extraConfig = ''
add_header Content-Type application/json;
add_header Access-Control-Allow-Origin *;
return 200 '{"m.homeserver": {"base_url": "https://matrix.forkos.org/"}, "m.identity_server": {"base_url": "https://matrix.org/"}, "org.matrix.msc3575.proxy": {"url": "https://matrix.forkos.org"}}';
'';
};
};
};
};
};
};
}

View file

@ -0,0 +1,77 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.bagel.services.hookshot;
inherit (lib) mkEnableOption mkIf mkOption types;
keyPath = "/var/lib/matrix-hookshot/key.pem";
in
{
options.bagel.services.hookshot = {
enable = mkEnableOption "matrix-hookshot";
settings = mkOption {
description = "Settings";
type = (pkgs.formats.yaml { }).type;
};
admins = mkOption {
description = "List of admin MXIDs";
type = types.listOf types.str;
};
};
config = mkIf cfg.enable {
systemd.services.matrix-hookshot = {
wantedBy = ["multi-user.target"];
wants = ["network-online.target"];
after = ["network-online.target"];
serviceConfig = {
ExecStart = "${lib.getExe pkgs.matrix-hookshot} ${pkgs.writers.writeYAML "config.yaml" cfg.settings}";
ExecStartPre = pkgs.writeShellScript "hookshot-generate-key" ''
if [ ! -f ${keyPath} ]; then
mkdir -p $(dirname ${keyPath})
${lib.getExe pkgs.openssl} genpkey -out ${keyPath} -outform PEM -algorithm RSA -pkeyopt rsa_keygen_bits:4096
fi
'';
DynamicUser = true;
StateDirectory = "matrix-hookshot";
WorkingDirectory = "/var/lib/matrix-hookshot";
};
};
bagel.services.hookshot.settings = {
bridge = {
domain = "forkos.org";
url = "https://matrix.forkos.org";
mediaUrl = "https://forkos.org";
port = 9993;
bindAddress = "127.0.0.1";
};
passFile = keyPath;
listeners = [{
port = 9994;
bindAddress = "127.0.0.1";
resources = [ "webhooks" ];
}];
generic = {
enabled = true;
urlPrefix = "https://alerts.forkos.org/webhook";
};
permissions = map (mxid: {
actor = mxid;
services = [{
service = "*";
level = "admin";
}];
}) cfg.admins;
};
services.nginx.virtualHosts."alerts.forkos.org" = {
forceSSL = true;
enableACME = true;
locations."/".proxyPass = "http://127.0.0.1:9994";
};
};
}

View file

@ -3,5 +3,6 @@
./exporters ./exporters
./lgtm ./lgtm
./agent.nix ./agent.nix
./hookshot-adapter
]; ];
} }

View file

@ -0,0 +1,30 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.bagel.services.alertmanager-hookshot-adapter;
inherit (lib) mkEnableOption mkIf;
package = pkgs.callPackage ./package.nix {};
in
{
options.bagel.services.alertmanager-hookshot-adapter.enable = mkEnableOption "alertmanager to matrix-hookshot adapter";
config = mkIf cfg.enable {
systemd.services.alertmanager-hookshot-adapter = {
wantedBy = ["multi-user.target"];
wants = ["network-online.target"];
after = ["network-online.target"];
environment = {
PORT = "9100";
UPSTREAM = "https://alerts.forkos.org/webhook";
};
serviceConfig = {
ExecStart = lib.getExe package;
DynamicUser = true;
};
};
};
}

View file

@ -0,0 +1,23 @@
{
"name": "alertmanager-hookshot-adapter",
"version": "1.0.0",
"description": "Adapter between alertmanager webhooks and the Matrix Hookshot Apapter",
"main": "index.ts",
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "https://github.com/hm-edu/alertmanager-hookshot-adapter"
},
"dependencies": {
"@types/express": "^4.17.21",
"@types/node": "^20.11.20",
"dotenv": "^16.4.5",
"express": "^4.18.2",
"node-fetch": "^3.3.2",
"typescript": "^5.3.3",
"winston": "^3.13.0"
},
"scripts": {
"build": "npx tsc"
}
}

View file

@ -0,0 +1,40 @@
{
lib,
mkYarnPackage,
fetchFromGitHub,
fetchYarnDeps,
makeWrapper,
nodejs,
}:
mkYarnPackage rec {
pname = "alertmanager-hookshot-adapter";
version = "1.9.1";
src = fetchFromGitHub {
owner = "hm-edu";
repo = "alertmanager-hookshot-adapter";
rev = "v${version}";
hash = "sha256-KTk70zFA1tymmR8AYrAl2XIyA+SPs5Uksd6Z3kvUb+o=";
};
packageJSON = ./package.json;
offlineCache = fetchYarnDeps {
yarnLock = "${src}/yarn.lock";
hash = "sha256-LU25cXB+0DdcHRzKQ1hjQIVntarqPOUXZTgcw6lvLRM=";
};
buildPhase = ''
yarn build
'';
nativeBuildInputs = [ makeWrapper ];
postInstall = ''
makeWrapper ${lib.getExe nodejs} $out/bin/alertmanager-hookshot-adapter \
--add-flags $out/libexec/alertmanager-hookshot-adapter/deps/alertmanager-hookshot-adapter/dist/index.js
'';
meta.mainProgram = "alertmanager-hookshot-adapter";
}

View file

@ -20,6 +20,7 @@ in
owner = "nginx"; owner = "nginx";
}; };
mimir-environment.file = ../../../secrets/mimir-environment.age; mimir-environment.file = ../../../secrets/mimir-environment.age;
mimir-webhook-url.file = ../../../secrets/mimir-webhook-url.age;
}; };
services.mimir = { services.mimir = {
@ -68,6 +69,11 @@ in
receivers = [ receivers = [
{ {
name = "matrix"; name = "matrix";
webhook_configs = [{
# Mimir can't expand environment variables in external config files,
# so work around it.
url_file = "/run/credentials/mimir.service/webhook-url";
}];
} }
]; ];
}; };
@ -78,7 +84,10 @@ in
}; };
}; };
systemd.services.mimir.serviceConfig.EnvironmentFile = [ config.age.secrets.mimir-environment.path ]; systemd.services.mimir.serviceConfig = {
EnvironmentFile = [ config.age.secrets.mimir-environment.path ];
LoadCredential = [ "webhook-url:${config.age.secrets.mimir-webhook-url.path}" ];
};
services.nginx.virtualHosts."mimir.forkos.org" = { services.nginx.virtualHosts."mimir.forkos.org" = {
enableACME = true; enableACME = true;
@ -90,5 +99,6 @@ in
}; };
bagel.monitoring.grafana-agent.exporters.mimir.port = 9009; bagel.monitoring.grafana-agent.exporters.mimir.port = 9009;
bagel.services.alertmanager-hookshot-adapter.enable = true;
}; };
} }

View file

@ -29,7 +29,7 @@ in
listenAddress = "127.0.0.1"; listenAddress = "127.0.0.1";
settings = { settings = {
ALLOWED_HOSTS = [ cfg.domain ]; ALLOWED_HOSTS = [ cfg.domain ];
# REMOTE_AUTH_BACKEND = "social_core.backends.open_id_connect.OpenIdConnectAuth"; REMOTE_AUTH_BACKEND = "social_core.backends.open_id_connect.OpenIdConnectAuth";
}; };
extraConfig = lib.mkForce '' extraConfig = lib.mkForce ''
@ -37,9 +37,9 @@ in
SECRET_KEY = env["SECRET_KEY"] SECRET_KEY = env["SECRET_KEY"]
# SOCIAL_AUTH_OIDC_OIDC_ENDPOINT = env["NETBOX_OIDC_URL"] SOCIAL_AUTH_OIDC_OIDC_ENDPOINT = env["NETBOX_OIDC_URL"]
# SOCIAL_AUTH_OIDC_KEY = env["NETBOX_OIDC_KEY"] SOCIAL_AUTH_OIDC_KEY = env["NETBOX_OIDC_KEY"]
# SOCIAL_AUTH_OIDC_SECRET = env["NETBOX_OIDC_SECRET"] SOCIAL_AUTH_OIDC_SECRET = env["NETBOX_OIDC_SECRET"]
''; '';
}; };

View file

@ -0,0 +1,35 @@
{ config, lib, ... }:
let
cfg = config.bagel.services.ofborg;
amqpHost = "amqp.forkos.org";
amqpPort = 5671;
in {
options.bagel.services.ofborg = with lib; {
enable = mkEnableOption "ofborg coordinator";
};
config = lib.mkIf cfg.enable {
services.rabbitmq = {
enable = true;
configItems = {
"listeners.tcp" = "none";
"listeners.ssl.default" = builtins.toString amqpPort;
"ssl_options.certfile" = "${config.security.acme.certs.${amqpHost}.directory}/cert.pem";
"ssl_options.keyfile" = "${config.security.acme.certs.${amqpHost}.directory}/key.pem";
};
};
security.acme.certs.${amqpHost} = {
webroot = "/var/lib/acme/.challenges";
group = "rabbitmq";
};
services.nginx.virtualHosts.${amqpHost}.locations."/.well-known/acme-challenge".root =
"/var/lib/acme/.challenges";
systemd.services.rabbitmq.requires = ["acme-finished-${amqpHost}.target"];
networking.firewall.allowedTCPPorts = [ amqpPort ];
};
}

View file

@ -61,12 +61,13 @@ in
(record "fodwatch.infra" 3600 "AAAA" ["2001:bc8:38ee:100:1000::30"]) (record "fodwatch.infra" 3600 "AAAA" ["2001:bc8:38ee:100:1000::30"])
(record "meta01.infra" 3600 "AAAA" ["2001:bc8:38ee:100:1000::20"]) (record "meta01.infra" 3600 "AAAA" ["2001:bc8:38ee:100:1000::20"])
(record "hydra" 3600 "CNAME" ["bagel-box.infra"]) (record "amqp" 3600 "CNAME" ["bagel-box.infra"])
(record "grafana" 3600 "CNAME" ["meta01.infra"]) (record "grafana" 3600 "CNAME" ["meta01.infra"])
(record "hydra" 3600 "CNAME" ["bagel-box.infra"])
(record "loki" 3600 "CNAME" ["meta01.infra"]) (record "loki" 3600 "CNAME" ["meta01.infra"])
(record "mimir" 3600 "CNAME" ["meta01.infra"]) (record "mimir" 3600 "CNAME" ["meta01.infra"])
(record "matrix" 3600 "CNAME" ["meta01.infra"]) (record "matrix" 3600 "CNAME" ["meta01.infra"])
(record "alerts" 3600 "CNAME" ["meta01.infra"])
]; ];
}; };
} }