Skip to content

Commit 87671cf

Browse files
committed
chore(mcl): rework compare_disko command
1 parent e9f297c commit 87671cf

File tree

2 files changed

+112
-44
lines changed

2 files changed

+112
-44
lines changed
Lines changed: 111 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
module mcl.commands.compare_disko;
22

3+
import core.cpuid : threadsPerCPU;
4+
35
import mcl.utils.env : optional, parseEnv;
46
import mcl.utils.nix : nix;
57
import mcl.utils.path : getTopLevel;
8+
import mcl.utils.path : rootDir, resultDir, gcRootsDir, createResultDirs;
69
import mcl.utils.process : execute;
10+
import mcl.utils.log : errorAndExit;
711

812
import std.typecons : Tuple, tuple;
913
import std.file : mkdirRecurse, exists, rmdirRecurse;
@@ -13,11 +17,14 @@ import std.json : JSONValue, parseJSON, JSONOptions;
1317
import std.file : write;
1418
import std.logger : tracef, errorf, infof;
1519
import std.process : environment;
16-
import mcl.utils.log : errorAndExit;
20+
import std.algorithm : map, filter, reduce, chunkBy, find, any, sort, startsWith, each, canFind, fold, uniq;
21+
import std.array;
22+
import std.meta : Filter;
1723

