Repository: metacraft-labs/nixos-modules
Pull request #104: feat(mcl/host_info): Upload results to Coda
Author: login: monyarm
Created: 2024-06-20T09:03:58Z | Merged: 2025-04-10T10:51:27Z



Diff:
diff --git a/flake.lock b/flake.lock
index ace8a3f2..9925fcb5 100644
--- a/flake.lock
+++ b/flake.lock
@@ -203,17 +203,17 @@
         "nixpkgs": "nixpkgs_2"
       },
       "locked": {
-        "lastModified": 1714055636,
-        "narHash": "sha256-8LCyIPAK4/4ge03ohCIWpoJrMGgoCaOriALzt7gPxHE=",
+        "lastModified": 1744030092,
+        "narHash": "sha256-EwKNnBBTpjgNH20nfhJocGh6rZQJji0/8i1qsaviiGQ=",
         "owner": "PetarKirov",
         "repo": "dlang.nix",
-        "rev": "dab4c199ad644dc23b0b9481e2e5a063e9492b84",
+        "rev": "ab6b8f72d7b56bfd0b12e189fc4dfaed9d46de76",
         "type": "github"
       },
       "original": {
         "owner": "PetarKirov",
+        "ref": "feat/build-dub-package",
         "repo": "dlang.nix",
-        "rev": "dab4c199ad644dc23b0b9481e2e5a063e9492b84",
         "type": "github"
       }
     },
diff --git a/flake.nix b/flake.nix
index cb69384f..79392ba7 100644
--- a/flake.nix
+++ b/flake.nix
@@ -197,7 +197,7 @@
     };
 
     dlang-nix = {
-      url = "github:PetarKirov/dlang.nix?branch=feat/build-dub-package&rev=dab4c199ad644dc23b0b9481e2e5a063e9492b84";
+      url = "github:PetarKirov/dlang.nix/feat/build-dub-package";
       inputs = {
         flake-compat.follows = "flake-compat";
         flake-parts.follows = "flake-parts";
diff --git a/packages/default.nix b/packages/default.nix
index 02d9f68d..1b0bb64c 100644
--- a/packages/default.nix
+++ b/packages/default.nix
@@ -65,10 +65,10 @@
         // optionalAttrs isLinux {
           folder-size-metrics = pkgs.callPackage ./folder-size-metrics { };
         }
-        // optionalAttrs (system == "x86_64-linux") {
+        // optionalAttrs (system == "x86_64-linux") rec {
           mcl = pkgs.callPackage ./mcl {
             buildDubPackage = inputs'.dlang-nix.legacyPackages.buildDubPackage.override {
-              ldc = inputs'.dlang-nix.packages."ldc-binary-1_34_0";
+              dCompiler = inputs'.dlang-nix.packages."ldc-binary-1_38_0";
             };
             inherit (legacyPackages.inputs.nixpkgs) cachix nix nix-eval-jobs;
           };
diff --git a/packages/mcl/default.nix b/packages/mcl/default.nix
index f99c12b9..998abcfd 100644
--- a/packages/mcl/default.nix
+++ b/packages/mcl/default.nix
@@ -7,7 +7,9 @@
   ...
 }:
 let
+  inherit (pkgs.hostPlatform) isLinux isx86;
   deps =
