diff --git a/docs/docs/deployment-guide.mdx b/docs/docs/deployment-guide.mdx
index 0c515c068..7364db6ef 100644
--- a/docs/docs/deployment-guide.mdx
+++ b/docs/docs/deployment-guide.mdx
@@ -4,10 +4,12 @@ title: "Deployment guide"
import SupportedPlatforms from '/snippets/platform-support.mdx'
+## Container deployment
+
The following guide will walk you through the steps to deploy Sourcebot on your own infrastructure. Sourcebot is distributed as a [single docker container](/docs/overview#architecture) that can be deployed to a k8s cluster, a VM, or any platform that supports docker.
-## Walkthrough video
+### Walkthrough video
---
Watch this 1:51 minute video to get a quick overview of how to deploy Sourcebot using Docker.
@@ -21,7 +23,7 @@ Watch this 1:51 minute video to get a quick overview of how to deploy Sourcebot
className="aspect-video w-full"
>
-## Step-by-step guide
+### Step-by-step guide
---
Hit an issue? Please let us know on [GitHub discussions](https://github.com/sourcebot-dev/sourcebot/discussions/categories/support) or by [emailing us](mailto:team@sourcebot.dev).
@@ -95,6 +97,117 @@ Watch this 1:51 minute video to get a quick overview of how to deploy Sourcebot
+
+## NixOS deployment
+
+Hit an issue? Please let us know on [GitHub discussions](https://github.com/sourcebot-dev/sourcebot/discussions/categories/support) or by [emailing us](mailto:team@sourcebot.dev).
+
+
+
+ Add the Sourcebot flake as an input to your NixOS configuration. This will allow you to use the Sourcebot container in your NixOS deployment.
+
+ ```nix
+ inputs.sourcebot.url = "github:sourcebot-dev/sourcebot";
+ ```
+
+ Add sourcebot module to your NixOS configuration:
+
+ ```nix
+ nixosConfigurations.mysystem = nixpkgs.lib.nixosSystem {
+ modules = [
+ inputs.sourcebot.nixosModules.sourcebot
+ ];
+ }
+ ```
+ [Learn more about NixOS flakes](/docs/installation/nixos-flakes).
+
+
+ Sourcebot requires a few secrets to be set up before it can run, and code host credentials can be managed using NixOS module too:
+
+ - [sops-nix](https://github.com/Mic92/sops-nix) example:
+
+ ```nix
+ sops = {
+ secrets = {
+ sourcebot-auth-secret.owner = "sourcebot";
+ sourcebot-encryption-key.owner = "sourcebot";
+ sourcebot-gitlab-token.owner = "sourcebot";
+ };
+ templates = {
+ sourcebot-env = {
+ content = ''
+ AUTH_SECRET=${config.sops.placeholder.sourcebot-auth-secret}
+ SOURCEBOT_ENCRYPTION_KEY=${config.sops.placeholder.sourcebot-encryption-key}
+ GITLAB_EXAMPLE_TOKEN=${config.sops.placeholder.sourcebot-gitlab-token}
+ '';
+ };
+ };
+ };
+ ```
+
+ - [agenix](https://github.com/ryantm/agenix) example:
+
+ ```nix
+ age.secrets.sourcebot-env.file = ../secrets/sourcebot.age;
+ ```
+
+ `sourcebot.age` file should be an environment file in the format:
+
+ ```
+ AUTH_SECRET=your-auth-secret
+ SOURCEBOT_ENCRYPTION_KEY=your-encryption-key
+ GITLAB_EXAMPLE_TOKEN=your-gitlab-token
+ ```
+
+
+ The following NixOS configuration will enable Sourcebot and set it up to run with the provided configuration.
+ Additional options could be found in the [source file](../../nix/nixosModule.nix)
+
+ ```nix
+ services.sourcebot = {
+ enable = true;
+ # envFile = config.sops.templates.sourcebot-env.path; # Uncomment if using sops-nix
+ # envFile = config.age.secrets.sourcebot-env.path; # Uncomment if using agenix
+ package = pkgs.sourcebot;
+ logLevel = "info";
+ dataDir = "/data/sourcebot";
+ dataCacheDir = "/data/sourcebot/cache";
+ configPath = "${pkgs.writeText "config" (builtins.toJSON {
+ "$schema" = "https://raw.githubusercontent.com/sourcebot-dev/sourcebot/main/schemas/v3/index.json";
+ connections = {
+ github-public = {
+ type = "github";
+ repos = [
+ "sourcebot-dev/sourcebot"
+ ];
+ };
+ gitlab-private = {
+ type = "gitlab";
+ url = "https://gitlab.example.com";
+ all = true;
+ token = {
+ env = "GITLAB_EXAMPLE_TOKEN";
+ };
+ exclude = {
+ forks = true;
+ };
+ };
+ };
+ settings = {
+ resyncConnectionIntervalMs = 1000 * 60 * 60 * 24 * 7; # 1 week
+ reindexIntervalMs = 1000 * 60 * 60 * 24 * 7; # 1 week
+ maxRepoIndexingJobConcurrency = 1000; # 8 default
+ maxConnectionSyncJobConcurrency = 1000; # 8 default
+ maxRepoGarbageCollectionJobConcurrency = 1000; # 8 default
+ };
+ })}";
+ };
+ ```
+
+
+
+
+
## Next steps
---
diff --git a/flake.lock b/flake.lock
new file mode 100644
index 000000000..279259c40
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,133 @@
+{
+ "nodes": {
+ "flake-utils": {
+ "inputs": {
+ "systems": "systems"
+ },
+ "locked": {
+ "lastModified": 1731533236,
+ "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "type": "github"
+ }
+ },
+ "flake-utils_2": {
+ "inputs": {
+ "systems": "systems_2"
+ },
+ "locked": {
+ "lastModified": 1731533236,
+ "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "type": "github"
+ }
+ },
+ "microvm": {
+ "inputs": {
+ "flake-utils": "flake-utils_2",
+ "nixpkgs": [
+ "nixpkgs"
+ ],
+ "spectrum": "spectrum"
+ },
+ "locked": {
+ "lastModified": 1747859546,
+ "narHash": "sha256-tDu6JFzM86y5L2eLAkkw5Aklzz0DwfohtcxRXw+fCHA=",
+ "owner": "astro",
+ "repo": "microvm.nix",
+ "rev": "91ba136db1a3dd73168639c185fa802eb1157ec1",
+ "type": "github"
+ },
+ "original": {
+ "owner": "astro",
+ "repo": "microvm.nix",
+ "type": "github"
+ }
+ },
+ "nixpkgs": {
+ "locked": {
+ "lastModified": 1747179050,
+ "narHash": "sha256-qhFMmDkeJX9KJwr5H32f1r7Prs7XbQWtO0h3V0a0rFY=",
+ "owner": "nixos",
+ "repo": "nixpkgs",
+ "rev": "adaa24fbf46737f3f1b5497bf64bae750f82942e",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nixos",
+ "ref": "nixos-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "root": {
+ "inputs": {
+ "flake-utils": "flake-utils",
+ "microvm": "microvm",
+ "nixpkgs": "nixpkgs"
+ }
+ },
+ "spectrum": {
+ "flake": false,
+ "locked": {
+ "lastModified": 1746869549,
+ "narHash": "sha256-BKZ/yZO/qeLKh9YqVkKB6wJiDQJAZNN5rk5NsMImsWs=",
+ "ref": "refs/heads/main",
+ "rev": "d927e78530892ec8ed389e8fae5f38abee00ad87",
+ "revCount": 862,
+ "type": "git",
+ "url": "https://spectrum-os.org/git/spectrum"
+ },
+ "original": {
+ "type": "git",
+ "url": "https://spectrum-os.org/git/spectrum"
+ }
+ },
+ "systems": {
+ "locked": {
+ "lastModified": 1681028828,
+ "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+ "owner": "nix-systems",
+ "repo": "default",
+ "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-systems",
+ "repo": "default",
+ "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"
+ }
+ }
+ },
+ "root": "root",
+ "version": 7
+}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 000000000..2affeb8fa
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,72 @@
+{
+ description = "SourceBot - Code search and navigation tool";
+ inputs = {
+ nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
+ flake-utils.url = "github:numtide/flake-utils";
+ microvm.url = "github:astro/microvm.nix";
+ microvm.inputs.nixpkgs.follows = "nixpkgs";
+ };
+ outputs = {
+ self,
+ nixpkgs,
+ flake-utils,
+ microvm,
+ }:
+ flake-utils.lib.eachSystemPassThrough ["x86_64-linux"] (system: {
+ nixosModules = rec {
+ default = sourcebot;
+ sourcebot = import ./nix/nixosModule.nix self;
+ };
+
+ nixosConfigurations.testing = nixpkgs.lib.nixosSystem {
+ inherit system;
+ modules = [
+ self.nixosModules.sourcebot
+ ];
+ };
+
+ overlays.default = import ./nix/overlay.nix;
+ })
+ // flake-utils.lib.eachSystem ["x86_64-linux"] (
+ system: let
+ pkgs = import nixpkgs {
+ inherit system;
+ overlays = [self.overlays.default];
+ };
+ sourcebotSystem = nixpkgs.lib.nixosSystem {
+ inherit system pkgs;
+ modules = [
+ microvm.nixosModules.microvm
+ self.nixosModules.sourcebot
+ ./nix/microvm.nix
+ ];
+ };
+ in {
+ packages = rec {
+ default = sourcebot;
+ sourcebot = pkgs.callPackage ./nix/sourcebot.nix {};
+ microvm = sourcebotSystem.config.microvm.declaredRunner;
+ };
+
+ checks.default = pkgs.callPackage ./nix/nixosTest.nix {inherit self;};
+
+ devShells.default = pkgs.mkShell {
+ packages = with pkgs; [
+ yarn-berry
+ yarn-berry.yarn-berry-fetcher
+ openssl
+ yarn
+ redis
+ ];
+ buildInputs = with pkgs; [
+ nodePackages.prisma
+ ];
+ YARN_ENABLE_SCRIPTS = "false";
+ PRISMA_SCHEMA_ENGINE_BINARY = "${pkgs.prisma-engines}/bin/schema-engine";
+ PRISMA_QUERY_ENGINE_BINARY = "${pkgs.prisma-engines}/bin/query-engine";
+ PRISMA_QUERY_ENGINE_LIBRARY = "${pkgs.prisma-engines}/lib/libquery_engine.node";
+ PRISMA_FMT_BINARY = "${pkgs.prisma-engines}/bin/prisma-fmt";
+ };
+ }
+ );
+}
diff --git a/nix/microvm.nix b/nix/microvm.nix
new file mode 100644
index 000000000..63758bff6
--- /dev/null
+++ b/nix/microvm.nix
@@ -0,0 +1,62 @@
+{
+ pkgs,
+ lib,
+ ...
+}: {
+ microvm = {
+ interfaces = [
+ {
+ type = "user";
+ id = "sourecbot";
+ mac = "02:00:00:00:00:10";
+ }
+ ];
+ forwardPorts = [
+ {
+ from = "host";
+ host.port = 47734;
+ guest.port = 7734;
+ }
+ ];
+
+ shares = [
+ {
+ tag = "ro-store";
+ source = "${builtins.storeDir}";
+ mountPoint = "/nix/.ro-store";
+ }
+ ];
+ volumes = [
+ {
+ mountPoint = "/var";
+ image = "sourcebot-var.img";
+ size = 1 * 1024; # 10GB
+ }
+ ];
+ };
+
+ system.stateVersion = lib.trivial.release;
+
+ users.users.root.password = "";
+
+ # Enable autologin for root user
+ services.getty.autologinUser = "root";
+
+ services.sourcebot = {
+ enable = true;
+ logLevel = "debug";
+ configPath = "${pkgs.writeText "config" (builtins.toJSON {
+ "$schema" = "https://raw.githubusercontent.com/sourcebot-dev/sourcebot/main/schemas/v3/index.json";
+ connections = {
+ starter-connection = {
+ type = "github";
+ repos = [
+ "sourcebot-dev/sourcebot"
+ ];
+ };
+ };
+ })}";
+ };
+
+ networking.firewall.enable = false;
+}
diff --git a/nix/missing-hashes.json b/nix/missing-hashes.json
new file mode 100644
index 000000000..2f30e8efe
--- /dev/null
+++ b/nix/missing-hashes.json
@@ -0,0 +1,180 @@
+{
+ "@esbuild/aix-ppc64@npm:0.19.11": "cfc55b56d9dd515356e1b89cf7d41945d8adef237d1e2b2d5bfaaf001bc73a9100a1bc78afbd8d9b0c4db232a4ed17a1c55fbfecb89dafe8fa04b93c3e98b242",
+ "@esbuild/aix-ppc64@npm:0.21.5": "1f48fea96ab4fbc2921756361bc8a0c4d0690f14dc2298a357aa3d436bcd1cd646d490e5d71c4e0fb46b9e04401bc93153d6886456dd0665b6758be643c95a16",
+ "@esbuild/aix-ppc64@npm:0.25.1": "6de3a364b7f79f21f66d606d4d29c72ef81c741d71ab4bb941c4eabe7b6a809b1340f4dd5c943827005e421912880741320b9617d17fce762d204cfb94175223",
+ "@esbuild/android-arm64@npm:0.19.11": "4d164bd5fa1b6a965aea993d13c69600a2cb902c77d32b92e8eaec28357b47c4a195aa1003400224fa0daf514e735a172d1b4ea95c723b3118af9aad7aa48b84",
+ "@esbuild/android-arm64@npm:0.21.5": "7a4831b0886c165ed671f0094dcd491235fe503364a261379c84e2225a3c3230a06bce1d3a02316fa8a040b0ffede56c617746dc3b5550549ae3fb07095bb20d",
+ "@esbuild/android-arm64@npm:0.25.1": "716c98ad3220c71cbaedcfa34aa7c877a3fae911938c3776a66600d7f65980e384799a6832a1b9aea96c6d5a8880610f54744bd0813e743c511d44056ef528d6",
+ "@esbuild/android-arm@npm:0.19.11": "255a097f9ba1eaae3ab045d198b8af9551018a1f769d7db38a0b9ba4a26435628023b79a2e75efb5c65b513d31b64924dac4c2cc5b77ceec23d7b596df7e3568",
+ "@esbuild/android-arm@npm:0.21.5": "9fa871018a9f2198f40fde2c672fcb1b9d3ab5ee602644ea4cf68c548ee2c0b6c60ad851ce85219f84886fd29757d8c49bac28ea48a2a16708a088e32dfe673a",
+ "@esbuild/android-arm@npm:0.25.1": "d2c9e95dd2027f6e14250a90a11136d9ce73a2157a8d104df4a9dd199d3c50cd91f25813536b4776630acb78596bd5a025976962c6d624df7594c32f2d9c1395",
+ "@esbuild/android-x64@npm:0.19.11": "3b0b8affdfec769c5e39f8729eba691af9742178d1e7caa46951f2ee33d29e6da19e7a7ff26a172eaa63b3ffd351c4b4cb8cd041d786f81e7edf8698b7bf1da0",
+ "@esbuild/android-x64@npm:0.21.5": "24e477ccdaf1437cabe8710bc052a13b975a53617094a225e39823a1c562a71ef975d860ab895c129a813302495d85387143e27068e62fc897a2ac0335e4a2f6",
+ "@esbuild/android-x64@npm:0.25.1": "92f1bec801b414ecdc4b73adfbb4482f4f2cfba423d33296914fa59ffdafb81facc445a7f65d1eea813c078220a0606f4be02252843bb9039ceb2755f643387c",
+ "@esbuild/darwin-arm64@npm:0.19.11": "9456be0fdc1e358d365c7f9eb7edcdf8f77ef1f5710778bdaa9f0e95fe4e2863cab3e0d91fa5c5af4a8db551fe8a4b99bb69d1504902064d1302b79779988c07",
+ "@esbuild/darwin-arm64@npm:0.21.5": "67b0e4ebc870d0babb6721328f02b0e75eca5ee8f176220fa194ac5897ce76a27aa4f6d724389a74e1517670f70e766fe03c30875fdebeee5b1f7b22e99c5a1f",
+ "@esbuild/darwin-arm64@npm:0.25.1": "9c4cd09c0bd8479e27e04e2397edccebae928184c463684490137d2b7ea3171051b66596db229af2ad3e6a911c1c8a9d10b8aed30d11d0aa155ac0d309fd5dd6",
+ "@esbuild/darwin-x64@npm:0.19.11": "44e4659456ac62bcab361bbbe67780afdbe5e019dddfa968b632cc8c42ae5b03e14b8cebbcfe73542ea2bf4b04916a4f22ca1c5922f02460def93e28749a132d",
+ "@esbuild/darwin-x64@npm:0.21.5": "a4b6df47edf4b1e91eeca9d7af18f538e25615e00763709a4cc24a8918e7ac93b9bfc2ef9f44086f16257d05ad99e23c1213a7a1397475d1a78f0a1e773af89a",
+ "@esbuild/darwin-x64@npm:0.25.1": "31b64c02be9438e175e8b343c52e0bbc7964f08a1b44e0adbfa2a1b229ccd0ad0184cdb8313272b4590298833ea0b197d82a909466caffe90105f8acb30402d0",
+ "@esbuild/freebsd-arm64@npm:0.19.11": "65315f2d4e7ee893619a8bfebc60cb8556f9942dba2cfe9844c4bb5c023e1d02482cb687aabe2ab318d8d15406269a667d34b106447ad79a45d53773a0972279",
+ "@esbuild/freebsd-arm64@npm:0.21.5": "a4cf357807f2ea445b5191b8e5488070b567e2b534dba24ce6f8f1a332598ee0b9ffa41b3b0e55cd0cf57e2b56f0f1d84413faba182b81cb43bf19edf58a7654",
+ "@esbuild/freebsd-arm64@npm:0.25.1": "e04018ffd760dd101a5931e0248751bdfca98e3f29be9b41051d55278907f52a3e5ab80c32168e208c2ff287510d6d5b765d3adc841bfd621af7930c10a547e6",
+ "@esbuild/freebsd-x64@npm:0.19.11": "6158a47c96d7aaabcf68484f10f256117738a24f33c3464ce75d142234fe5ef488595a1b25b3eb2ef6ebe5819eae7e3002f871fbb4b015774ce3f5e398858669",
+ "@esbuild/freebsd-x64@npm:0.21.5": "8957c1345196e5dabd7d9f290b5292161f5d9955f269051fa7873118cfb5a20c31d70771ea3560b513f879d0948ba32fba915fb1b387571c4fbbb1fbeaf2dd87",
+ "@esbuild/freebsd-x64@npm:0.25.1": "438215bea2dc716d2c9ffe9b921bbde06e77d96ec0c837fd79f46bc6e195aa50e5f55490228f7433df7aa47810eec5c602a9d9cf956c1794778018632b196f34",
+ "@esbuild/linux-arm64@npm:0.19.11": "f2be49288d2e6fb6885ff3952163e42529b73e78b164217550804ca331c61b522d6ede133ceb1025732c3581461133d7703ba2a2a40830a060b63ccce5f94022",
+ "@esbuild/linux-arm64@npm:0.21.5": "1b95b17ed94eb977e38ea9130e677551b7cf0ccccdb3f23a9f8b59b5d67400817c2a417e4f043295bd3f67796853da2a1b1a8ca201ffe745efb40a144dfdc99c",
+ "@esbuild/linux-arm64@npm:0.25.1": "c839788b6db471f144bc2627a117083c9a50402c76cde2f0e5411faaeb3a5cde4972bb7336b87de67cd0a65d4d5b00759668407a03b5d4ed3130d4984837429a",
+ "@esbuild/linux-arm@npm:0.19.11": "8b781b2881b5913dbbc1ab1ae256091980bbf5e0128f23639b3129f7dc0cf8b86726b4df01f0e513a22115c740818e57cc3584f4e092881acc461f623ba472c3",
+ "@esbuild/linux-arm@npm:0.21.5": "6bfcd098ada5e6117d028777e5cc58456c2f570157fa0a0dce30c9d05b8389b86f74bf6b862534bf6994d342946c98b6774e1820880fd289765864b668e94c17",
+ "@esbuild/linux-arm@npm:0.25.1": "ecf9fd9028d2166619b9a45161a987eebb6ef3dcb0159a2607ab164c58c26a15cf274e0b1088834c07ef9166349276d595fdad5c42bce8d03a55cb213d947efa",
+ "@esbuild/linux-ia32@npm:0.19.11": "769746be1d9e3553af93a7bb567436c079d6235da7066a7e225ee2f631f4a9400dd5a0c898c075127176ae00f39d239773bc16ab1177857b4db4db88245e6990",
+ "@esbuild/linux-ia32@npm:0.21.5": "73c249c9918f0c9a9268ffe14fe745f5e7564b309dcea213da08a5e4367ffdfc8df4b004c70f80269dce0f653a3280cfdd8bf9a7a616b5b60649e4faea6e69b5",
+ "@esbuild/linux-ia32@npm:0.25.1": "36f208e10a4b778a28a9002338872e52d24dfc18e25d3b41dc53892279c3ee842d76c7b608a30620f128d85344acac0dc86f203f5de7082a21fa2e908c96b68c",
+ "@esbuild/linux-loong64@npm:0.19.11": "4dea9a63ab261c0b48adca8661db6c80937a58311307d82c04576a83fbde358a14428ab3a2110017b6010e087ff28b8d31af6e91d787b8cc2b1bda9fa8347cac",
+ "@esbuild/linux-loong64@npm:0.21.5": "60977efe24b3b6e1461d49da07dd57c1234992b9d2e6ac7d0dedfee538321d42be25e496ffb193121d3a6c6ca6ea6722b880e95695824dcc6643a3d9426b2296",
+ "@esbuild/linux-loong64@npm:0.25.1": "75d95e6ee995c9f2abb202ca430685e5d58fbe9b0b5b01a69b498c9b360d309026d15bae7831de9c0c4f02e45028a92ffb169117c3b56dd1ac7ea8c6ef50628d",
+ "@esbuild/linux-mips64el@npm:0.19.11": "e84d6938b45c688f35cc1adb141fc10cbf49fedbc6320a981316921c57cff03ecade48df08dff74c0b052b02322cc8b5105258c0eb371c4ebaae5728c08f80fc",
+ "@esbuild/linux-mips64el@npm:0.21.5": "20fb6c8f6e58f66cd4351034858b2ad85bda4144576b180979305cfabed43780a71934e9f176e476c719f14e37253b231a43d46638ad232989d5f4dd72ec6b75",
+ "@esbuild/linux-mips64el@npm:0.25.1": "7fc22eb8a7dc9ee743113cb327eef3591bced4753f416f8dd722794a198a053ab6e120b3b13c1bb6cc89cbd02ea502187b1a3c9ebe8187407665c78995153740",
+ "@esbuild/linux-ppc64@npm:0.19.11": "12882f9a20795b5b395c7b10f52b3f37eb864c024cf964cb657df2337ce88681d3e3896e951917d2870c5d24cb876b6f1e3749356cd1ce0d86a90e7e4ab7edeb",
+ "@esbuild/linux-ppc64@npm:0.21.5": "69f2ef1d127f48bc14cec479ae1a96dbf5fea651e0a3f148486f73495d2acb91acdaa164cd80648844916f05e7f9f9665a1864dd394e7f9acf96bea70937e6b5",
+ "@esbuild/linux-ppc64@npm:0.25.1": "84f914a776774bf209c5f96a3708e52477e1966b689a880c0256530063bc581125b5cc04b9700f4aa892e7770ba47c5a950a4be9f1bb3ccbe60a500383602156",
+ "@esbuild/linux-riscv64@npm:0.19.11": "e12b8f4223a26e9a6e2198698824595daa7d05490b550b6a3d11a8904880507301e16bb4804bc25b018192a6efee07bb4a96da8b8696c6cf95898185fd14a284",
+ "@esbuild/linux-riscv64@npm:0.21.5": "60c749d87c0f67cc67c5cc0d82aa597b7a807bc52510a16960337433bdbc8fa9f3c46eba98080106c0971e404e2250ca11c441bb4ae5b7a7d78b4095e3a70363",
+ "@esbuild/linux-riscv64@npm:0.25.1": "97f47cd5695686254b58f950ca973df1fcecfebf3bd585629121a65323ffaa7f95cf821392011ee069da492b46fdff771be332c699a438cf123351610e12d621",
+ "@esbuild/linux-s390x@npm:0.19.11": "8272e9de0fec7b06cb5f44d95d69b95cbe6bef941368f05215c5b21b1cf6613f35fe9a05e2fe57df179974c67954edceb69b47ca8da984aab62d907764306c31",
+ "@esbuild/linux-s390x@npm:0.21.5": "a14ff0484b962b374fd1e4662a53f8dd8999ba39fcf891f15631dfb2802c8d18893d6e366c42d28d55885e5804b7d6252c0e3cee038c241285c9b537ef12b4ae",
+ "@esbuild/linux-s390x@npm:0.25.1": "10760a999d432d092a8ebe5f09752ee7e8ea77a1afa5f1adfe7f9ccb5bbf77d6e0da6dce5d1a4a7cd731d89b6075723c2bc0328f446ed129c4b63c6441ba22ae",
+ "@esbuild/linux-x64@npm:0.19.11": "52891f2aa5979213831b379081bb303b470750925ddfdb63ede7edb11d48d692c36e0280f4faaa59d4d5a313ba981887fec9097a91388cb36e39c508bdad7d9c",
+ "@esbuild/linux-x64@npm:0.21.5": "9e5663fcace9c8456e9934a9ed6e7428db4080024eef3bfeaf82d476120bd881382c958be2785463d6b44467b3d3f870d6cce09a9cb37bcef19afeb97814d674",
+ "@esbuild/linux-x64@npm:0.25.1": "4cdb1625726580eb42432878912d5480a0321559ef2c6425c1db55f89f3fe1c35fb03b3adec92c3f52a4db751d7535f23086b80ed7b219d1edbb254ffbe96e68",
+ "@esbuild/netbsd-arm64@npm:0.25.1": "f2427b094e072d2db7944b1d2a988dd9f17627976a395b941f225aec2d0565da2ea110a845eafae960af08e3eea74eb327e5eb9dbe06dc1e14f4439596c3b47c",
+ "@esbuild/netbsd-x64@npm:0.19.11": "a6b0afc71b586c6e3d3db35e84c606b6e587cf708f759bad5c31ee4ed1e960a2285fe0c6a92c4d4404bf9fe5ec5459a137a5fba914fc75b77fbcdb390e3e3a28",
+ "@esbuild/netbsd-x64@npm:0.21.5": "3cb6115c4557d653c7ad6d2be5b4ed7a688b14d85b7b7108a1a57dda0b2cca3f8ed13560fa6639da8788f860b75eb714a17cfb7ba8f967e93bdf40c9b3a1cde1",
+ "@esbuild/netbsd-x64@npm:0.25.1": "c8347ecc18b175923a3cbdaef61b64815ffe0cf2cc285f4034337c2df83e78a118ebfdf2ac3a46a8d8a19ce6a0e0605d27d11570d1758ab90074c99047b82fc0",
+ "@esbuild/openbsd-arm64@npm:0.25.1": "719812786b4f3ab4471a306c930ab1bf31e9d92f933ea2a34566f07d6269df5058e7d48408127daa197e6fdb21e30697303127d259b76b18936d534619a3eb15",
+ "@esbuild/openbsd-x64@npm:0.19.11": "e35f1612a882b252462c790e7ee7b6cfd231826762a0faa2593f0e3176dd76f180923174098a7636a8c7c40976d471e36d3bc6b773eb06fb025970bc9e54395a",
+ "@esbuild/openbsd-x64@npm:0.21.5": "1caf0b502d6e2612ffd3e62589de2b9cd48cd742818746011d437e2d5787df4984f7c17b7a536aa20f12d04e519c859d755a7b57e6db0ed277054bd9c1036e85",
+ "@esbuild/openbsd-x64@npm:0.25.1": "21590cbeda028e9b9f8131c54c36bab65a5d5bd5dd4e6bd9f80438045a655e9ac634bb3535a8650b694db267fe23bd2318a59cdec2fae1ba389ed1a00cc0bbe7",
+ "@esbuild/sunos-x64@npm:0.19.11": "1e435f24551bab27f9217fa095f9f0aa420db00bf84b244d7a2257bd846affbe812ca8577c20cf08c05910e94e38894dba733c6732af4f6d76f2e6f1c972ca8b",
+ "@esbuild/sunos-x64@npm:0.21.5": "676da7301c7c600bb7de2523ecf5d877128da3c125fd8136533f5be38ca15d9f800b2fbbd396ca37d44c5daa51b8124d8a4728bb18245cd2becb3191b897c45a",
+ "@esbuild/sunos-x64@npm:0.25.1": "e20ce3891d8717fe2cb885d92d16e9409802316eaa91071be5c7d75164d23dbc5a502be3691039051843d94192e8cf43ade61014a8bce298cfc13a8d3ddd85b0",
+ "@esbuild/win32-arm64@npm:0.19.11": "4dc08bcba8e7a59e76627b7ed3f6522e82580d01eedc6e826d19e0068b6a2a1504681e87d311564235d7b9a9d1f40fd3b6cb41777f962f7bc58b9622a55796cf",
+ "@esbuild/win32-arm64@npm:0.21.5": "9b2ab87429efd3c2697dc5c7948ea57b57757b6e4709469e773b73cd5fe4b7fda5912073f4c3bdf9d0346b8f3ae443367a63bcd51de24fb81b9f592712eb3366",
+ "@esbuild/win32-arm64@npm:0.25.1": "749a211eae6a47e5ceb71898df668d083bdec2ed762116fea7772824281f793aceb0487946e20ff604d7e102d1fc8538a73f15b476ca36e07f7ddfb601f6dfa1",
+ "@esbuild/win32-ia32@npm:0.19.11": "113663eb7c1da574d24453a0633b503e44e9961f7fbf2da4673536ae0cd1e627136c04da22741bb6ecf8c199461cec80539a0a6747499c5f3efb75cf9f94116b",
+ "@esbuild/win32-ia32@npm:0.21.5": "c1fe3276507d82202c464cd4809e67e6f151e29ed9de05c32d086dfe30207db15e646911ebc7f50df659891bfee292a25062792c589c2ff769be238c6b5fb8be",
+ "@esbuild/win32-ia32@npm:0.25.1": "bb45fd889d858678ec68114bfc398965ed8d44e46a9517fcd9f7b397101c2cf94d78938a2640f6f2a1fe65de4ae8830fd426cd21a28302bb92333913b3c16c85",
+ "@esbuild/win32-x64@npm:0.19.11": "b62f30ee80d032aaa5937e3046c644fa4884ada4bea32cc7a420bd1f6e827f65c85565438c6adf55335addc52eb78a8d806679d5c96f13093ef719406d2cf89c",
+ "@esbuild/win32-x64@npm:0.21.5": "5d7b28baa9c22684d35ec0515f6d36f8f583f26733c8e84c7f78edf17b8a7d133819267486f2fd66f20ca3a810896f11c3c81106d745040c2f07ade314846bf1",
+ "@esbuild/win32-x64@npm:0.25.1": "e33291b9834095e6460bd20bb15c49361758bf66d28ccffe0c06a1565211c91f668d9cfc0cbd5bd7a5def693fe7272dbe290b08d4eadba29e750c8a9c739f564",
+ "@img/sharp-darwin-arm64@npm:0.33.5": "bca72454845f361ae9e6464547fb1ca9353b1b9ed7bfe5bd116280d6cae8f49b5f941e760bd9f8fd4d5d5274e5e231af7ef42a645318f074a67e3c3de03d5576",
+ "@img/sharp-darwin-x64@npm:0.33.5": "b293e20e66e37180d1251906fcd13ce20c824b84476add53df3f2a1376cbaee1288b91665ea2058db31ec05ef19b85eee9b52fbe82a7603a6c1835007a75121a",
+ "@img/sharp-libvips-darwin-arm64@npm:1.0.4": "1711bd98c5e6d5d381d2301152747fda3e769b5671d4b3a572b383ddbc3568483b591f65bdc7899d46ff1a511f63023e42703b4cb79b4d185e48d6e8e769a6e7",
+ "@img/sharp-libvips-darwin-x64@npm:1.0.4": "3f6ebbdf1b81c1b899ef1448bdfc7e380e5194036d6fdb8b41a136c586e866fd2031f27e5c41575c8126fa2290c061e4ac2e8d8551cdabd489c2f8b617dfd418",
+ "@img/sharp-libvips-linux-arm64@npm:1.0.4": "0bef56e45f639cf521208dfeead61247a1ff5b968ed8d8687e346a9ff188cb3a34a90a81376d5557034bff32aaf4566d665a5a055888c2aec3e2a4a25b5060a0",
+ "@img/sharp-libvips-linux-arm@npm:1.0.5": "bc68be221896ac177649f2efb98ac77c39150fb5adf5abd90094cec3229122c01058fbe1932aed42d19e8ac6260ba043c807806b946830db385d50887dcde906",
+ "@img/sharp-libvips-linux-s390x@npm:1.0.4": "4d0da56b04fc135694d8c92c8d5410cbe2ea0fc6dbce9aaadea02059a628b07231b2f4560e22e98636ce26897aea8eb48c4537b6010f07ced6754dc0b6bda382",
+ "@img/sharp-libvips-linux-x64@npm:1.0.4": "3c2c09d95b5916dff138390d6aba4c09ba6555a908d5b9eaa0576408d61eba299b013f3828e702c418d830b8f53a73e2e118619df7e5a6738c729ed7f78b6601",
+ "@img/sharp-libvips-linuxmusl-arm64@npm:1.0.4": "952819ca53ba8d9911e8803674fb71b5854d0f2bff8e947b04a45e84915ffce29f04241359d0ce3ddff874d5ab1a61b3efdac36a5c5245732ca43250e172ff60",
+ "@img/sharp-libvips-linuxmusl-x64@npm:1.0.4": "74c5f218ff390c67d9a95bab314b6d0bb8f2217dd55cbe2bcee2474a5fee4be9772d7924672c5e4d9dc6589f569a1d27e1562c4bb6ec1cee50f02b4799a64849",
+ "@img/sharp-linux-arm64@npm:0.33.5": "68bbcd09e6598aa480818d2c8a17317b8fe7709491eae4a2a68688c14ba2d39cde5b9f7b48d701f231909b4efd8038d2ad8fd7cfd8ea8fca9adbc2d42487da45",
+ "@img/sharp-linux-arm@npm:0.33.5": "2d9a2c22e174fb4b4f832d98587076c7e24ce44ed1a6f1ff074335373dbd9ceaace539652a2109dfc2b4b87c95a9fa3ebb19dfec205a7962129deecfc48da818",
+ "@img/sharp-linux-s390x@npm:0.33.5": "c1d363553c699cb97abe2107ab59abe16631cef2150c4bd5214902cf444a57d7dc5dd2919b8daa8f09d49671bb6633565df0895fda2b27e29ceb0121343dc689",
+ "@img/sharp-linux-x64@npm:0.33.5": "870e897b4eaae97140515ce6c2d87c010b015bf53e9cd7182465dc3b1c032cf4e5d4d9e00ec66996220cb924be4b748cdcbcc03f0fffb378338ed4ad81d8d672",
+ "@img/sharp-linuxmusl-arm64@npm:0.33.5": "1f4ef8055027962e3dcc8307e7b4ea070ae2f41ae9ef3442afd0d7515223bf66201bcbdf9097d353f4e64fd572c2ac26eec9e832597fb42da8f27fad6ec4a34d",
+ "@img/sharp-linuxmusl-x64@npm:0.33.5": "c82dd5576f1836947c81a2b573e1953d7b4fcdccb532473e2073024c02a999f8eb17ddb9c2fc5b0fd77a9a7a38edb9720dcfc9358e43a60e8f8eb0ea3f763bae",
+ "@img/sharp-wasm32@npm:0.33.5": "050bab055f1345c12e2fb7cc9dd69b1da3094c3e4536556992c165063f42cd36c8a26ef86c7dbfb1e44534662864f32175e848b2c9f73fbf3623a5433ce77096",
+ "@img/sharp-win32-ia32@npm:0.33.5": "494e67275dcab551c8310a7d19175e9d8c5cd005546ff970a9fcb6c759fae3a8bf97e68f91e2e292d6c48805ea40ece96b75ba2a49e3feb62d30de25cd833539",
+ "@img/sharp-win32-x64@npm:0.33.5": "df976250034aef085d37a52e975e22a6418459bc12e4d0a2ee4f2cd623275b9380a461ccd2cd8945e3b1d30a11b89afda31b5b14e58aaf308e20173f521c4ed4",
+ "@msgpackr-extract/msgpackr-extract-darwin-arm64@npm:3.0.3": "f8d48613de17e5356d273745042bbc7c0d345e1e4e5e88ed1132a3ed9ca65db9f8b908a708383d7a58ede276c105e7c9a2887fad00d5e47892010466e5312b47",
+ "@msgpackr-extract/msgpackr-extract-darwin-x64@npm:3.0.3": "270f8831d7f6d61ff36e7118e09ec13baa633bb22dd57fa3a9e6af67f7036a3fd1beec4c8aad1c72639522300364cef98bc5ba2cddf67a3832f8a35b7fc45502",
+ "@msgpackr-extract/msgpackr-extract-linux-arm64@npm:3.0.3": "326c19865ead39017326dd8066cde30adaf87a62b968a22d5e5369691817086f681fc291be80b7c61c5496fe915f6df546f41e14ee18b896c54de5099cf147ac",
+ "@msgpackr-extract/msgpackr-extract-linux-arm@npm:3.0.3": "115f627bcd7c290ddfa1d16405cc1ce388be0b295a26adb1ee2d774133a77809aa0bae83775d3c11f220544e77d34c01d607037bf9dfc6aaac67329cb5fe2818",
+ "@msgpackr-extract/msgpackr-extract-linux-x64@npm:3.0.3": "ab74755aa2afd27ad8f5277c7cb7a0c721028ba63c3e3109458689340d76ed4c901aa2c71ae5950f3a2149ae9e835e711a12842db219864107cc0c5c1fbdc0da",
+ "@msgpackr-extract/msgpackr-extract-win32-x64@npm:3.0.3": "edfea4f5455f71f608c15163467b98245d46fbd6078019347418e193968431afe632a11e8729ec39755a54563d86940899e2fe8f3d124393f91855956bd1d745",
+ "@next/swc-darwin-arm64@npm:14.2.16": "fc2057e68dc5010a7c77b5c57aa8b97678a98d1a57007bf2bba4ecbc625ccb8c1c56dbbbd76b52cca50188608e62964d8fb0f972d7c2d9a31412aa6ca7020bca",
+ "@next/swc-darwin-arm64@npm:14.2.26": "93c6635d8d7cf3e447e9a2556596f61af8304c15f4c91e230cf8bf4d9e2dc4dcd5dcca656efef94c4dff4af091574ed37604d9d96f5984e6db38eb48ef9b31fa",
+ "@next/swc-darwin-x64@npm:14.2.16": "837a4f04a82685542861788aacc74a830f5c000e65c1099a9f3b93204fb54b4c47b7d5b10bef5a157f9c0d6cf75a28b022da53c2d4eb71b1e4b9d5ad35d843a7",
+ "@next/swc-darwin-x64@npm:14.2.26": "5d3e3cda1c63d40ec430b18f442d5b4c427c879abfd1ef10de9a3e4246fad35c9ec6e6eb1c03e23179ad5001e13e11595a42cd5ae73bba2f1e0ec5a3c5d47b97",
+ "@next/swc-linux-arm64-gnu@npm:14.2.16": "299a1fb742f133987fb945b9b00c438a3f5f77586576239b5b52f0ed8db14d6dcb6e32400940a0ef831dcaadfd299559d7b28328b245dde71a8ee26f78068077",
+ "@next/swc-linux-arm64-gnu@npm:14.2.26": "5f6f019414f0cbd6be8521f34c20ad2bf21360fec1af887ebc9d1a071f86eaf8c7968e350c63ef811abdb6104725c78cb53e207cad1d92791d22e559b4e42fcb",
+ "@next/swc-linux-arm64-musl@npm:14.2.16": "a07024b271f7018700b55774d536cdca3deba796b04892b605cd01cb7d91129db19cb084b8d82456f11f98126a2a441ac3302fbd6d37bc9d3e06a63fb457684a",
+ "@next/swc-linux-arm64-musl@npm:14.2.26": "d57ef200a88016ebc6615d0a4409d931fbdd47377301e9fbeceb1b78bd31fe7d51e3c91b479ee0eca998e7afd487d583e24d5f394679be6c6fe469d6f51bfd44",
+ "@next/swc-linux-x64-gnu@npm:14.2.16": "40e294f23bb630eb13e22cc98489689b9da68ed914120543c91a165328405e8210105b8004676bf32c109136643d31cd96cbbdd99f71c009c6b3386b1f437ead",
+ "@next/swc-linux-x64-gnu@npm:14.2.26": "7f20c7ed0ddc2f58cae79603245e50b7d372d7d26bd5d3d6d3b7f11d891167ffff451827b5181feabb87ab0f3f2387236694df4f84a88e0046f097bfd98026b2",
+ "@next/swc-linux-x64-musl@npm:14.2.16": "b8c3f4b4bb36bd823c19f74890a91d54abd1958c5c3d9da913ec35e7235a2857987f6af83370797c067bd692c293f8c0db1f614537f0e7feeba4f42cbc468e7a",
+ "@next/swc-linux-x64-musl@npm:14.2.26": "8f9b85f8c647bef9fd7605184f33a43f0f52330142a389ff370a36358620b16698796339dca392b29b253b34e73ad937159feee9ead57b85e801827aea652e7c",
+ "@next/swc-win32-arm64-msvc@npm:14.2.16": "839e55fb5e5c3208bdc7b5811b8f13d4ff52643efdd703d469149a11fcef54a1ccb855330188652eee2a2d6b65e1b76110d15c3d5643750ce9a2eac74f539ac2",
+ "@next/swc-win32-arm64-msvc@npm:14.2.26": "3eaa8b3eb149db9d4d21fc0a0033ec2c0d9173efd61d43b59a64557ec5cdf0cd31866095c8209bc7e95e1ad0f2eda992649cc5ca235800f0a0fb77a6dc431306",
+ "@next/swc-win32-ia32-msvc@npm:14.2.16": "55acbf3583f3a2241f5d960db1704c8bd5680ad5adbec7dc6047969c6ce0b4745b611da3c3eba38c6a50a15eb5a2eaaadadfd30d6b387d180f374da3bdda8633",
+ "@next/swc-win32-ia32-msvc@npm:14.2.26": "7024622239f372b4094a625548d978460aab6e9b43d9feb43aaa22390ff4b6bbc79663e449320e17e4ae093c49d85913999732154933224d20c851517bd7d785",
+ "@next/swc-win32-x64-msvc@npm:14.2.16": "e85f92eca235e6605262c89a45c3b8ea99e8088dec4bf2fa4b16d3dce64f2f5c6653efcac7a23ac98fbb148d2cd18845309c18618d4a63062790b79ee5203561",
+ "@next/swc-win32-x64-msvc@npm:14.2.26": "c9a3e5d45c2bef31a921da4a29c242a388fab1673da95a4f1d49407bd5ab275de4a60041fb0bb07fef6d7cfb6013df6f9b17d104312b84a6272ec2d7360fc64f",
+ "@rollup/rollup-android-arm-eabi@npm:4.35.0": "0d365880d060032a95e862d72ef9086de822087d2442d0c56b8e775b35c4bd512b8f5a9a992ef2ff27c60f38fb53084e65e50dbedc599fd560c070b4927c63d1",
+ "@rollup/rollup-android-arm-eabi@npm:4.37.0": "137cc44b5452772bc857f3a9b113dc89a95b0d529963e91a49ac1b336302f37931da052a5d07c294da6060d07251c0251c7d363fd2be1775b59afd84a4c2ec67",
+ "@rollup/rollup-android-arm64@npm:4.35.0": "72aa2d3d47f1f2e99952bc871d360797c79a8f41474e0cdd8e17ba839f94523d3c83fefa5b6e9ea5296578e92cd07573c2eb824c3bad1fa6ce0aa4312c954e60",
+ "@rollup/rollup-android-arm64@npm:4.37.0": "a6856efb422e0e9ab62f407e0e73908fe78d42fbb2730aeeba3d8b3f034449ef248a0fb596b5af3e87f7054e00328aa4322525a375508f492dbc7c2478aa0e49",
+ "@rollup/rollup-darwin-arm64@npm:4.35.0": "37bf58948774b446148c6705dd07cdbdb5fa7ec9467bca92021b1d0a061f052e7ca45ddf65f6c22de15acd73b365ab6b26e0b7a870ed012f8bc375bf9bec753d",
+ "@rollup/rollup-darwin-arm64@npm:4.37.0": "ac29a2a8eb0e40c54a1685c868436658682293cacde10b7cbf587845902e4e95ec4b84e4eefbf7ac6a4b6330b47fe4094dbeeb90f22c0ee130ff984b8d3a1cb9",
+ "@rollup/rollup-darwin-x64@npm:4.35.0": "70b41ae4f14eaff3658634e79531e5666a76cc0484f6f129fff21cc41aec372676622c88ccd2aa3ccb6fbc56580dd9985e93bc90b9e3e767f936b339bc989201",
+ "@rollup/rollup-darwin-x64@npm:4.37.0": "037115d7f3b9ceab60df8ea12a48762a6c4051da7008a6b5d47174d9980759c392d7c6e4e79e2989744f0a16d1fb78ae97a9abd309fb1c41b07b65fab1700338",
+ "@rollup/rollup-freebsd-arm64@npm:4.35.0": "adefc811863f9ff9e8528055506178a3695991fc3a1dd568f98677dd64ed9b003135fd8b064ddcfa11d1515affeb7bab6daace77f8220a8e6264e91108e319c9",
+ "@rollup/rollup-freebsd-arm64@npm:4.37.0": "8ac2a91f628962633cf33aa462e567b1fc09b4ea09bf7a882ebb7ebf7adf4530d5f9d1b2dfa50ef64ddfa4b820e1eea324041f4c847298ddbe48d7491a944016",
+ "@rollup/rollup-freebsd-x64@npm:4.35.0": "c4bbee2f451ca386ac3457adb0a4ff1f73e6ba1e02c7887472bf220dd4ea7cec225c86dcc4748073af2306f8738769a8ff90d2343fb96589bac254e91157db70",
+ "@rollup/rollup-freebsd-x64@npm:4.37.0": "855b528e935ad21de0297112b2b73ea4ca7db94278606811f2d61011cf8f8042e4b7127f7993859320876fba585a00c5e7e11c7b50ce23af4ccca3679a69754f",
+ "@rollup/rollup-linux-arm-gnueabihf@npm:4.35.0": "450123c65e4e40c250c9cd4b925f01ab34be651b66c544f083e36332efc867298a4d03c4e0de4cc49ef77dda5b72bfcf8815811a392632ca266f459ca2b7d31e",
+ "@rollup/rollup-linux-arm-gnueabihf@npm:4.37.0": "dc126fb662d1a645ab41680033a987908ed0565b4ea9d414a9a46a61c06e9b136c010d1f28b1c34ffb101375a5279f18ee70d0d9b4ff1f2db80a54a05af3071d",
+ "@rollup/rollup-linux-arm-musleabihf@npm:4.35.0": "8a9107834b546a8430d438f0a8ab5c016913ae3aeceef23c21e462a7738502c1a7b0cef15751602846fa0f530ffa676d1c3e821591fbcadd31983b9169415bd0",
+ "@rollup/rollup-linux-arm-musleabihf@npm:4.37.0": "206cf3b319948b5d2d5ada18c698f16350c4cb64b53ad754fad91c1fe403c5fc110b3d3d2436433c5fd9ed1e1cec3535ed184902a6986f8bc0ae9770360e3c0b",
+ "@rollup/rollup-linux-arm64-gnu@npm:4.35.0": "844c3b1aad40fe18a69b9f6bc4d31228e6497ba95ed8fa9ad1037eb18a472a55b31e9d3bdf7d19f2035213ce4e7e73127cfcadac6141fb975f0c41bacfb360b7",
+ "@rollup/rollup-linux-arm64-gnu@npm:4.37.0": "f96da93f09aad5145f6fcfd69eccb6a2d407754e13f168d161dd2280991b7e4a3a514009b8a796767e5bd85ca0e4f81e2aea19781bb027d3ec59cf01768e2e1c",
+ "@rollup/rollup-linux-arm64-musl@npm:4.35.0": "e89bf799436ecf71db90c40a0dcd40eef9813f32c993777676bf81a548eb7d5ec4c0959d33c9c3ee8ae448731655ede2649807601d4c714276ccb295c0ec5e35",
+ "@rollup/rollup-linux-arm64-musl@npm:4.37.0": "c31f0c0aeefd18b59212f845aacac5a5a614ce0101db27dcc53884ae4d4e766d399d816104a10f4ee8c4c28e2045abb2d6a778c00bd30a430b3f34be4445cb1c",
+ "@rollup/rollup-linux-loongarch64-gnu@npm:4.35.0": "70e331016745c7688c861ad49f54c444f37570171fbd810c4116cc5335b72802d540e0f6019a0b4475b5c570f51691f313548f9c5bececa37eb759b02802f6dd",
+ "@rollup/rollup-linux-loongarch64-gnu@npm:4.37.0": "d69b456caaa8c12c2936a6836dc2dd3b9169ebfc2d888c904d3407fb2c53ba99a3323bfd36d447c17ff0b9e31b4709defaf72cafb32c23c773fbffdee50bc7fc",
+ "@rollup/rollup-linux-powerpc64le-gnu@npm:4.35.0": "73419009942cf6d86815a0a47d7e21b1dcb0d029561cc76d53d2cd1b7b9281f2a532eda7c7bfb546cc997337c9a1c68a1aabfee0330a76d249c119567b9607f5",
+ "@rollup/rollup-linux-powerpc64le-gnu@npm:4.37.0": "abc5b4e7960965f01948dbf7e956695643cf6ab78ef1d14f2bc34b93cbb85a9ccb9c005dfbf948a80145cd808078a34d216c9d4e801570e2ca638a563ecd0ae2",
+ "@rollup/rollup-linux-riscv64-gnu@npm:4.35.0": "9c182d0055b6b93008ec8958eeb715e6083c733ec1cf146151572a3111c98e777fdcb0e159c0b9edf5760c1dfcb295f8cbaba4d6115e0a4800bb53828cab228f",
+ "@rollup/rollup-linux-riscv64-gnu@npm:4.37.0": "a9a5fe6a24955dae476c9ab5fb22d5f807e0ff916ff1ec289c8050e6688695e9c434dfdb130282712dd3657bf883af4544ed1ebc77b68530ccb8e33af484a41e",
+ "@rollup/rollup-linux-riscv64-musl@npm:4.37.0": "e6cd70f709a59037f496d1086f34148ef2ba92bbf1813e07ccd52b3643822486fa95671a6ed4a3397b9ef23e3a4dea20926f9e1a26b00a06173202627cf2f0ce",
+ "@rollup/rollup-linux-s390x-gnu@npm:4.35.0": "5d9b6fefcf02ded2487b5e71633c2c8aacd7b6b0a8ab36367fb672995155c0a3164716ef8df859793bb7f6e8d8d1e8435a9bb4f0814bc1135fc13276abec0f79",
+ "@rollup/rollup-linux-s390x-gnu@npm:4.37.0": "a6704bd605db0a99a60947a6a69ad6d1dbf712f57d4bcaba24a122cc062b2513319726e8211410d5d9d2bc1face3b153c45c4c9209ead6a25f87cf6e79828864",
+ "@rollup/rollup-linux-x64-gnu@npm:4.35.0": "1488b698bd314b6af0ee114d7cef40c480c6de5c3d7a429f376f5ab93e0c599c442b2fad9eceec95797b189522bb455ecedb3a3f8c2787239da712403b2b3105",
+ "@rollup/rollup-linux-x64-gnu@npm:4.37.0": "fb0ee05c300d73fb05e5584418c41cd616aca8ccb4c4397b56ada769b4e3655223cdea2ce136c3ff3ed3efb9f74800acbd4801135afc3fbd49b29fff092ebd8c",
+ "@rollup/rollup-linux-x64-musl@npm:4.35.0": "1d9ac6fd0b9293e86c581c9b2233128b79ddd43e62d982e98a09950fd1892ec4aca30ad9ef356ac9807aa185c3e5888e4eae018dd24409340c300a8c28fa4a14",
+ "@rollup/rollup-linux-x64-musl@npm:4.37.0": "c502057b9ef77bb43df6337eb02c5bbb8f7aabb2780f3de8256048ade427d319e4d5cca4e4238c3076ddddfd9749d9854e8fbc7f6e50cec84e778da6dafe90b7",
+ "@rollup/rollup-win32-arm64-msvc@npm:4.35.0": "7d4174cb4976d62df0186c6eb85e1d5e7837773b2faf8da5dc2238c5d08b15a27b098c187e78e425f235b84eda666dc5797ca5f672849842ce7c1a0de673c03f",
+ "@rollup/rollup-win32-arm64-msvc@npm:4.37.0": "4a6a5c2fb0c520edd3f67deb773ff600698a665ac5a4ba4ac8147836aa054be0f3fab54c841f3eef2b638224dd432a62b5f48b3704f5d3dd37ec7fcfc6a70591",
+ "@rollup/rollup-win32-ia32-msvc@npm:4.35.0": "dca1a8f3cd91f4c025612d7ec28d624dda0e02d13ae98fcafaa53139fc94e2a9df507635d847c79176ff37dd9bb5d272578c4cdfd9af05bc1cdb78e31296669f",
+ "@rollup/rollup-win32-ia32-msvc@npm:4.37.0": "439f4339c26c104333b3f55a34ef1a2280b22eec165362eee1816548a0b93f0440a6e3982af9263d1a4158d665a6ecba68c64bf12a786eae69db232399598321",
+ "@rollup/rollup-win32-x64-msvc@npm:4.35.0": "c6d7a0cbfb489d60ea6f74a2c77c6ae8f9c9b4dae4106da59b640718c4fe7b2273d59ac07fd6d83ac95bcaf172ea908e2eb09c4bab3e0dfbc957e572ab055482",
+ "@rollup/rollup-win32-x64-msvc@npm:4.37.0": "0c85f06fddfc73dd4457a6c6ec841e52d0fb81179fe63754bd3c02c4795ec338685bd14ff49672ddfb3767aecf4f01f4a5da5730b0cf62be318753390027de37",
+ "@sentry/cli-darwin@npm:2.42.2": "3df1b8774a2dffba2a413420b573d0ddb94a51ab530cfcf719736cac37fb9b6ff835516b4b783771f003659436a00d3ee5ea201fb2510c1ea517d7256c619b50",
+ "@sentry/cli-darwin@npm:2.42.4": "c1a2aee566a2d35f6c39f312dd6f4a30b6fa2edc973009fedefc4dc7e36fc26e42cbaf9269e40f6aed8fcc3ab5c106293248b09a1bbec0c3a3872810c949c2bc",
+ "@sentry/cli-linux-arm64@npm:2.42.2": "17ab90626910c137925aa307bfb4b2f5790a756eaec9bbfa1066b1c2265ec1981ae3d7c8b2014071da9dd3a604cf150ba9a8d93a16e0da155831c7c8a524f956",
+ "@sentry/cli-linux-arm64@npm:2.42.4": "a9ab66366473ec445cf2daacaa934b16d2b1376a9a7eb1240a35e970c5e93e098248cadc4c2b08a9ab8a97dc3eefb70ff1e70820f15f29227ed07ab165d31e91",
+ "@sentry/cli-linux-arm@npm:2.42.2": "2d31e7d2603f124e2a36626e91dd8c50a0eba27386b94a7076115022e50193140d7f613850adf64740ee46102cec99845750df05430dd16ea50f01607ead6d2d",
+ "@sentry/cli-linux-arm@npm:2.42.4": "47ca5bd7049983c863384a318e231aa0cb7ec5f9d5cc7ac6c11f86c4540e3ce4217545eeec2f526dfce3e9580c665ea61d17b40b9cc83e2887f2318b8f527923",
+ "@sentry/cli-linux-i686@npm:2.42.2": "e5c52d65f507393c786373aaff873d6522f9ab26ef3cea498268ef63d053dc9f4e806c3295c22b48c5dd1f7ecf0c47bfaa0f7545424ef81a3cd51914de2af2bc",
+ "@sentry/cli-linux-i686@npm:2.42.4": "cd201e1beb369028601fe5c11c6126529c63c08979f01a00ac2aed88979790600a52cd150259a8fd0d9acf15e52d3da2f6fea69b083abf2e09f2293caf968989",
+ "@sentry/cli-linux-x64@npm:2.42.2": "62ed35b355b7673c8cc1d1542c21d1fb908022c2cc951eda47ee4a55c3e53499c1eca08b93408b547adf9ffe7bcb6d43efa50f185f6fa66155a13373ea79d5f3",
+ "@sentry/cli-linux-x64@npm:2.42.4": "84c1758ddb2a4c0529d06e73cacfce707711e215c16ceb028e69abe62c1e9bc4b5d410295ac75bc4399b64d76b4516df58ecebe2b46f879eb781e189537f993c",
+ "@sentry/cli-win32-i686@npm:2.42.2": "3876d8d3f6139d984714ed5fed8ef124d32af8f5d7cd6968adc8e910727e12eb39d85dcc6304a5e249dce7d8e93e11f8bcfde83545835d959f260bf4b1914d00",
+ "@sentry/cli-win32-i686@npm:2.42.4": "14207ff600dde2406c9d20c794197628e80b71ecdb14e930db67c2f3d806ac0db66303c6284bae4f108e30e357cfa894510a479ed7bbb74ca89a627bbbd4ff1b",
+ "@sentry/cli-win32-x64@npm:2.42.2": "16e6deb7f0faaed1a88c121463b3ef727726351ec04afe3308ff3775d26e1d7b990a3fd65b216d7fc2ac4f6967c625f14e6dbc2dab99fcc9d7a625ec80422ab8",
+ "@sentry/cli-win32-x64@npm:2.42.4": "3e6f5fa0bde62824ebbe427a47cba538fd5b7e19ce10b6bb0743ac0f52fc1e677f9c68a84e22fe5b995d29ba00bd1370ed172e6e2ad1732b4c07f181cb1d7d83",
+ "@unrs/rspack-resolver-binding-darwin-arm64@npm:1.2.2": "8bd22eb6e552a6e205f559fd2a08ab5abba0845df88fa857472a1b5eda460ba890dec1f51d4ead8d1aa33c304378642aea363a1911ad781811ec59f61ace3fea",
+ "@unrs/rspack-resolver-binding-darwin-x64@npm:1.2.2": "9fbeb7d55fc163ba1a8b3f834e2acdc371145a805b96e46636ff95e76eb895e144b9002fbf1d14ce54a0386e07a7293e6e559f3943f5d0afa91e51370c3191da",
+ "@unrs/rspack-resolver-binding-freebsd-x64@npm:1.2.2": "6391109bcce6c9a82ea283a59c120fdcad4e0adfc3403096333c5ff2c11e526401aa03f69557a3b314083aaf1cc260b5cbbd945c1177ffcd27d001ab1e212bf5",
+ "@unrs/rspack-resolver-binding-linux-arm-gnueabihf@npm:1.2.2": "1a97e5a33e937861933c3e1eb100f419229b6e51046bdee1b10ca178c78f26017b6d1cda2dcd64aa65c868cbdd59797e914133d509fc291678beb4349f8717db",
+ "@unrs/rspack-resolver-binding-linux-arm64-gnu@npm:1.2.2": "30ea0931074f679fc416a8b90d249673fd9d27a89e0df4c0a936d0ee1329145e0bbf5ff9680e30cd5549eb261833dfaa0d8018b7ea7b6ae1ece86ea4cce9e440",
+ "@unrs/rspack-resolver-binding-linux-arm64-musl@npm:1.2.2": "bee718ba77407238445f2d1950b9287b52fed11f71ebaabd826059ab85cffd3d174e93e2cfbeff151057abd7fb2f4dcbb583aeefd92c9b5342b715ea0aa7cee9",
+ "@unrs/rspack-resolver-binding-linux-x64-gnu@npm:1.2.2": "d3f4577b88c2d53372144056f8340af9cabd3b48b751a619dd662f7df2e799dcfcd53547eebb6ca1e21e847e2de74d5c692f13be5080076d09e7190904f8d701",
+ "@unrs/rspack-resolver-binding-linux-x64-musl@npm:1.2.2": "a07c77a1b3f513b262bed00265186d336b07bb271f93ff1998212d66e236310ee53eb618d26bd49f4d021d7f3407e2aa115cf37b28616ba750a8c54065d4db04",
+ "@unrs/rspack-resolver-binding-wasm32-wasi@npm:1.2.2": "ffc4938bdb6163a789f308b7df55541871dfb5750d59b1a6ac709f76aaa19eb58c6d4ab8f509b4a0e994e97592c682120e78855ff57bfd0d7cf46c008b63a992",
+ "@unrs/rspack-resolver-binding-win32-arm64-msvc@npm:1.2.2": "1e45426dc0d2d4cdf68b4a331ef4ab3f51c2e60d939654d7db581b32c624636545f829753ee982fd9c5c505150a4ef725aeedb890a7d99f2b4f2c4b0a0e4b18d",
+ "@unrs/rspack-resolver-binding-win32-x64-msvc@npm:1.2.2": "06db4f536ffe00470cc68904cce08d0f6f8305ce250d2d3b21fbf4f0c53c69074633a82edcfba57a87121595986e6636f40f087ebdfca0e9320c7c8263cdb241"
+}
diff --git a/nix/nixosModule.nix b/nix/nixosModule.nix
new file mode 100644
index 000000000..ac088b402
--- /dev/null
+++ b/nix/nixosModule.nix
@@ -0,0 +1,234 @@
+self: {
+ config,
+ pkgs,
+ lib ? pkgs.lib,
+ ...
+}:
+with lib; let
+ cfg = config.services.sourcebot;
+in {
+ options.services.sourcebot = {
+ enable = mkEnableOption "Enable SourceBot";
+ dataDir = mkOption {
+ type = types.path;
+ default = "/var/lib/sourcebot";
+ description = "Root data directory for SourceBot";
+ };
+ package = lib.mkOption {
+ type = types.package;
+ default = self.packages.${pkgs.system}.sourcebot;
+ description = "Package to use for nixos-cli";
+ };
+ # Override default, based on DATA_DIR
+ dataCacheDir = mkOption {
+ type = types.path;
+ default = "/var/cache/sourcebot";
+ description = "Directory for SourceBot data cache";
+ };
+ envFile = mkOption {
+ type = types.nullOr types.path;
+ default = null;
+ description = "Environment file for additional settings";
+ };
+
+ logLevel = mkOption {
+ type = types.enum ["debug" "info" "warn" "error"];
+ default = "info";
+ description = "SourceBot logging level";
+ };
+ authEnabled = mkOption {
+ type = types.bool;
+ default = false;
+ description = "Enables authentication in SourceBot";
+ };
+ telemetryDisabled = mkOption {
+ type = types.bool;
+ default = true;
+ description = "Disables telemetry collection in SourceBot";
+ };
+ openFirewall = mkOption {
+ type = types.bool;
+ default = true;
+ description = "Open Firwall ports for SourceBot";
+ };
+
+ configPath = mkOption {
+ type = types.str;
+ description = "Path to the SourceBot configuration file";
+ };
+ port = mkOption {
+ type = types.int;
+ default = 7734;
+ description = "TCP port for the SourceBot web server to listen on";
+ };
+ hostname = mkOption {
+ type = types.str;
+ default = "0.0.0.0";
+ description = "Hostname or IP address for the SourceBot web server to bind to";
+ };
+ authUrl = mkOption {
+ type = types.str;
+ default = "http://${cfg.hostname}:${toString cfg.port}";
+ description = "Hostname or IP address for the SourceBot web server to bind to";
+ };
+ redisPort = mkOption {
+ type = types.int;
+ default = 16379;
+ description = "TCP port for the SourceBot Redis server to listen on";
+ };
+ };
+
+ config = mkIf cfg.enable {
+ # Create a dedicated system group for SourceBot
+ users.groups.sourcebot = {};
+ # Create a dedicated system user for SourceBot
+ users.users.sourcebot = {
+ isSystemUser = true;
+ group = "sourcebot"; # primary group
+ description = "Service account for SourceBot";
+ home = cfg.dataDir;
+ shell = "/run/current-system/sw/bin/false";
+ };
+
+ networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [cfg.port];
+ # Enable redis using the existing NixOS module
+ services.redis.servers.sourcebot = {
+ enable = true;
+ user = "sourcebot";
+ port = cfg.redisPort;
+ };
+
+ # Enable Postgres for SourceBot with isolated data directory and database
+ services.postgresql = {
+ enable = true;
+ enableTCPIP = true;
+ ensureDatabases = ["sourcebot"];
+ ensureUsers = [
+ {
+ name = "sourcebot";
+ ensureDBOwnership = true;
+ }
+ ];
+ # Allow connections from any container IP addresses
+ authentication = mkBefore ''
+ local sourcebot sourcebot trust
+ host sourcebot sourcebot 127.0.0.1/32 trust
+ host sourcebot sourcebot ::1/128 trust
+ '';
+ };
+
+ # Create and own data directories
+ systemd.tmpfiles.rules = [
+ # ensure root data dir
+ "d ${cfg.dataDir} 0755 sourcebot sourcebot -"
+ # ensure cache dir
+ "d ${cfg.dataCacheDir} 0755 sourcebot sourcebot -"
+ ];
+
+ # Zoekt search service
+ systemd.services.sourcebot-zoekt = {
+ description = "SourceBot Zoekt Search Service";
+ after = ["network.target" "redis.service"];
+ wants = ["redis.service"];
+ serviceConfig = {
+ # Run under the sourcebot user
+ User = "sourcebot";
+ Group = "sourcebot";
+ ExecStart = "${pkgs.zoekt}/bin/zoekt-webserver -index ${cfg.dataCacheDir}/index -rpc";
+ Restart = "on-failure";
+ RestartSec = "5s";
+ Environment = [
+ "DATA_DIR=${cfg.dataDir}"
+ "DATA_CACHE_DIR=${cfg.dataCacheDir}"
+ ];
+ };
+ };
+
+ systemd.services.sourcebot-db-setup = {
+ description = "SourceBot Database setup";
+ after = ["network.target" "postgresql.service"];
+ wants = ["postgresql.service"];
+ serviceConfig = {
+ # Run under the sourcebot user
+ Type = "oneshot";
+ User = "sourcebot";
+ Group = "sourcebot";
+ ExecStart = "${pkgs.prisma}/bin/prisma migrate deploy --schema ${cfg.package}/packages/db/prisma/schema.prisma";
+ Environment = [
+ "DATABASE_URL=postgresql://sourcebot@localhost:${toString config.services.postgresql.settings.port}/sourcebot"
+ "PATH=${makeBinPath (with pkgs; [prisma openssl])}"
+ ];
+ Restart = "on-failure";
+ RestartSec = "5s";
+ };
+ };
+ # Web frontend service
+ systemd.services.sourcebot-web = {
+ description = "SourceBot Web Service";
+ after = ["network.target" "sourcebot-zoekt.service" "sourcebot-db-setup.service"];
+ wants = ["sourcebot-zoekt.service" "sourcebot-db-setup.service"];
+ wantedBy = ["multi-user.target"];
+ serviceConfig = {
+ # Run under the sourcebot user
+ User = "sourcebot";
+ Group = "sourcebot";
+ Environment =
+ [
+ "DATA_DIR=${cfg.dataDir}"
+ "DATA_CACHE_DIR=${cfg.dataCacheDir}"
+ "PORT=${toString cfg.port}"
+ "HOSTNAME=${cfg.hostname}"
+ "DATABASE_URL=postgresql://sourcebot@localhost:${toString config.services.postgresql.settings.port}/sourcebot"
+ "REDIS_URL=redis://localhost:${toString cfg.redisPort}"
+ "CONFIG_PATH=${cfg.configPath}"
+ "SOURCEBOT_LOG_LEVEL=${cfg.logLevel}"
+ "SOURCEBOT_TENANCY_MODE=single"
+ "SOURCEBOT_AUTH_ENABLED=${boolToString cfg.authEnabled}"
+ "SOURCEBOT_TELEMETRY_DISABLED=${boolToString cfg.telemetryDisabled}"
+ "AUTH_URL=${cfg.authUrl}"
+ ]
+ ++ optional (cfg.envFile == null) [
+ "AUTH_SECRET=00000000000000000000000000000000000000000000"
+ "SOURCEBOT_ENCRYPTION_KEY=00000000000000000000000000000000"
+ ];
+ EnvironmentFile = cfg.envFile;
+ ExecStart = "${cfg.package}/bin/sourcebot-web";
+ Restart = "always";
+ };
+ };
+
+ # Backend API service
+ systemd.services.sourcebot-backend = {
+ description = "SourceBot Backend Service";
+ after = ["network.target" "sourcebot-zoekt.service" "sourcebot-db-setup.service"];
+ wants = ["sourcebot-zoekt.service" "sourcebot-db-setup.service"];
+ wantedBy = ["multi-user.target"];
+ serviceConfig = {
+ # Run under the sourcebot user
+ User = "sourcebot";
+ Group = "sourcebot";
+ Environment =
+ [
+ "DATA_DIR=${cfg.dataDir}"
+ "DATA_CACHE_DIR=${cfg.dataCacheDir}"
+ "DATABASE_URL=postgresql://sourcebot@localhost:${toString config.services.postgresql.settings.port}/sourcebot"
+ "REDIS_URL=redis://localhost:${toString cfg.redisPort}"
+ "CONFIG_PATH=${cfg.configPath}"
+ "SOURCEBOT_LOG_LEVEL=${cfg.logLevel}"
+ "SOURCEBOT_TENANCY_MODE=single"
+ "SOURCEBOT_AUTH_ENABLED=${boolToString cfg.authEnabled}"
+ "SOURCEBOT_TELEMETRY_DISABLED=${boolToString cfg.telemetryDisabled}"
+ "AUTH_URL=http://${cfg.hostname}:${toString cfg.port}"
+ ]
+ ++ optional (cfg.envFile == null) [
+ "AUTH_SECRET=00000000000000000000000000000000000000000000"
+ "SOURCEBOT_ENCRYPTION_KEY=00000000000000000000000000000000"
+ ];
+ EnvironmentFile = cfg.envFile;
+ ExecStart = "${cfg.package}/bin/sourcebot-backend --cacheDir ${cfg.dataCacheDir}";
+ Restart = "on-failure";
+ RestartSec = "5s";
+ };
+ };
+ };
+}
diff --git a/nix/nixosTest.nix b/nix/nixosTest.nix
new file mode 100644
index 000000000..38a45ddff
--- /dev/null
+++ b/nix/nixosTest.nix
@@ -0,0 +1,31 @@
+{
+ pkgs,
+ self,
+}:
+pkgs.testers.nixosTest {
+ name = "sourcebot-nixos-module-test";
+ nodes.machine = {config, ...}: {
+ virtualisation.graphics = false;
+ documentation.enable = false;
+ imports = [self.nixosModules.sourcebot];
+ # disables ForwardToConsole=yes:
+ # https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/testing/test-instrumentation.nix#L207
+ services.journald.extraConfig = pkgs.lib.mkForce "";
+ services.sourcebot = {
+ enable = true;
+ configPath = "${pkgs.writeText "config" ''
+ {
+ }
+ ''}";
+ };
+ };
+ testScript = ''
+ start_all()
+ machine.wait_for_unit("sourcebot-web.service")
+ machine.wait_for_unit("sourcebot-backend.service")
+ machine.wait_for_unit("sourcebot-db-setup.service")
+ machine.wait_for_unit("postgresql.service")
+ machine.wait_for_unit("sourcebot-zoekt.service")
+ machine.succeed("${pkgs.curl}/bin/curl http://localhost:7734")
+ '';
+}
diff --git a/nix/overlay.nix b/nix/overlay.nix
new file mode 100644
index 000000000..24464a3dc
--- /dev/null
+++ b/nix/overlay.nix
@@ -0,0 +1,12 @@
+final: prev: {
+ sourcebot = final.callPackage ./sourcebot.nix {};
+ zoekt = prev.zoekt.overrideAttrs (old: rec {
+ vendorHash = "sha256-laiBp+nMWEGofu7zOgfM2b8MIC+Dfw7eCLgb/5zf9oo=";
+ src = final.fetchFromGitHub {
+ owner = "sourcegraph";
+ repo = "zoekt";
+ rev = "12a2f4ad075359a09bd8a91793acb002211217aa";
+ hash = "sha256-JByTgJsnqLlP7hNbQumM4zqZZuj7igc2V35vw0ahCqM=";
+ };
+ });
+}
diff --git a/nix/sourcebot.nix b/nix/sourcebot.nix
new file mode 100644
index 000000000..ca1d4a11e
--- /dev/null
+++ b/nix/sourcebot.nix
@@ -0,0 +1,111 @@
+{
+ pkgs,
+ lib,
+}:
+pkgs.stdenv.mkDerivation (finalAttrs: {
+ name = "sourcebot";
+
+ src = ../.;
+
+ buildInputs = with pkgs; [
+ nodejs
+ yarn
+ redis
+ openssl
+ ];
+ buildPhase = ''
+ runHook preBuild
+ ${lib.getExe pkgs.yarn-berry} workspaces foreach -R --from '{@sourcebot/schemas,@sourcebot/error,@sourcebot/db}' run build
+ ${lib.getExe pkgs.yarn-berry} build:deps
+ ${lib.getExe pkgs.yarn-berry} build
+ runHook postBuild
+ '';
+ missingHashes = ./missing-hashes.json;
+ offlineCache = pkgs.yarn-berry.fetchYarnBerryDeps {
+ inherit (finalAttrs) src missingHashes;
+ hash = "sha256-mM268PS5jgT+glNRtjNMLV936Z/Xgd16YavADWUiSmE=";
+ };
+
+ nativeBuildInputs = with pkgs; [
+ yarn-berry.yarnBerryConfigHook
+ yarn-berry
+ nodejs
+ nodePackages.prisma
+ makeWrapper
+ ];
+
+ env = {
+ YARN_ENABLE_SCRIPTS = "false";
+ PRISMA_SCHEMA_ENGINE_BINARY = "${pkgs.prisma-engines}/bin/schema-engine";
+ PRISMA_QUERY_ENGINE_BINARY = "${pkgs.prisma-engines}/bin/query-engine";
+ PRISMA_QUERY_ENGINE_LIBRARY = "${pkgs.prisma-engines}/lib/libquery_engine.node";
+ PRISMA_FMT_BINARY = "${pkgs.prisma-engines}/bin/prisma-fmt";
+ };
+
+ installPhase = ''
+ runHook preInstall
+
+ cp -r packages/web/.next/standalone $out
+ cp -r node_modules/* $out/node_modules
+
+ mkdir -p $out/packages/web
+ cp -r packages/web/public $out/packages/web/public
+ mkdir -p $out/packages/web/.next
+ cp -r packages/web/.next/static $out/packages/web/.next/static
+
+ mkdir -p $out/packages/backend
+ cp -r packages/backend/node_modules/* $out/node_modules
+ cp -r packages/backend/* $out/packages/backend
+
+ cp -r packages/db/* $out/packages/db
+ cp -r packages/schemas/* $out/packages/schemas
+ cp -r packages/crypto/* $out/packages/crypto
+ cp -r packages/error $out/packages/error
+ cp -r packages/mcp $out/packages/mcp
+ cp -r node_modules/* $out/node_modules
+
+ mkdir -p $out/bin
+
+ rm -rf $out/pacakages/web/.next/cache
+ ln -s /var/cache/sourcebot $out/packages/web/.next/cache
+
+ # web
+ cat < $out/bin/sourcebot-web
+ #!${pkgs.runtimeShell}
+ export PATH=\$PATH:${pkgs.openssl}/bin
+ export NODE_ENV=production
+ export NODE_PATH=$out/node_modules
+ export PRISMA_SCHEMA_ENGINE_BINARY=${pkgs.prisma-engines}/bin/schema-engine
+ export PRISMA_QUERY_ENGINE_BINARY=${pkgs.prisma-engines}/bin/query-engine
+ export PRISMA_QUERY_ENGINE_LIBRARY=${pkgs.prisma-engines}/lib/libquery_engine.node
+ export PRISMA_FMT_BINARY=${pkgs.prisma-engines}/bin/prisma-fmt
+
+ exec ${pkgs.nodejs}/bin/node $out/packages/web/server.js "\$@"
+ EOF
+
+ # backend
+ cat < $out/bin/sourcebot-backend
+ #!${pkgs.runtimeShell}
+ export NODE_ENV=production
+ export NODE_PATH=$out/node_modules
+ export PRISMA_SCHEMA_ENGINE_BINARY=${pkgs.prisma-engines}/bin/schema-engine
+ export PRISMA_QUERY_ENGINE_BINARY=${pkgs.prisma-engines}/bin/query-engine
+ export PRISMA_QUERY_ENGINE_LIBRARY=${pkgs.prisma-engines}/lib/libquery_engine.node
+ export PRISMA_FMT_BINARY=${pkgs.prisma-engines}/bin/prisma-fmt
+
+ export PATH=${pkgs.lib.makeBinPath (with pkgs; [prisma openssl git zoekt])}
+ exec ${pkgs.nodejs}/bin/node $out/packages/backend/dist/index.js "\$@"
+ EOF
+
+ # mcp
+ cat < $out/bin/sourcebot-mcp
+ #!${pkgs.runtimeShell}
+ export NODE_ENV=production
+ export NODE_PATH=$out/node_modules
+ exec ${pkgs.nodejs}/bin/node $out/packages/mcp/dist/index.js "\$@"
+ EOF
+ chmod +x $out/bin/*
+
+ runHook postInstall
+ '';
+})
diff --git a/packages/backend/src/gitlab.ts b/packages/backend/src/gitlab.ts
index f08b42186..7c6d62284 100644
--- a/packages/backend/src/gitlab.ts
+++ b/packages/backend/src/gitlab.ts
@@ -7,6 +7,7 @@ import { PrismaClient } from "@sourcebot/db";
import { processPromiseResults, throwIfAnyFailed } from "./connectionUtils.js";
import * as Sentry from "@sentry/node";
import { env } from "./env.js";
+import { log } from "console";
const logger = createLogger('gitlab');
export const GITLAB_CLOUD_HOSTNAME = "gitlab.com";
@@ -45,15 +46,27 @@ export const getGitLabReposFromConfig = async (config: GitlabConnectionConfig, o
if (config.all === true) {
if (hostname !== GITLAB_CLOUD_HOSTNAME) {
try {
- logger.debug(`Fetching all projects visible in ${config.url}...`);
- const { durationMs, data: _projects } = await measure(async () => {
- const fetchFn = () => api.Projects.all({
- perPage: 100,
- });
- return fetchWithRetry(fetchFn, `all projects in ${config.url}`, logger);
+ // Fetch all groups
+ logger.debug(`Fetching all groups visible in ${config.url}...`);
+ const { durationMs: groupsDuration, data: _groups } = await measure(async () => {
+ const fetchFn = () => api.Groups.all({ perPage: 100, allAvailable: true });
+ return fetchWithRetry(fetchFn, `all groups in ${config.url}`, logger);
+ });
+ logger.debug(`Found ${_groups.length} groups in ${groupsDuration}ms.`);
+
+ config.groups = _groups.map(g => g.full_path);
+
+ logger.debug(`Found these groups: ${config.groups.join('\n')}`);
+
+ // Fetch all users - too much for sourcebot/gitlab
+ logger.debug(`Fetching all users visible in ${config.url}...`);
+ const { durationMs: usersDuration, data: _users } = await measure(async () => {
+ const fetchFn = () => api.Users.all({ perPage: 100, withoutProjects: false });
+ return fetchWithRetry(fetchFn, `all users in ${config.url}`, logger);
});
- logger.debug(`Found ${_projects.length} projects in ${durationMs}ms.`);
- allRepos = allRepos.concat(_projects);
+ logger.debug(`Found ${_users.length} users in ${usersDuration}ms.`);
+
+ config.users = _users.map(u => u.username);
} catch (e) {
Sentry.captureException(e);
logger.error(`Failed to fetch all projects visible in ${config.url}.`, e);
@@ -65,76 +78,96 @@ export const getGitLabReposFromConfig = async (config: GitlabConnectionConfig, o
}
if (config.groups) {
- const results = await Promise.allSettled(config.groups.map(async (group) => {
- try {
- logger.debug(`Fetching project info for group ${group}...`);
- const { durationMs, data } = await measure(async () => {
- const fetchFn = () => api.Groups.allProjects(group, {
- perPage: 100,
- includeSubgroups: true
+ const batchSize = 10;
+ const allResults = [];
+
+ // Process groups in batches of 10
+ for (let i = 0; i < config.groups.length; i += batchSize) {
+ const batch = config.groups.slice(i, i + batchSize);
+ logger.debug(`Processing batch ${i/batchSize + 1} of ${Math.ceil(config.groups.length/batchSize)} (${batch.length} groups)`);
+
+ const batchResults = await Promise.allSettled(batch.map(async (group) => {
+ try {
+ logger.debug(`Fetching project info for group ${group}...`);
+ const { durationMs, data } = await measure(async () => {
+ const fetchFn = () => api.Groups.allProjects(group, {
+ perPage: 100,
+ includeSubgroups: true
+ });
+ return fetchWithRetry(fetchFn, `group ${group}`, logger);
});
- return fetchWithRetry(fetchFn, `group ${group}`, logger);
- });
- logger.debug(`Found ${data.length} projects in group ${group} in ${durationMs}ms.`);
- return {
- type: 'valid' as const,
- data
- };
- } catch (e: any) {
- Sentry.captureException(e);
- logger.error(`Failed to fetch projects for group ${group}.`, e);
-
- const status = e?.cause?.response?.status;
- if (status === 404) {
- logger.error(`Group ${group} not found or no access`);
+ logger.debug(`Found ${data.length} projects in group ${group} in ${durationMs}ms.`);
return {
- type: 'notFound' as const,
- value: group
+ type: 'valid' as const,
+ data
};
- }
- throw e;
- }
- }));
+ } catch (e: any) {
+ Sentry.captureException(e);
+ logger.error(`Failed to fetch projects for group ${group}.`, e);
- throwIfAnyFailed(results);
- const { validItems: validRepos, notFoundItems: notFoundOrgs } = processPromiseResults(results);
+ const status = e?.cause?.response?.status;
+ if (status === 404) {
+ logger.error(`Group ${group} not found or no access`);
+ return {
+ type: 'notFound' as const,
+ value: group
+ };
+ }
+ throw e;
+ }
+ }));
+ allResults.push(...batchResults);
+ }
+ const { validItems: validRepos, notFoundItems: notFoundOrgs } = processPromiseResults(allResults);
allRepos = allRepos.concat(validRepos);
notFound.orgs = notFoundOrgs;
+ logger.debug(`Found ${validRepos.length} valid repositories in groups.`);
+ logger.debug(`Not found groups: ${notFoundOrgs.join(', ')}`);
+ logger.debug(`These repositories will be downloaded: ${allRepos.map(repo => repo.path_with_namespace).join('\n')}`);
}
if (config.users) {
- const results = await Promise.allSettled(config.users.map(async (user) => {
- try {
- logger.debug(`Fetching project info for user ${user}...`);
- const { durationMs, data } = await measure(async () => {
- const fetchFn = () => api.Users.allProjects(user, {
- perPage: 100,
+ const batchSize = 10;
+ const allResults = [];
+
+ // Process users in batches of 10
+ for (let i = 0; i < config.users.length; i += batchSize) {
+ const batch = config.users.slice(i, i + batchSize);
+ logger.debug(`Processing batch ${i/batchSize + 1} of ${Math.ceil(config.users.length/batchSize)} (${batch.length} users)`);
+
+ const batchResults = await Promise.allSettled(batch.map(async (user) => {
+ try {
+ logger.debug(`Fetching project info for user ${user}...`);
+ const { durationMs, data } = await measure(async () => {
+ const fetchFn = () => api.Users.allProjects(user, {
+ perPage: 100,
+ });
+ return fetchWithRetry(fetchFn, `user ${user}`, logger);
});
- return fetchWithRetry(fetchFn, `user ${user}`, logger);
- });
- logger.debug(`Found ${data.length} projects owned by user ${user} in ${durationMs}ms.`);
- return {
- type: 'valid' as const,
- data
- };
- } catch (e: any) {
- Sentry.captureException(e);
- logger.error(`Failed to fetch projects for user ${user}.`, e);
-
- const status = e?.cause?.response?.status;
- if (status === 404) {
- logger.error(`User ${user} not found or no access`);
+ logger.debug(`Found ${data.length} projects owned by user ${user} in ${durationMs}ms.`);
return {
- type: 'notFound' as const,
- value: user
+ type: 'valid' as const,
+ data
};
- }
- throw e;
- }
- }));
+ } catch (e: any) {
+ Sentry.captureException(e);
+ logger.error(`Failed to fetch projects for user ${user}.`, e);
- throwIfAnyFailed(results);
- const { validItems: validRepos, notFoundItems: notFoundUsers } = processPromiseResults(results);
+ const status = e?.cause?.response?.status;
+ if (status === 404) {
+ logger.error(`User ${user} not found or no access`);
+ return {
+ type: 'notFound' as const,
+ value: user
+ };
+ }
+ throw e;
+ }
+ }));
+
+ allResults.push(...batchResults);
+ }
+ const { validItems: validRepos, notFoundItems: notFoundUsers } = processPromiseResults(allResults);
allRepos = allRepos.concat(validRepos);
notFound.users = notFoundUsers;
}
@@ -169,7 +202,6 @@ export const getGitLabReposFromConfig = async (config: GitlabConnectionConfig, o
}
}));
- throwIfAnyFailed(results);
const { validItems: validRepos, notFoundItems: notFoundRepos } = processPromiseResults(results);
allRepos = allRepos.concat(validRepos);
notFound.repos = notFoundRepos;
diff --git a/packages/backend/src/repoManager.ts b/packages/backend/src/repoManager.ts
index 50dc0478b..77e19681f 100644
--- a/packages/backend/src/repoManager.ts
+++ b/packages/backend/src/repoManager.ts
@@ -165,7 +165,9 @@ export class RepoManager implements IRepoManager {
});
if (repos.length > 0) {
- await this.scheduleRepoIndexingBulk(repos);
+ for (let i = 0; i < repos.length; i += 100) {
+ await this.scheduleRepoIndexingBulk(repos.slice(i, i + 100));
+ }
}
}
diff --git a/packages/web/src/actions.ts b/packages/web/src/actions.ts
index e9adbc2a3..390c7a08b 100644
--- a/packages/web/src/actions.ts
+++ b/packages/web/src/actions.ts
@@ -920,15 +920,17 @@ export const flagConnectionForSync = async (connectionId: number, domain: string
export const flagReposForIndex = async (repoIds: number[], domain: string) => sew(() =>
withAuth((userId) =>
withOrgMembership(userId, domain, async ({ org }) => {
- await prisma.repo.updateMany({
- where: {
- id: { in: repoIds },
- orgId: org.id,
- },
- data: {
- repoIndexingStatus: RepoIndexingStatus.NEW,
- }
- });
+ for (let i = 0; i < repoIds.length; i += 1000) {
+ await prisma.repo.updateMany({
+ where: {
+ id: { in: repoIds.slice(i, i + 1000) },
+ orgId: org.id,
+ },
+ data: {
+ repoIndexingStatus: RepoIndexingStatus.NEW,
+ }
+ });
+ }
return {
success: true,
@@ -2152,4 +2154,4 @@ export const encryptValue = async (value: string) => {
export const decryptValue = async (iv: string, encryptedValue: string) => {
return decrypt(iv, encryptedValue);
-}
\ No newline at end of file
+}
diff --git a/packages/web/src/initialize.ts b/packages/web/src/initialize.ts
index 975fa83d0..1e1ad1a40 100644
--- a/packages/web/src/initialize.ts
+++ b/packages/web/src/initialize.ts
@@ -67,16 +67,18 @@ const syncConnections = async (connections?: { [key: string]: ConnectionConfig }
// Re-try any repos that failed to index.
const failedRepos = currentConnection?.repos.filter(repo => repo.repo.repoIndexingStatus === RepoIndexingStatus.FAILED).map(repo => repo.repo.id) ?? [];
if (failedRepos.length > 0) {
- await prisma.repo.updateMany({
- where: {
- id: {
- in: failedRepos,
+ for (let i = 0; i < failedRepos.length; i += 100) {
+ await prisma.repo.updateMany({
+ where: {
+ id: {
+ in: failedRepos.slice(i, i + 100),
+ }
+ },
+ data: {
+ repoIndexingStatus: RepoIndexingStatus.NEW,
}
- },
- data: {
- repoIndexingStatus: RepoIndexingStatus.NEW,
- }
- })
+ })
+ }
}
}
}