Skip to content

Commit 3c79db7

Browse files
committed
feat(mcl): add compare_disko command
1 parent eef1b08 commit 3c79db7

File tree

4 files changed

+157
-2
lines changed

4 files changed

+157
-2
lines changed

packages/mcl/src/main.d

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ alias supportedCommands = imported!`std.traits`.AliasSeq!(
1818
cmds.ci,
1919
cmds.machine,
2020
cmds.config,
21+
cmds.compare_disko,
2122
);
2223

2324
int main(string[] args)
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
module mcl.commands.compare_disko;
2+
3+
import mcl.utils.env : optional, parseEnv;
4+
import mcl.utils.nix : nix;
5+
import mcl.utils.path : getTopLevel;
6+
import mcl.utils.process : execute;
7+
8+
import std.typecons : Tuple, tuple;
9+
import std.file : mkdirRecurse, exists, rmdirRecurse;
10+
import std.format : fmt = format;
11+
import std.stdio;
12+
import std.json : JSONValue, parseJSON, JSONOptions;
13+
import std.file : write;
14+
import std.logger : tracef, errorf, infof;
15+
import std.process : environment;
16+
import mcl.utils.log : errorAndExit;
17+
18+
struct Params
19+
{
20+
@optional() string baseBranch;
21+
22+
void setup(){
23+
if (baseBranch == null){
24+
baseBranch = "main";
25+
}
26+
}
27+
}
28+
29+
struct MachineChanges{
30+
string machine;
31+
bool _config;
32+
bool _create;
33+
}
34+
35+
// TODO: handle case where a machine is missing from one branch
36+
export void compare_disko(string[] args){
37+
const params = parseEnv!Params;
38+
JSONValue configurations = nix.flake!JSONValue("", [], "show");
39+
string[] machines;
40+
foreach (string k, JSONValue v; configurations["nixosConfigurations"]){
41+
if (k[$-3 .. $] != "-vm" &&
42+
k != "gitlab-runner-container" &&
43+
k != "minimal-container" )
44+
{
45+
machines ~= k;
46+
}
47+
}
48+
49+
string gitRoot = getTopLevel();
50+
string worktreeBaseBranch = gitRoot~"-"~params.baseBranch;
51+
52+
if (execute("git rev-parse --abbrev-ref HEAD") == params.baseBranch){
53+
errorAndExit("Trying to compare branch "~params.baseBranch~" with itself. Quitting.");
54+
}
55+
56+
execute(["git", "worktree" , "add", worktreeBaseBranch, params.baseBranch]);
57+
string freshTestsDir = gitRoot~"/disko-tests";
58+
string staleTestsDir = worktreeBaseBranch~"/disko-tests";
59+
mkdirRecurse(staleTestsDir);
60+
mkdirRecurse(freshTestsDir);
61+
62+
Tuple!(string , string )[] machineDiffs;
63+
MachineChanges[] machineChanges;
64+
65+
foreach (string m; machines){
66+
MachineChanges mc;
67+
mc.machine = m;
68+
foreach (setting; __traits(allMembers, MachineChanges)){
69+
static if (is(typeof(__traits(getMember, mc, setting)) == bool)){
70+
string new_setting =
71+
nix.eval(gitRoot~"#nixosConfigurations."~m~".config.disko.devices."~setting, [
72+
"--option", "warn-dirty", "false",
73+
"--accept-flake-config"]);
74+
tracef("CREATING %s_%s_new FILE", m, setting);
75+
write(freshTestsDir~"/"~m~"_"~setting~"_new", new_setting);
76+
string old_setting =
77+
nix.eval(worktreeBaseBranch~"#nixosConfigurations."~m~".config.disko.devices."~setting, [
78+
"--option", "warn-dirty", "false",
79+
"--accept-flake-config"]);
80+
tracef("CREATING %s_%s_old FILE", m, setting);
81+
write(staleTestsDir~"/"~m~"_"~setting~"_old", old_setting);
82+
83+
84+
string diff = execute([
85+
"git", "--no-pager", "diff", "--no-index",
86+
staleTestsDir~"/"~m~"_"~setting~"_old",
87+
freshTestsDir~"/"~m~"_"~setting~"_new"]);
88+
89+
if (diff == ""){
90+
infof("✔ NO DIFFERENCE IN %s", m);
91+
__traits(getMember, mc, setting) = true;
92+
}
93+
else{
94+
infof("✖ DIFFERENCE IN %s", m);
95+
__traits(getMember, mc, setting) = false;
96+
}
97+
}
98+
}
99+
machineChanges ~= mc;
100+
}
101+
infof("------------------------------------------------------");
102+
if(machineDiffs.length == 0){
103+
infof("✔✔✔ NO CONFIGS WITH DIFFS");
104+
}
105+
else{
106+
infof("✖✖✖ LIST OF CONFIGS WITH DIFFS");
107+
foreach(mc; machineChanges){
108+
infof(mc.machine);
109+
}
110+
}
111+
create_comment(machineChanges);
112+
113+
// Cleanup
114+
execute(["git", "worktree" , "remove", worktreeBaseBranch, "--force"]);
115+
rmdirRecurse(freshTestsDir);
116+
}
117+
118+
void create_comment(MachineChanges[] machineChanges){
119+
string data = "Thanks for your Pull Request!";
120+
if (machineChanges.length == 0){
121+
data ~="\n\n✅ There have been no changes to disko configs";
122+
}
123+
else{
124+
// TODO: Change the generation of the table to have as many collumns as fields in MachineChanges at compile time
125+
data ~= "\n\nBellow you will find a summary of machines and whether their disko attributes have differences.";
126+
127+
data ~= "\n\n";
128+
foreach (string field; __traits(allMembers, MachineChanges)){
129+
data~="| " ~ field ~ " ";
130+
}
131+
data ~= "|\n";
132+
foreach (string field; __traits(allMembers, MachineChanges)){
133+
data~="| --- ";
134+
}
135+
data ~= "|\n";
136+
137+
foreach(mc; machineChanges){
138+
foreach (string field; __traits(allMembers, MachineChanges)){
139+
static if (is(typeof(__traits(getMember, mc, field)) == bool)){
140+
data~="| " ~ (__traits(getMember, mc, field) ? "🟩" : "⚠️") ~ " ";
141+
}
142+
else static if (is(typeof(__traits(getMember, mc, field)) == string)){
143+
data~="| " ~ __traits(getMember, mc, field) ~ " ";
144+
}
145+
else{
146+
assert(0);
147+
}
148+
}
149+
data ~= "|\n";
150+
}
151+
}
152+
write("comment.md", data);
153+
}

packages/mcl/src/src/mcl/commands/package.d

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ public import mcl.commands.ci : ci;
88
public import mcl.commands.host_info : host_info;
99
public import mcl.commands.machine : machine;
1010
public import mcl.commands.config : config;
11+
public import mcl.commands.compare_disko: compare_disko;

packages/mcl/src/src/mcl/utils/nix.d

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ struct NixCommand
6161

6262
template opDispatch(string commandName)
6363
{
64-
T opDispatch(T = string)(string path, string[] args = [])
64+
T opDispatch(T = string)(string path, string[] args = [], string subcommand = "")
6565
{
6666
import std.algorithm : canFind;
6767

@@ -81,7 +81,7 @@ struct NixCommand
8181

8282
auto command = [
8383
"nix", "--experimental-features", "nix-command flakes",
84-
commandName,
84+
commandName, subcommand,
8585
] ~ args ~ path;
8686

8787
auto output = command.execute(true).strip();

0 commit comments

Comments
 (0)