+    with pkgs;
     [
       nix
       nix-eval-jobs
@@ -15,18 +17,26 @@ let
     ++ (with pkgs; [
       gitMinimal
       gawk
-      dmidecode
       jc
       edid-decode
       coreutils-full
       util-linux
       xorg.xrandr
-      glxinfo
+      perl
+      alejandra
+      openssh
       cachix
-    ]);
+      curl
+    ])
+    ++ lib.optionals (isLinux && isx86) [
+      dmidecode
+      glxinfo
+      nixos-install-tools
+      systemd
+    ];
   excludedTests = (
     lib.concatStringsSep "|" [
-      "(nix\\.(build|run))"
+      "(nix\\.(build|run|eval))"
       "fetchJson|(coda\.)"
       "checkPackage"
       "generateShardMatrix"
@@ -52,17 +62,15 @@ buildDubPackage rec {
   nativeBuildInputs = [ pkgs.makeWrapper ] ++ deps;
 
   postFixup = ''
-    wrapProgram $out/bin/${pname} --set PATH "${lib.makeBinPath deps}"
+    wrapProgram $out/bin/${pname} --set PATH "${lib.makeBinPath deps}" --set LD_LIBRARY_PATH "${lib.makeLibraryPath deps}"
   '';
 
   dubBuildFlags = [
-    "--compiler=dmd"
     "-b"
     "debug"
   ];
 
   dubTestFlags = [
-    "--compiler=dmd"
     "--"
     "-e"
     excludedTests
diff --git a/packages/mcl/src/src/mcl/commands/host_info.d b/packages/mcl/src/src/mcl/commands/host_info.d
index 1e45ff40..4fc139a0 100644
--- a/packages/mcl/src/src/mcl/commands/host_info.d
+++ b/packages/mcl/src/src/mcl/commands/host_info.d
@@ -9,7 +9,7 @@ import std.stdio : writeln;
 import std.conv : to;
 import std.string : strip, indexOf, isNumeric;
 import std.array : split, join, array, replace;
-import std.algorithm : map, filter, startsWith, joiner, any, sum;
+import std.algorithm : map, filter, startsWith, joiner, any, sum, find;
 import std.file : exists, write, readText, readLink, dirEntries, SpanMode;
 import std.path : baseName;
 import std.json;
@@ -22,6 +22,7 @@ import mcl.utils.process : execute, isRoot;
 import mcl.utils.number : humanReadableSize;
 import mcl.utils.array : uniqIfSame;
 import mcl.utils.nix : Literal;
+import mcl.utils.coda;
 
 // enum InfoFormat
 // {
@@ -34,10 +35,12 @@ struct Params
 {
     // @optional()
     // InfoFormat format = InfoFormat.JSON;
+    @optional() string codaApiToken;
     void setup()
     {
     }
 }
+Params params;
 
 string[string] cpuinfo;
 
@@ -64,7 +67,7 @@ string[string] getProcInfo(string fileOrData, bool file = true)
 
 export void host_info()
 {
-    const params = parseEnv!Params;
+    params = parseEnv!Params;
 
     Info info = getInfo();
 
@@ -79,6 +82,8 @@ Info getInfo()
     meminfo = getProcInfo("/proc/meminfo");
 
     Info info;
+    info.softwareInfo.hostid = execute("hostid", false);
+    info.softwareInfo.hostname = execute("cat /etc/hostname", false);
     info.softwareInfo.operatingSystemInfo = getOperatingSystemInfo();
     info.softwareInfo.opensshInfo = getOpenSSHInfo();
     info.softwareInfo.machineConfigInfo = getMachineConfigInfo();
@@ -90,6 +95,86 @@ Info getInfo()
     info.hardwareInfo.displayInfo = getDisplayInfo();
     info.hardwareInfo.graphicsProcessorInfo = getGraphicsProcessorInfo();
 
+    if (params.codaApiToken) {
+        auto docId = "0rz18jyJ1M";
+        auto hostTableId = "grid-b3MAjem325";
+        auto cpuTableId = "grid-mCI3x3nEIE";
+        auto memoryTableId = "grid-o7o2PeB4rz";
+        auto motherboardTableId = "grid-270PlzmA8K";
+        auto gpuTableId = "grid-ho6EPztvni";
+        auto storageTableId = "grid-JvXFbttMNz";
+        auto osTableId = "grid-ora7n98-ls";
+        auto coda = CodaApiClient(params.codaApiToken);
+
+        auto hostValues = RowValues([
+            CodaCell("Host Name", info.softwareInfo.hostname),
+            CodaCell("Host ID", info.softwareInfo.hostid),
+            CodaCell("OpenSSH Public Key", info.softwareInfo.opensshInfo.publicKey),
+            CodaCell("JSON", info.toJSON(true).toPrettyString(JSONOptions.doNotEscapeSlashes))
+        ]);
+
+        coda.updateOrInsertRow(docId, hostTableId, hostValues);
+
+        auto cpuValues = RowValues([
+            CodaCell("Host Name", info.softwareInfo.hostname),
+            CodaCell("Vendor", info.hardwareInfo.processorInfo.vendor),
+            CodaCell("Model", info.hardwareInfo.processorInfo.model),
+            CodaCell("Architecture", info.hardwareInfo.processorInfo.architectureInfo.architecture),
+            CodaCell("Flags", info.hardwareInfo.processorInfo.architectureInfo.flags),
+        ]);
+        coda.updateOrInsertRow(docId, cpuTableId, cpuValues);
+
+        auto memoryValues = RowValues([
+            CodaCell("Host Name", info.softwareInfo.hostname),
+            CodaCell("Vendor", info.hardwareInfo.memoryInfo.vendor),
+            CodaCell("Part Number", info.hardwareInfo.memoryInfo.partNumber),
+            CodaCell("Serial", info.hardwareInfo.memoryInfo.serial),
+            CodaCell("Generation", info.hardwareInfo.memoryInfo.type),
+            CodaCell("Slots", info.hardwareInfo.memoryInfo.slots == 0 ? "Soldered" : info.hardwareInfo.memoryInfo.count.to!string ~ "/" ~ info.hardwareInfo.memoryInfo.slots.to!string),
+            CodaCell("Total", info.hardwareInfo.memoryInfo.total),
+            CodaCell("Speed", info.hardwareInfo.memoryInfo.speed),
+        ]);
+        coda.updateOrInsertRow(docId, memoryTableId, memoryValues);
+
+        auto motherboardValues = RowValues([
+            CodaCell("Host Name", info.softwareInfo.hostname),
+            CodaCell("Vendor", info.hardwareInfo.motherboardInfo.vendor),
+            CodaCell("Model", info.hardwareInfo.motherboardInfo.model),
+            CodaCell("Revision", info.hardwareInfo.motherboardInfo.version_),
+            CodaCell("Serial", info.hardwareInfo.motherboardInfo.serial),
+            CodaCell("BIOS Vendor", info.hardwareInfo.motherboardInfo.biosInfo.vendor),
+            CodaCell("BIOS Version", info.hardwareInfo.motherboardInfo.biosInfo.version_),
+            CodaCell("BIOS Release", info.hardwareInfo.motherboardInfo.biosInfo.release),
+            CodaCell("BIOS Date", info.hardwareInfo.motherboardInfo.biosInfo.date)
+        ]);
+        coda.updateOrInsertRow(docId, motherboardTableId, motherboardValues);
+
+        auto gpuValues = RowValues([
+            CodaCell("Host Name", info.softwareInfo.hostname),
+            CodaCell("Vendor", info.hardwareInfo.graphicsProcessorInfo.vendor),
+            CodaCell("Model", info.hardwareInfo.graphicsProcessorInfo.model),
+            CodaCell("VRam", info.hardwareInfo.graphicsProcessorInfo.vram)
+        ]);
+        coda.updateOrInsertRow(docId, gpuTableId, gpuValues);
+
+        auto osValues = RowValues([
+            CodaCell("Host Name", info.softwareInfo.hostname),
+            CodaCell("Distribution", info.softwareInfo.operatingSystemInfo.distribution),
+            CodaCell("Distribution Version", info.softwareInfo.operatingSystemInfo.distributionVersion),
+            CodaCell("Kernel", info.softwareInfo.operatingSystemInfo.kernel),
+            CodaCell("Kernel Version", info.softwareInfo.operatingSystemInfo.kernelVersion)
+        ]);
+        coda.updateOrInsertRow(docId, osTableId, osValues);
+
+        auto storageValues = RowValues([
+            CodaCell("Host Name", info.softwareInfo.hostname),
+            CodaCell("Count", info.hardwareInfo.storageInfo.devices.length.to!string),
+            CodaCell("Total", info.hardwareInfo.storageInfo.total),
+            CodaCell("JSON", info.hardwareInfo.storageInfo.toJSON(true).toPrettyString(JSONOptions.doNotEscapeSlashes))
+        ]);
+        coda.updateOrInsertRow(docId, storageTableId, storageValues);
+    }
+
     return info;
 }
 
@@ -101,6 +186,8 @@ struct Info
 
 struct SoftwareInfo
 {
+    string hostname;
+    string hostid;
     OperatingSystemInfo operatingSystemInfo;
     OpenSSHInfo opensshInfo;
     MachineConfigInfo machineConfigInfo;
diff --git a/packages/mcl/src/src/mcl/utils/coda.d b/packages/mcl/src/src/mcl/utils/coda.d
index b2809216..3970d9f2 100644
--- a/packages/mcl/src/src/mcl/utils/coda.d
+++ b/packages/mcl/src/src/mcl/utils/coda.d
@@ -31,7 +31,7 @@ import std.traits : isArray;
 import mcl.utils.json : toJSON, fromJSON;
 import std.process : environment;
 import std.stdio : writeln, writefln;
-import std.algorithm : map, filter;
+import std.algorithm : map, filter, find;
 import std.exception : assertThrown;
 import std.sumtype : SumType;
 import core.thread;
@@ -527,6 +527,16 @@ struct CodaApiClient
         coda.deleteRow("dEJJPwdxcw", tables[0].id, resp[0]);
     }
 
+    void updateOrInsertRow(string docId, string tableId, RowValues values) {
+        auto table = listRows(docId, tableId);
+        auto rows = find!(row => row.name == values.cells[0].value)(table);
+        if (rows.length > 0) {
+            updateRow(docId, tableId, rows[0].id, values);
+        }
+        else {
+            insertRows(docId, tableId, [values]);
+        }
+    }
     struct PushButtonResponse {
         string requestId;
         string rowId;
diff --git a/packages/mcl/src/src/mcl/utils/process.d b/packages/mcl/src/src/mcl/utils/process.d
index c0d55ad3..b7518ad0 100644
--- a/packages/mcl/src/src/mcl/utils/process.d
+++ b/packages/mcl/src/src/mcl/utils/process.d
@@ -24,6 +24,7 @@ T execute(T = string)(string[] args, bool printCommand = true, bool returnErr =
     import std.array : join;
     import std.algorithm : map;
     import std.conv : to;
+    import std.stdio : writeln;
 
     auto cmd = args.map!escapeShellCommand.join(" ");
 


Reviews:
user: login: EmilIvanichkovv
state: APPROVED
author_association: NONE
submitted_at: 2024-06-20T13:30:47Z
user: login: EmilIvanichkovv
state: COMMENTED
author_association: NONE
submitted_at: 2024-06-20T13:44:28Z
user: login: monyarm
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-06-20T13:47:19Z
user: login: monyarm
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-06-20T13:48:13Z
user: login: EmilIvanichkovv
state: COMMENTED
author_association: NONE
submitted_at: 2024-06-20T14:08:54Z
user: login: monyarm
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-06-20T14:15:05Z
user: login: PetarKirov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-06-20T14:30:29Z
user: login: monyarm
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-06-20T14:33:19Z
user: login: PetarKirov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-06-20T15:25:13Z
user: login: monyarm
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-06-20T15:28:29Z
user: login: PetarKirov
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-10T11:00:13Z

Review comments:
diff_hunk: @@ -90,9 +95,91 @@ Info getInfo()
     info.hardwareInfo.displayInfo = getDisplayInfo();
     info.hardwareInfo.graphicsProcessorInfo = getGraphicsProcessorInfo();
 
+    if (params.codaApiToken) {
+        auto docId = "0rz18jyJ1M";
+        auto hostTableId = "grid-b3MAjem325";
+        auto cpuTableId = "grid-mCI3x3nEIE";
+        auto memoryTableId = "grid-o7o2PeB4rz";
+        auto motherboardTableId = "grid-270PlzmA8K";
+        auto gpuTableId = "grid-ho6EPztvni";
+        auto storageTableId = "grid-JvXFbttMNz";
+        auto osTableId = "grid-ora7n98-ls";
+        auto coda = CodaApiClient(params.codaApiToken);
+
+        auto hostValues = RowValues([
+            CodaCell("Host Name", info.softwareInfo.hostname),
+            CodaCell("Host ID", info.softwareInfo.hostid),
+            CodaCell("OpenSSH Public Key", info.softwareInfo.opensshInfo.publicKey),
+            CodaCell("JSON", info.toJSON(true).toPrettyString(JSONOptions.doNotEscapeSlashes))
+        ]);
+
+        coda.updateOrInsertRow(docId, hostTableId, hostValues);
+
+        auto cpuValues = RowValues([
+            CodaCell("Host Name", info.softwareInfo.hostname),
+            CodaCell("Vendor", info.hardwareInfo.processorInfo.vendor),
+            CodaCell("Model", info.hardwareInfo.processorInfo.model),
+            CodaCell("Architecture", info.hardwareInfo.processorInfo.architectureInfo.architecture),
+            CodaCell("Flags", info.hardwareInfo.processorInfo.architectureInfo.flags),
+        ]);
+        coda.updateOrInsertRow(docId, cpuTableId, cpuValues);
+
+        auto memoryValues = RowValues([
+            CodaCell("Host Name", info.softwareInfo.hostname),
+            CodaCell("Vendor", info.hardwareInfo.memoryInfo.vendor),
+            CodaCell("Part Number", info.hardwareInfo.memoryInfo.partNumber),
+            CodaCell("Serial", info.hardwareInfo.memoryInfo.serial),
+            CodaCell("Generation", info.hardwareInfo.memoryInfo.type),
+            CodaCell("Slots", info.hardwareInfo.memoryInfo.slots == 0 ? "Soldered" : info.hardwareInfo.memoryInfo.count.to!string ~ "/" ~ info.hardwareInfo.memoryInfo.slots.to!string),
+            CodaCell("Total", info.hardwareInfo.memoryInfo.total),
+            CodaCell("Speed", info.hardwareInfo.memoryInfo.speed),
+        ]);
+        coda.updateOrInsertRow(docId, memoryTableId, memoryValues);
+
+        auto motherboardValues = RowValues([
+            CodaCell("Host Name", info.softwareInfo.hostname),
+            CodaCell("Vendor", info.hardwareInfo.motherboardInfo.vendor),
+            CodaCell("Model", info.hardwareInfo.motherboardInfo.model),
+            CodaCell("Revision", info.hardwareInfo.motherboardInfo.version_),
+            CodaCell("Serial", info.hardwareInfo.motherboardInfo.serial),
+            CodaCell("BIOS Vendor", info.hardwareInfo.motherboardInfo.biosInfo.vendor),
+            CodaCell("BIOS Version", info.hardwareInfo.motherboardInfo.biosInfo.version_),
+            CodaCell("BIOS Release", info.hardwareInfo.motherboardInfo.biosInfo.release),
+            CodaCell("BIOS Date", info.hardwareInfo.motherboardInfo.biosInfo.date)
+        ]);
+        coda.updateOrInsertRow(docId, motherboardTableId, motherboardValues);
+
+        auto gpuValues = RowValues([
+            CodaCell("Host Name", info.softwareInfo.hostname),
+            CodaCell("Vendor", info.hardwareInfo.graphicsProcessorInfo.vendor),
+            CodaCell("Model", info.hardwareInfo.graphicsProcessorInfo.model),
+            CodaCell("VRam", info.hardwareInfo.graphicsProcessorInfo.vram)
+        ]);
+        coda.updateOrInsertRow(docId, gpuTableId, gpuValues);
+
+        auto osValues = RowValues([
+            CodaCell("Host Name", info.softwareInfo.hostname),
+            CodaCell("Distribution", info.softwareInfo.operatingSystemInfo.distribution),
+            CodaCell("Distribution Version", info.softwareInfo.operatingSystemInfo.distributionVersion),
+            CodaCell("Kernel", info.softwareInfo.operatingSystemInfo.kernel),
+            CodaCell("Kernel Version", info.softwareInfo.operatingSystemInfo.kernelVersion)
+        ]);
+        coda.updateOrInsertRow(docId, osTableId, osValues);
+
+        auto storageValues = RowValues([
+            CodaCell("Host Name", info.softwareInfo.hostname),
+            CodaCell("Count", info.hardwareInfo.storageInfo.devices.length.to!string),
+            CodaCell("Total", info.hardwareInfo.storageInfo.total),
+            CodaCell("JSON", info.hardwareInfo.storageInfo.toJSON(true).toPrettyString(JSONOptions.doNotEscapeSlashes))
+        ]);
+        coda.updateOrInsertRow(docId, storageTableId, storageValues);
+    }
+
     return info;
 }
 
+
+
path: packages/mcl/src/src/mcl/commands/host_info.d
user: login: EmilIvanichkovv
body: Why 2 new lines here?
created_at: 2024-06-20T13:30:12Z
author_association: NONE
diff_hunk: @@ -527,6 +527,16 @@ struct CodaApiClient
         coda.deleteRow("dEJJPwdxcw", tables[0].id, resp[0]);
     }
 
+    void updateOrInsertRow(string docId, string tableId, RowValues values) {
+        auto table = listRows(docId, tableId);
+        auto rows = find!(row => row.name == values.cells[0].value)(table);
+        if (rows.length > 0) {
+            updateRow(docId, tableId, rows[0].id, values);
+        }
+        else {
+            insertRows(docId, tableId, [values]);
+        }
path: packages/mcl/src/src/mcl/utils/coda.d
user: login: EmilIvanichkovv
body: If there is conditional (ternary) operator in `d` here might be nice place to use it 
created_at: 2024-06-20T13:34:09Z
author_association: NONE
diff_hunk: @@ -90,9 +95,91 @@ Info getInfo()
     info.hardwareInfo.displayInfo = getDisplayInfo();
     info.hardwareInfo.graphicsProcessorInfo = getGraphicsProcessorInfo();
 
+    if (params.codaApiToken) {
+        auto docId = "0rz18jyJ1M";
+        auto hostTableId = "grid-b3MAjem325";
+        auto cpuTableId = "grid-mCI3x3nEIE";
+        auto memoryTableId = "grid-o7o2PeB4rz";
+        auto motherboardTableId = "grid-270PlzmA8K";
+        auto gpuTableId = "grid-ho6EPztvni";
+        auto storageTableId = "grid-JvXFbttMNz";
+        auto osTableId = "grid-ora7n98-ls";
path: packages/mcl/src/src/mcl/commands/host_info.d
user: login: EmilIvanichkovv
body: Are these specifics for the table you edit? Isn't it better to obtain them from some env var? 
created_at: 2024-06-20T13:39:12Z
author_association: NONE
diff_hunk: @@ -90,9 +95,91 @@ Info getInfo()
     info.hardwareInfo.displayInfo = getDisplayInfo();
     info.hardwareInfo.graphicsProcessorInfo = getGraphicsProcessorInfo();
 
+    if (params.codaApiToken) {
+        auto docId = "0rz18jyJ1M";
+        auto hostTableId = "grid-b3MAjem325";
+        auto cpuTableId = "grid-mCI3x3nEIE";
+        auto memoryTableId = "grid-o7o2PeB4rz";
+        auto motherboardTableId = "grid-270PlzmA8K";
+        auto gpuTableId = "grid-ho6EPztvni";
+        auto storageTableId = "grid-JvXFbttMNz";
+        auto osTableId = "grid-ora7n98-ls";
path: packages/mcl/src/src/mcl/commands/host_info.d
user: login: monyarm
body: These are specific tables in coda, in a specific document. So the ids need to be hardcoded.
created_at: 2024-06-20T13:47:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -90,9 +95,91 @@ Info getInfo()
     info.hardwareInfo.displayInfo = getDisplayInfo();
     info.hardwareInfo.graphicsProcessorInfo = getGraphicsProcessorInfo();
 
+    if (params.codaApiToken) {
+        auto docId = "0rz18jyJ1M";
+        auto hostTableId = "grid-b3MAjem325";
+        auto cpuTableId = "grid-mCI3x3nEIE";
+        auto memoryTableId = "grid-o7o2PeB4rz";
+        auto motherboardTableId = "grid-270PlzmA8K";
+        auto gpuTableId = "grid-ho6EPztvni";
+        auto storageTableId = "grid-JvXFbttMNz";
+        auto osTableId = "grid-ora7n98-ls";
+        auto coda = CodaApiClient(params.codaApiToken);
+
+        auto hostValues = RowValues([
+            CodaCell("Host Name", info.softwareInfo.hostname),
+            CodaCell("Host ID", info.softwareInfo.hostid),
+            CodaCell("OpenSSH Public Key", info.softwareInfo.opensshInfo.publicKey),
+            CodaCell("JSON", info.toJSON(true).toPrettyString(JSONOptions.doNotEscapeSlashes))
+        ]);
+
+        coda.updateOrInsertRow(docId, hostTableId, hostValues);
+
+        auto cpuValues = RowValues([
+            CodaCell("Host Name", info.softwareInfo.hostname),
+            CodaCell("Vendor", info.hardwareInfo.processorInfo.vendor),
+            CodaCell("Model", info.hardwareInfo.processorInfo.model),
+            CodaCell("Architecture", info.hardwareInfo.processorInfo.architectureInfo.architecture),
+            CodaCell("Flags", info.hardwareInfo.processorInfo.architectureInfo.flags),
+        ]);
+        coda.updateOrInsertRow(docId, cpuTableId, cpuValues);
+
+        auto memoryValues = RowValues([
+            CodaCell("Host Name", info.softwareInfo.hostname),
+            CodaCell("Vendor", info.hardwareInfo.memoryInfo.vendor),
+            CodaCell("Part Number", info.hardwareInfo.memoryInfo.partNumber),
+            CodaCell("Serial", info.hardwareInfo.memoryInfo.serial),
+            CodaCell("Generation", info.hardwareInfo.memoryInfo.type),
+            CodaCell("Slots", info.hardwareInfo.memoryInfo.slots == 0 ? "Soldered" : info.hardwareInfo.memoryInfo.count.to!string ~ "/" ~ info.hardwareInfo.memoryInfo.slots.to!string),
+            CodaCell("Total", info.hardwareInfo.memoryInfo.total),
+            CodaCell("Speed", info.hardwareInfo.memoryInfo.speed),
+        ]);
+        coda.updateOrInsertRow(docId, memoryTableId, memoryValues);
+
+        auto motherboardValues = RowValues([
+            CodaCell("Host Name", info.softwareInfo.hostname),
+            CodaCell("Vendor", info.hardwareInfo.motherboardInfo.vendor),
+            CodaCell("Model", info.hardwareInfo.motherboardInfo.model),
+            CodaCell("Revision", info.hardwareInfo.motherboardInfo.version_),
+            CodaCell("Serial", info.hardwareInfo.motherboardInfo.serial),
+            CodaCell("BIOS Vendor", info.hardwareInfo.motherboardInfo.biosInfo.vendor),
+            CodaCell("BIOS Version", info.hardwareInfo.motherboardInfo.biosInfo.version_),
+            CodaCell("BIOS Release", info.hardwareInfo.motherboardInfo.biosInfo.release),
+            CodaCell("BIOS Date", info.hardwareInfo.motherboardInfo.biosInfo.date)
+        ]);
+        coda.updateOrInsertRow(docId, motherboardTableId, motherboardValues);
+
+        auto gpuValues = RowValues([
+            CodaCell("Host Name", info.softwareInfo.hostname),
+            CodaCell("Vendor", info.hardwareInfo.graphicsProcessorInfo.vendor),
+            CodaCell("Model", info.hardwareInfo.graphicsProcessorInfo.model),
+            CodaCell("VRam", info.hardwareInfo.graphicsProcessorInfo.vram)
+        ]);
+        coda.updateOrInsertRow(docId, gpuTableId, gpuValues);
+
+        auto osValues = RowValues([
+            CodaCell("Host Name", info.softwareInfo.hostname),
+            CodaCell("Distribution", info.softwareInfo.operatingSystemInfo.distribution),
+            CodaCell("Distribution Version", info.softwareInfo.operatingSystemInfo.distributionVersion),
+            CodaCell("Kernel", info.softwareInfo.operatingSystemInfo.kernel),
+            CodaCell("Kernel Version", info.softwareInfo.operatingSystemInfo.kernelVersion)
+        ]);
+        coda.updateOrInsertRow(docId, osTableId, osValues);
+
+        auto storageValues = RowValues([
+            CodaCell("Host Name", info.softwareInfo.hostname),
+            CodaCell("Count", info.hardwareInfo.storageInfo.devices.length.to!string),
+            CodaCell("Total", info.hardwareInfo.storageInfo.total),
+            CodaCell("JSON", info.hardwareInfo.storageInfo.toJSON(true).toPrettyString(JSONOptions.doNotEscapeSlashes))
+        ]);
+        coda.updateOrInsertRow(docId, storageTableId, storageValues);
+    }
+
     return info;
 }
 
+
+
path: packages/mcl/src/src/mcl/commands/host_info.d
user: login: monyarm
body: A mistake on my part, one which will be resolved in my next commit.
created_at: 2024-06-20T13:48:13Z
author_association: CONTRIBUTOR
path: packages/mcl/src/src/mcl/commands/host_info.d
user: login: EmilIvanichkovv
body: This is just a suggestion and the provided code snippets are AI-generated, so do threat it as pseudo-code, You might use them as inspiration

I see a lot of repetition every time you create `RowValues`. Maybe you can create a generic function that walks through the `processorInfo`, `memoryInfo`, etc objects and automatically generates the CodaCell instances based on the object's fields. 
Something like this:
```d
import std.traits : FieldNameTuple, FieldTypeTuple;
import std.conv : to;

// Function to generate CodaCells
CodaCell[] generateCells(T)(T obj) {
    CodaCell[] cells;
    foreach (field; FieldNameTuple!T) {
        auto value = obj.tupleof[__traits(allMembers, T).indexOf(field)];
        cells ~= CodaCell(field, value.to!string);
    }
    return cells;
}
```

And then use it as follows:
```d
auto memoryValues = RowValues(generateCells(info.memoryInfo));
coda.updateOrInsertRow(docId, memoryTableId, memoryValues);
```

This function can be used with any struct that represents the information you want to convert into `CodaCell` instances, making it a flexible solution.
created_at: 2024-06-20T14:08:54Z
author_association: NONE
path: packages/mcl/src/src/mcl/commands/host_info.d
user: login: monyarm
body: It would branch too much, easily ballooning to 300 lines and negating any savings.
Some object values aren't used in the coda tables, others are partially used (the count or presense of the value), some coda columns are the whole json.
It would become overly convoluted to create a generateCells fucntion.
created_at: 2024-06-20T14:15:04Z
author_association: CONTRIBUTOR
diff_hunk: @@ -527,6 +527,16 @@ struct CodaApiClient
         coda.deleteRow("dEJJPwdxcw", tables[0].id, resp[0]);
     }
 
+    void updateOrInsertRow(string docId, string tableId, RowValues values) {
+        auto table = listRows(docId, tableId);
+        auto rows = find!(row => row.name == values.cells[0].value)(table);
+        if (rows.length > 0) {
+            updateRow(docId, tableId, rows[0].id, values);
+        }
+        else {
+            insertRows(docId, tableId, [values]);
+        }
path: packages/mcl/src/src/mcl/utils/coda.d
user: login: PetarKirov
body: @EmilIvanichkovv note that the arms of the if statement do not have identical arguments (update passes the row id to update), so I don't think it's doable, also perhaps it would be as clean:

```d
(rows.length > 0 ? updateRow : insertRows)(args)
```

created_at: 2024-06-20T14:30:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -527,6 +527,16 @@ struct CodaApiClient
         coda.deleteRow("dEJJPwdxcw", tables[0].id, resp[0]);
     }
 
+    void updateOrInsertRow(string docId, string tableId, RowValues values) {
+        auto table = listRows(docId, tableId);
+        auto rows = find!(row => row.name == values.cells[0].value)(table);
+        if (rows.length > 0) {
+            updateRow(docId, tableId, rows[0].id, values);
+        }
+        else {
+            insertRows(docId, tableId, [values]);
+        }
path: packages/mcl/src/src/mcl/utils/coda.d
user: login: monyarm
body: Except the args aren't the same. One requires the row id, the other doesn't.
Also, wait a minute, for me it's a helper function called updateOrInsertRow. I think I forgot to push.
created_at: 2024-06-20T14:33:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -90,9 +95,91 @@ Info getInfo()
     info.hardwareInfo.displayInfo = getDisplayInfo();
     info.hardwareInfo.graphicsProcessorInfo = getGraphicsProcessorInfo();
 
+    if (params.codaApiToken) {
+        auto docId = "0rz18jyJ1M";
+        auto hostTableId = "grid-b3MAjem325";
+        auto cpuTableId = "grid-mCI3x3nEIE";
+        auto memoryTableId = "grid-o7o2PeB4rz";
+        auto motherboardTableId = "grid-270PlzmA8K";
+        auto gpuTableId = "grid-ho6EPztvni";
+        auto storageTableId = "grid-JvXFbttMNz";
+        auto osTableId = "grid-ora7n98-ls";
path: packages/mcl/src/src/mcl/commands/host_info.d
user: login: PetarKirov
body: @monyarm I agree about these being doc specific, but they still shouldn't be hard coded. * the `docId` should be an env / cli parameter.
* ideally your code will list the tables in the document and locate the table ids by their name
  * if not easily doable, these should also be configurable via env or cli
created_at: 2024-06-20T15:25:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -90,9 +95,91 @@ Info getInfo()
     info.hardwareInfo.displayInfo = getDisplayInfo();
     info.hardwareInfo.graphicsProcessorInfo = getGraphicsProcessorInfo();
 
+    if (params.codaApiToken) {
+        auto docId = "0rz18jyJ1M";
+        auto hostTableId = "grid-b3MAjem325";
+        auto cpuTableId = "grid-mCI3x3nEIE";
+        auto memoryTableId = "grid-o7o2PeB4rz";
+        auto motherboardTableId = "grid-270PlzmA8K";
+        auto gpuTableId = "grid-ho6EPztvni";
+        auto storageTableId = "grid-JvXFbttMNz";
+        auto osTableId = "grid-ora7n98-ls";
path: packages/mcl/src/src/mcl/commands/host_info.d
user: login: monyarm
body: I dunno, given that we want people's hardware info to show up in coda after they run host_info, that seems like a bad idea. I mean, do you expect everyone to look up the coda document id, and add it as an env variable?
I can see wether using names instead of ids is supported for tables, it is for columns, but I'm not sure about the rest.
created_at: 2024-06-20T15:28:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -24,6 +24,7 @@ T execute(T = string)(string[] args, bool printCommand = true, bool returnErr =
     import std.array : join;
     import std.algorithm : map;
     import std.conv : to;
+    import std.stdio : writeln;
path: packages/mcl/src/src/mcl/utils/process.d
user: login: PetarKirov
body: Looks like this line is unused:

```suggestion
```
created_at: 2025-04-10T10:46:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -527,6 +527,16 @@ struct CodaApiClient
         coda.deleteRow("dEJJPwdxcw", tables[0].id, resp[0]);
     }
 
+    void updateOrInsertRow(string docId, string tableId, RowValues values) {
path: packages/mcl/src/src/mcl/utils/coda.d
user: login: PetarKirov
body: According to the Coda Api docs, we should be able to directly create an "Upsert" request, without first fetching the data:

https://coda.io/developers/apis/v1#tag/Rows/operation/upsertRows

Can you look into directly doing an upsert?

created_at: 2025-04-10T10:49:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -90,6 +95,86 @@ Info getInfo()
     info.hardwareInfo.displayInfo = getDisplayInfo();
     info.hardwareInfo.graphicsProcessorInfo = getGraphicsProcessorInfo();
 
+    if (params.codaApiToken) {
path: packages/mcl/src/src/mcl/commands/host_info.d
user: login: PetarKirov
body: Inform the user of this conditional behavior by logging:

* No Coda API token specified -> not uploading to Coda
* Coda API token specified -> uploading

(feed free to rephrase the messages)
created_at: 2025-04-10T10:51:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -34,10 +35,12 @@ struct Params
 {
     // @optional()
     // InfoFormat format = InfoFormat.JSON;
+    @optional() string codaApiToken;
     void setup()
     {
     }
 }
+Params params;
path: packages/mcl/src/src/mcl/commands/host_info.d
user: login: PetarKirov
body: Avoid global variables, as they make the control flow harder to reason about as the codebase evolves.
created_at: 2025-04-10T10:52:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -64,7 +67,7 @@ string[string] getProcInfo(string fileOrData, bool file = true)
 
 export void host_info()
 {
-    const params = parseEnv!Params;
+    params = parseEnv!Params;
path: packages/mcl/src/src/mcl/commands/host_info.d
user: login: PetarKirov
body: This should remain a local `const` variable. If necessary add parameters to the functions that need to access it.
created_at: 2025-04-10T10:54:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -65,10 +65,10 @@
         // optionalAttrs isLinux {
           folder-size-metrics = pkgs.callPackage ./folder-size-metrics { };
         }
-        // optionalAttrs (system == "x86_64-linux") {
+        // optionalAttrs (system == "x86_64-linux") rec {
path: packages/default.nix
user: login: PetarKirov
body: I don't think we need to make this attrset recursive:

```suggestion
        // optionalAttrs (system == "x86_64-linux") {
```
created_at: 2025-04-10T10:56:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -22,6 +22,7 @@ import mcl.utils.process : execute, isRoot;
 import mcl.utils.number : humanReadableSize;
 import mcl.utils.array : uniqIfSame;
 import mcl.utils.nix : Literal;
+import mcl.utils.coda;
path: packages/mcl/src/src/mcl/commands/host_info.d
user: login: PetarKirov
body: Use selective imports:

```suggestion
import mcl.utils.coda : CodaApiClient, RowValues, CodaCell;
```
created_at: 2025-04-10T10:59:10Z
author_association: CONTRIBUTOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-04-10T10:51:21Z
body: Thanks for your Pull Request!

Below you will find a summary of the cachix status of each package, for each supported platform.

| package | `x86_64-linux` | `x86_64-darwin` | `aarch64-darwin` |
| ------- | -------------- | --------------- | ---------------- |
| cachix | ❌ build failed | ❌ build failed | ❌ build failed |
| dcd | [✅ cached](https://mcl-public-cache.cachix.org/iib1qip90s6rnbh8qsnns78ljyj7ip8g.narinfo) | 🚫 not supported | 🚫 not supported |
| dmd | [✅ cached](https://mcl-public-cache.cachix.org/aybnywh0xr6wk48qw6921q7zq6dvfs1a.narinfo) | 🚫 not supported | 🚫 not supported |
| dscanner | [✅ cached](https://mcl-public-cache.cachix.org/zkwrasc6q3lbscisg22i2rwn6iljzh70.narinfo) | 🚫 not supported | 🚫 not supported |
| dub | [✅ cached](https://mcl-public-cache.cachix.org/fk3yaq1jmddj0wfb48fndiqpcigpsdkg.narinfo) | [✅ cached](https://mcl-public-cache.cachix.org/ybdi00r71hj18z0gnf37kl7qqxmpdksw.narinfo) | [✅ cached](https://mcl-public-cache.cachix.org/vgyi55adlh16ib5kc4p5v9ax7w73mf8p.narinfo) |
| folder-size-metrics | [✅ cached](https://mcl-public-cache.cachix.org/64gcxxvs2miawg65szh9gygaygcihls7.narinfo) | 🚫 not supported | 🚫 not supported |
| foundry | [✅ cached](https://mcl-public-cache.cachix.org/rfgpfqz6ahzv9f4ws6m37lwpx0gsk2lk.narinfo) | [✅ cached](https://mcl-public-cache.cachix.org/8kmkyhg63kkwdjvx028dh43xcyybzlsg.narinfo) | [✅ cached](https://mcl-public-cache.cachix.org/h6pyg2hsj557yyyx9p9xygki3pizklid.narinfo) |
| geth | [✅ cached](https://mcl-public-cache.cachix.org/jl3w4acdld1rxm4ymlal5s02r55r2jzp.narinfo) | 🚫 not supported | [✅ cached](https://mcl-public-cache.cachix.org/mj1qp82kb4cr9scql38s9bx5vmlndpa3.narinfo) |
| ldc | [✅ cached](https://mcl-public-cache.cachix.org/a79blga8fdvvy9zwdc378wrpwlc835x4.narinfo) | 🚫 not supported | 🚫 not supported |
| lido-withdrawals-automation | [✅ cached](https://mcl-public-cache.cachix.org/fnpndbgz8lqs14ffbp6ql7aj4rmzizwn.narinfo) | [✅ cached](https://mcl-public-cache.cachix.org/gb4z4i6ls36asg274p35ar0vsnzdym6y.narinfo) | [✅ cached](https://mcl-public-cache.cachix.org/wg284y7vh97mpyj41zp93var1zlsbzwy.narinfo) |
| mcl | [✅ cached](https://mcl-public-cache.cachix.org/9r6pmck1qyr21wakggg410qz6fjycwj0.narinfo) | 🚫 not supported | 🚫 not supported |
| mev-boost | [✅ cached](https://mcl-public-cache.cachix.org/qgxqyn3a4plzzb2nywnldg6xjwlk93rw.narinfo) | 🚫 not supported | 🚫 not supported |
| nethermind | [✅ cached](https://mcl-public-cache.cachix.org/wf2ivqa4g915yxsi01yzkjsrvxlr9ch1.narinfo) | 🚫 not supported | 🚫 not supported |
| nimbus | [✅ cached](https://mcl-public-cache.cachix.org/pznw5pcz828jfqwqjxf6lp0fffp074ym.narinfo) | 🚫 not supported | 🚫 not supported |
| nix | ❌ build failed | ❌ build failed | ❌ build failed |
| nix-eval-jobs | ❌ build failed | ❌ build failed | ❌ build failed |
| nix-fast-build | ❌ build failed | ❌ build failed | ❌ build failed |
| pyroscope | [✅ cached](https://mcl-public-cache.cachix.org/6crsnga59j6h6anpb0x09sixsijxk84d.narinfo) | [✅ cached](https://mcl-public-cache.cachix.org/iylg450i17vnjs35znyhkrgbmsr6rp7k.narinfo) | [✅ cached](https://mcl-public-cache.cachix.org/qakpnysfznw09iiyprxnbr2gl1pxxvds.narinfo) |
| random-alerts | [✅ cached](https://mcl-public-cache.cachix.org/zsxqjmifgndqqkkdh82ydk93v3w55qcj.narinfo) | [✅ cached](https://mcl-public-cache.cachix.org/wra9pavz6llgrvi0s0q18hv86ykm6669.narinfo) | [✅ cached](https://mcl-public-cache.cachix.org/zgi24rs5xv13b0y391bnk2q7hwycp44z.narinfo) |
| rustToolchain | [✅ cached](https://mcl-public-cache.cachix.org/89hcmsk1kqi36x3nan645s4vhvlifcfb.narinfo) | [✅ cached](https://mcl-public-cache.cachix.org/i88n8iilnksz2dcwhvcsy9vjd92sm5f2.narinfo) | [✅ cached](https://mcl-public-cache.cachix.org/r821066vnfcix2mkv5plf0bmfwqznfy8.narinfo) |
| secret | [✅ cached](https://mcl-public-cache.cachix.org/hiqyd9lxvmf39hw2yq23s8zrw4jqwlh7.narinfo) | 🚫 not supported | [✅ cached](https://mcl-public-cache.cachix.org/aiqps1wmwb7kbvgw8qrd7jpj8zlja4sp.narinfo) |
| serve-d | [✅ cached](https://mcl-public-cache.cachix.org/0mhyvhpazsnnycz8mvq36949i5855icv.narinfo) | 🚫 not supported | 🚫 not supported |
| terraform | [✅ cached](https://mcl-public-cache.cachix.org/2m764ifysbhfkh096vnzfvhhcdzwmg5m.narinfo) | 🚫 not supported | 🚫 not supported |
| terranix | [✅ cached](https://mcl-public-cache.cachix.org/q59vsgldd9hihfsdad8ip06bq938x9sg.narinfo) | 🚫 not supported | 🚫 not supported |
| validator-ejector | [✅ cached](https://mcl-public-cache.cachix.org/gka4wpb4mkph4nmyhdq1ci9fg4nd1wbr.narinfo) | 🚫 not supported | 🚫 not supported |
| web3signer | [✅ cached](https://mcl-public-cache.cachix.org/fs1b9wzp171z6cln9fw53xrw9axmaabi.narinfo) | 🚫 not supported | 🚫 not supported |
<!-- Sticky Pull Request Comment -->
author_association: CONTRIBUTOR