Skip to content

Commit 48e8d7f

Browse files
committed
chore(mcl): rework compare_disko command
1 parent e9f297c commit 48e8d7f

File tree

2 files changed

+112
-45
lines changed

2 files changed

+112
-45
lines changed
Lines changed: 111 additions & 44 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,67 @@ 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){
37-
nix.eval!JSONValue("", [], "");
3879
const params = parseEnv!Params;
80+
3981
JSONValue configurations = nix.flake!JSONValue("", [], "show");
40-
string[] machines;
82+
83+
string[] machinesNew;
4184
foreach (string k, JSONValue v; configurations["nixosConfigurations"]){
4285
if (k[$-3 .. $] != "-vm" &&
4386
k != "gitlab-runner-container" &&
4487
k != "minimal-container" )
4588
{
46-
machines ~= k;
89+
machinesNew ~= k;
4790
}
4891
}
4992

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

@@ -55,56 +102,61 @@ export void compare_disko(string[] args){
55102
}
56103

57104
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);
62105

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

123+
string[] machines = uniq(machinesOld ~ machinesNew).array;
124+
66125
foreach (string m; machines){
67126
MachineChanges mc;
68127
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 == ""){
128+
if (m in machineOptionsRootOld.object && m in machineOptionsRootNew.object) {
129+
static foreach (setting; diskoOptions){
130+
if (machineOptionsRootOld[m][setting] == machineOptionsRootNew[m][setting]){
91131
infof("✔ NO DIFFERENCE IN %s", m);
92-
__traits(getMember, mc, setting) = true;
132+
__traits(getMember, mc, setting) = ChangeStatus.Unchanged;
93133
}
94134
else{
95135
infof("✖ DIFFERENCE IN %s", m);
96-
__traits(getMember, mc, setting) = false;
136+
__traits(getMember, mc, setting) = ChangeStatus.Changed;
97137
}
98138
}
99139
}
140+
else if (m in machineOptionsRootOld) {
141+
static foreach (setting; diskoOptions){
142+
infof("✖ MACHINE %s NO LONGER EXISTS", m);
143+
__traits(getMember, mc, setting) = ChangeStatus.Removed;
144+
}
145+
}
146+
else {
147+
static foreach (setting; diskoOptions){
148+
infof("✖ MACHINE %s IS NEW", m);
149+
__traits(getMember, mc, setting) = ChangeStatus.New;
150+
}
151+
}
100152
machineChanges ~= mc;
101153
}
102154
infof("------------------------------------------------------");
103-
if(machineDiffs.length == 0){
155+
if(machineChanges.length == 0){
104156
infof("✔✔✔ NO CONFIGS WITH DIFFS");
105157
}
106158
else{
107-
infof("✖✖✖ LIST OF CONFIGS WITH DIFFS");
159+
infof("✖✖✖ LIST OF CONFIGS WITH DIFFS"); //TODO: HANDLE DIFFERENCES ON OPTION LEVEL WITH DIFFERENTIATION OF STATUS
108160
foreach(mc; machineChanges){
109161
infof(mc.machine);
110162
}
@@ -113,7 +165,6 @@ export void compare_disko(string[] args){
113165

114166
// Cleanup
115167
execute(["git", "worktree" , "remove", worktreeBaseBranch, "--force"]);
116-
rmdirRecurse(freshTestsDir);
117168
}
118169

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

128184
data ~= "\n\n";
129185
foreach (string field; __traits(allMembers, MachineChanges)){
@@ -137,18 +193,29 @@ void create_comment(MachineChanges[] machineChanges){
137193

138194
foreach(mc; machineChanges){
139195
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)){
196+
static if (is(typeof(__traits(getMember, mc, field)) == string)){
144197
data~="| " ~ __traits(getMember, mc, field) ~ " ";
145198
}
146-
else{
147-
assert(0);
199+
else {
200+
data~="| " ~ statusToSymbol(__traits(getMember, mc, field)) ~ " ";
148201
}
149202
}
150203
data ~= "|\n";
151204
}
152205
}
153206
write("comment.md", data);
154207
}
208+
209+
210+
string constructCommandAttr(string flakePath, string[] machines){
211+
string attr = "let flake = (builtins.getFlake (builtins.toString " ~ flakePath ~ ")); in { ";
212+
foreach (m; machines){
213+
attr ~= m ~ " = { ";
214+
foreach (option; diskoOptions){
215+
attr ~= option ~ " = flake.nixosConfigurations." ~ m ~ ".config.disko.devices." ~ option ~ "; ";
216+
}
217+
attr ~= "}; ";
218+
}
219+
attr ~= "}";
220+
return attr;
221+
}

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)