1824
struct Params
1925
{
2026
@optional() string baseBranch;
27+
@optional() int maxWorkers;
2128

2229
void setup(){
2330
if (baseBranch == null){
@@ -26,27 +33,68 @@ struct Params
2633
}
2734
}
2835

36+
enum ChangeStatus{
37+
Unchanged,
38+
Changed,
39+
Removed,
40+
New
41+
}
42+
2943
struct MachineChanges{
3044
string machine;
31-
bool _config;
32-
bool _create;
45+
ChangeStatus _config;
46+
ChangeStatus _create;
47+
}
48+
49+
template FilterFields(S) {
50+
template isNotExcluded(string memberName) {
51+
enum isNotExcluded = (memberName != "machine");
52+
}
53+
alias AllMembers = __traits(allMembers, S);
54+
alias FilteredMembers = Filter!(isNotExcluded, AllMembers);
55+
enum FieldNamesExcept = [ FilteredMembers ];
56+
}
57+
58+
enum string[] diskoOptions = FilterFields!(MachineChanges).FieldNamesExcept;
59+
60+
string statusToSymbol(ChangeStatus s) {
61+
final switch(s){
62+
case ChangeStatus.Unchanged:
63+
return "🟩";
64+
break;
65+
case ChangeStatus.Changed:
66+
return "";
67+
break;
68+
case ChangeStatus.Removed:
69+
return "🗑";
70+
break;
71+
case ChangeStatus.New:
72+
return "🧩";
73+
break;
74+
}
3375
}
3476

3577
// TODO: handle case where a machine is missing from one branch
3678
export void compare_disko(string[] args){
3779
nix.eval!JSONValue("", [], "");
3880
const params = parseEnv!Params;
81+
3982
JSONValue configurations = nix.flake!JSONValue("", [], "show");
40-
string[] machines;
83+
84+
string[] machinesNew;
4185
foreach (string k, JSONValue v; configurations["nixosConfigurations"]){
4286
if (k[$-3 .. $] != "-vm" &&
4387
k != "gitlab-runner-container" &&
4488
k != "minimal-container" )
4589
{
46-
machines ~= k;
90+
machinesNew ~= k;
4791
}
4892
}
4993

94+
auto attr = constructCommandAttr("./.", machinesNew);
95+
auto machineOptionsRootNew = nix.eval!JSONValue("", ["--impure", "--expr", attr]);
96+
97+
5098
string gitRoot = getTopLevel();
5199
string worktreeBaseBranch = gitRoot~"-"~params.baseBranch;
52100

@@ -55,56 +103,61 @@ export void compare_disko(string[] args){
55103
}
56104

57105
execute(["git", "worktree" , "add", worktreeBaseBranch, params.baseBranch]);
58-
string freshTestsDir = gitRoot~"/disko-tests";
59-
string staleTestsDir = worktreeBaseBranch~"/disko-tests";
60-
mkdirRecurse(staleTestsDir);
61-
mkdirRecurse(freshTestsDir);
62106

63-
Tuple!(string , string )[] machineDiffs;
107+
configurations = nix.flake!JSONValue(worktreeBaseBranch, [], "show");
108+
109+
string[] machinesOld;
110+
foreach (string k, JSONValue v; configurations["nixosConfigurations"]){
111+
if (k[$-3 .. $] != "-vm" &&
112+
k != "gitlab-runner-container" &&
113+
k != "minimal-container" )
114+
{
115+
machinesOld ~= k;
116+
}
117+
}
118+
119+
attr = constructCommandAttr(worktreeBaseBranch, machinesOld);
120+
auto machineOptionsRootOld = nix.eval!JSONValue("", ["--impure", "--expr", attr]);
121+
64122
MachineChanges[] machineChanges;
65123

124+
string[] machines = uniq(machinesOld ~ machinesNew).array;
125+
66126
foreach (string m; machines){
67127
MachineChanges mc;
68128
mc.machine = m;
69-
foreach (setting; __traits(allMembers, MachineChanges)){
70-
static if (is(typeof(__traits(getMember, mc, setting)) == bool)){
71-
string new_setting =
72-
nix.eval(gitRoot~"#nixosConfigurations."~m~".config.disko.devices."~setting, [
73-
"--option", "warn-dirty", "false",
74-
"--accept-flake-config"]);
75-
tracef("CREATING %s_%s_new FILE", m, setting);
76-
write(freshTestsDir~"/"~m~"_"~setting~"_new", new_setting);
77-
string old_setting =
78-
nix.eval(worktreeBaseBranch~"#nixosConfigurations."~m~".config.disko.devices."~setting, [
79-
"--option", "warn-dirty", "false",
80-
"--accept-flake-config"]);
81-
tracef("CREATING %s_%s_old FILE", m, setting);
82-
write(staleTestsDir~"/"~m~"_"~setting~"_old", old_setting);
83-
84-
85-
string diff = execute([
86-
"git", "--no-pager", "diff", "--no-index",
87-
staleTestsDir~"/"~m~"_"~setting~"_old",
88-
freshTestsDir~"/"~m~"_"~setting~"_new"]);
89-
90-
if (diff == ""){
129+
if (m in machineOptionsRootOld.object && m in machineOptionsRootNew.object) {
130+
static foreach (setting; diskoOptions){
131+
if (machineOptionsRootOld[m][setting] == machineOptionsRootNew[m][setting]){
91132
infof("✔ NO DIFFERENCE IN %s", m);
92-
__traits(getMember, mc, setting) = true;
133+
__traits(getMember, mc, setting) = ChangeStatus.Unchanged;
93134
}
94135
else{
95136
infof("✖ DIFFERENCE IN %s", m);
96-
__traits(getMember, mc, setting) = false;
137+
__traits(getMember, mc, setting) = ChangeStatus.Changed;
97138
}
98139
}
99140
}
141+
else if (m in machineOptionsRootOld) {
142+
static foreach (setting; diskoOptions){
143+
infof("✖ MACHINE %s NO LONGER EXISTS", m);
144+
__traits(getMember, mc, setting) = ChangeStatus.Removed;
145+
}
146+
}
147+
else {
148+
static foreach (setting; diskoOptions){
149+
infof("✖ MACHINE %s IS NEW", m);
150+
__traits(getMember, mc, setting) = ChangeStatus.New;
151+
}
152+
}
100153
machineChanges ~= mc;
101154
}
102155
infof("------------------------------------------------------");
103-
if(machineDiffs.length == 0){
156+
if(machineChanges.length == 0){
104157
infof("✔✔✔ NO CONFIGS WITH DIFFS");
105158
}
106159
else{
107-
infof("✖✖✖ LIST OF CONFIGS WITH DIFFS");
160+
infof("✖✖✖ LIST OF CONFIGS WITH DIFFS"); //TODO: HANDLE DIFFERENCES ON OPTION LEVEL WITH DIFFERENTIATION OF STATUS
108161
foreach(mc; machineChanges){
109162
infof(mc.machine);
110163
}
@@ -113,7 +166,6 @@ export void compare_disko(string[] args){
113166

114167
// Cleanup
115168
execute(["git", "worktree" , "remove", worktreeBaseBranch, "--force"]);
116-
rmdirRecurse(freshTestsDir);
117169
}
118170

119171
void create_comment(MachineChanges[] machineChanges){
@@ -124,6 +176,11 @@ void create_comment(MachineChanges[] machineChanges){
124176
else{
125177
// TODO: Change the generation of the table to have as many collumns as fields in MachineChanges at compile time
126178
data ~= "\n\nBellow you will find a summary of machines and whether their disko attributes have differences.";
179+
data ~= "\n\n**Legend:**";
180+
data ~= "\n🟩 = No changes";
181+
data ~= "\n⚡ = Something is different";
182+
data ~= "\n🗑 = Has been removed";
183+
data ~= "\n🧩 = Has been added";
127184

128185
data ~= "\n\n";
129186
foreach (string field; __traits(allMembers, MachineChanges)){
@@ -137,18 +194,29 @@ void create_comment(MachineChanges[] machineChanges){
137194

138195
foreach(mc; machineChanges){
139196
foreach (string field; __traits(allMembers, MachineChanges)){
140-
static if (is(typeof(__traits(getMember, mc, field)) == bool)){
141-
data~="| " ~ (__traits(getMember, mc, field) ? "🟩" : "⚠️") ~ " ";
142-
}
143-
else static if (is(typeof(__traits(getMember, mc, field)) == string)){
197+
static if (is(typeof(__traits(getMember, mc, field)) == string)){
144198
data~="| " ~ __traits(getMember, mc, field) ~ " ";
145199
}
146-
else{
147-
assert(0);
200+
else {
201+
data~="| " ~ statusToSymbol(__traits(getMember, mc, field)) ~ " ";
148202
}
149203
}
150204
data ~= "|\n";
151205
}
152206
}
153207
write("comment.md", data);
154208
}
209+
210+
211+
string constructCommandAttr(string flakePath, string[] machines){
212+
string attr = "let flake = (builtins.getFlake (builtins.toString " ~ flakePath ~ ")); in { ";
213+
foreach (m; machines){
214+
attr ~= m ~ " = { ";
215+
foreach (option; diskoOptions){
216+
attr ~= option ~ " = flake.nixosConfigurations." ~ m ~ ".config.disko.devices." ~ option ~ "; ";
217+
}
218+
attr ~= "}; ";
219+
}
220+
attr ~= "}";
221+
return attr;
222+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ struct NixCommand
8080
args = ["--json"] ~ args;
8181

8282
auto command = [
83-
"nix", "--experimental-features", "nix-command flakes",
83+
"nix", "--experimental-features", "nix-command flakes pipe-operators",
8484
commandName
8585
] ~ (subcommand == "" ? [] : [ subcommand ]) ~ args ~ path;
8686

0 commit comments

Comments
 (0)