Compare commits
1 commit
ab50b0958f
...
83aeba6fb7
Author | SHA1 | Date | |
---|---|---|---|
Ilya K | 83aeba6fb7 |
|
@ -7,7 +7,6 @@ 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"
|
||||||
|
|
|
@ -16,6 +16,5 @@
|
||||||
"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'' ];
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,8 +20,6 @@ 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;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
21
flake.lock
21
flake.lock
|
@ -430,26 +430,6 @@
|
||||||
"type": "github"
|
"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": {
|
||||||
|
@ -571,7 +551,6 @@
|
||||||
"hydra",
|
"hydra",
|
||||||
"nix"
|
"nix"
|
||||||
],
|
],
|
||||||
"nix-gerrit": "nix-gerrit",
|
|
||||||
"nixpkgs": "nixpkgs_2",
|
"nixpkgs": "nixpkgs_2",
|
||||||
"terranix": "terranix"
|
"terranix": "terranix"
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,9 +14,6 @@
|
||||||
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 = {
|
grapevine = {
|
||||||
|
@ -36,7 +33,6 @@
|
||||||
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;
|
||||||
|
@ -81,7 +77,6 @@
|
||||||
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;
|
||||||
|
|
|
@ -39,12 +39,8 @@
|
||||||
|
|
||||||
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";
|
||||||
|
|
||||||
|
|
|
@ -25,12 +25,7 @@
|
||||||
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.grapevine.enable = true;
|
||||||
bagel.services.hookshot = {
|
bagel.services.hookshot.enable = true;
|
||||||
enable = true;
|
|
||||||
admins = [
|
|
||||||
"@k900:0upti.me"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
i18n.defaultLocale = "fr_FR.UTF-8";
|
i18n.defaultLocale = "fr_FR.UTF-8";
|
||||||
|
|
||||||
|
|
|
@ -1 +1,3 @@
|
||||||
[]
|
[
|
||||||
|
(import ./gerrit.nix)
|
||||||
|
]
|
||||||
|
|
11
overlays/gerrit.nix
Normal file
11
overlays/gerrit.nix
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
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 { };
|
||||||
|
};
|
||||||
|
}
|
37
pkgs/gerrit/0001-Syntax-highlight-nix.patch
Normal file
37
pkgs/gerrit/0001-Syntax-highlight-nix.patch
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
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
|
||||||
|
|
37
pkgs/gerrit/0002-Syntax-highlight-rules.pl.patch
Normal file
37
pkgs/gerrit/0002-Syntax-highlight-rules.pl.patch
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
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
|
||||||
|
|
215
pkgs/gerrit/0003-Add-titles-to-CLs-over-HTTP.patch
Normal file
215
pkgs/gerrit/0003-Add-titles-to-CLs-over-HTTP.patch
Normal file
|
@ -0,0 +1,215 @@
|
||||||
|
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
|
||||||
|
|
152
pkgs/gerrit/default.nix
Normal file
152
pkgs/gerrit/default.nix
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
{ 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"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
97
pkgs/gerrit/detzip.go
Normal file
97
pkgs/gerrit/detzip.go
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
36
pkgs/gerrit_plugins/builder.nix
Normal file
36
pkgs/gerrit_plugins/builder.nix
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
{ 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}
|
||||||
|
'';
|
||||||
|
})
|
14
pkgs/gerrit_plugins/code-owners/default.nix
Normal file
14
pkgs/gerrit_plugins/code-owners/default.nix
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{ 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
|
||||||
|
];
|
||||||
|
}
|
472
pkgs/gerrit_plugins/code-owners/using-usernames.patch
Normal file
472
pkgs/gerrit_plugins/code-owners/using-usernames.patch
Normal file
|
@ -0,0 +1,472 @@
|
||||||
|
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(
|
16
pkgs/gerrit_plugins/oauth/default.nix
Normal file
16
pkgs/gerrit_plugins/oauth/default.nix
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{ 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"
|
||||||
|
'';
|
||||||
|
}
|
|
@ -8,7 +8,6 @@ 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.
|
@ -1,11 +1,10 @@
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
./gerrit
|
|
||||||
./hydra
|
./hydra
|
||||||
|
./postgres
|
||||||
|
./netbox
|
||||||
|
./gerrit
|
||||||
./matrix
|
./matrix
|
||||||
./monitoring
|
./monitoring
|
||||||
./netbox
|
|
||||||
./ofborg
|
|
||||||
./postgres
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,6 @@ 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 = {
|
||||||
|
@ -30,7 +28,7 @@ in
|
||||||
config = mkIf cfg.enable {
|
config = mkIf cfg.enable {
|
||||||
networking.firewall.allowedTCPPorts = [ 29418 ];
|
networking.firewall.allowedTCPPorts = [ 29418 ];
|
||||||
|
|
||||||
environment.systemPackages = [ jdk ];
|
environment.systemPackages = [ pkgs.openjdk17_headless ];
|
||||||
|
|
||||||
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;
|
||||||
|
@ -66,7 +64,12 @@ in
|
||||||
|
|
||||||
jvmHeapLimit = "32g";
|
jvmHeapLimit = "32g";
|
||||||
|
|
||||||
jvmPackage = jdk;
|
# In some NixOS channel bump, the default version of OpenJDK has
|
||||||
|
# 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
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
cfg = config.bagel.services.hookshot;
|
cfg = config.bagel.services.hookshot;
|
||||||
inherit (lib) mkEnableOption mkIf mkOption types;
|
inherit (lib) mkEnableOption mkIf mkOption;
|
||||||
keyPath = "/var/lib/matrix-hookshot/key.pem";
|
keyPath = "/var/lib/matrix-hookshot/key.pem";
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
@ -16,10 +16,6 @@ in
|
||||||
description = "Settings";
|
description = "Settings";
|
||||||
type = (pkgs.formats.yaml { }).type;
|
type = (pkgs.formats.yaml { }).type;
|
||||||
};
|
};
|
||||||
admins = mkOption {
|
|
||||||
description = "List of admin MXIDs";
|
|
||||||
type = types.listOf types.str;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
config = mkIf cfg.enable {
|
config = mkIf cfg.enable {
|
||||||
|
@ -59,19 +55,6 @@ in
|
||||||
enabled = true;
|
enabled = true;
|
||||||
urlPrefix = "https://alerts.forkos.org/webhook";
|
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";
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,5 @@
|
||||||
./exporters
|
./exporters
|
||||||
./lgtm
|
./lgtm
|
||||||
./agent.nix
|
./agent.nix
|
||||||
./hookshot-adapter
|
|
||||||
];
|
];
|
||||||
}
|
}
|
|
@ -1,30 +0,0 @@
|
||||||
{
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
{
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
{
|
|
||||||
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";
|
|
||||||
}
|
|
|
@ -20,7 +20,6 @@ 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 = {
|
||||||
|
@ -69,11 +68,6 @@ 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";
|
|
||||||
}];
|
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
@ -84,10 +78,7 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.services.mimir.serviceConfig = {
|
systemd.services.mimir.serviceConfig.EnvironmentFile = [ config.age.secrets.mimir-environment.path ];
|
||||||
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;
|
||||||
|
@ -99,6 +90,5 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
bagel.monitoring.grafana-agent.exporters.mimir.port = 9009;
|
bagel.monitoring.grafana-agent.exporters.mimir.port = 9009;
|
||||||
bagel.services.alertmanager-hookshot-adapter.enable = true;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"]
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
{ 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 ];
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -61,13 +61,12 @@ 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 "amqp" 3600 "CNAME" ["bagel-box.infra"])
|
|
||||||
(record "grafana" 3600 "CNAME" ["meta01.infra"])
|
|
||||||
(record "hydra" 3600 "CNAME" ["bagel-box.infra"])
|
(record "hydra" 3600 "CNAME" ["bagel-box.infra"])
|
||||||
|
|
||||||
|
(record "grafana" 3600 "CNAME" ["meta01.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"])
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue