Files
server/extra/admin-api/update-deps.cs
T
2026-04-24 01:33:25 +02:00

190 lines
8.9 KiB
C#
Executable File

#! /usr/bin/env dotnet
#:property Nullable=enable
#:property PublishAOT=false
#:package ArcaneLibs@1.0.1-preview.2026*
using System.Diagnostics;
using ArcaneLibs;
using System.Text.Json;
#region Sync package versions for CDN worker
{
Console.WriteLine("==> Ensuring CDN worker dependencies are in sync...");
var origContent = await File.ReadAllTextAsync("Spacebar.Cdn.Worker/Spacebar.Cdn.Worker.Q16-HDRI.x86_64.csproj");
var depToReplace = "Magick.NET-Q16-HDRI-OpenMP-x64";
(string Project, string Dependency)[] replaceTargets = [
// ("Spacebar.Cdn.Worker/Spacebar.Cdn.Worker.Q16-HDRI.x86_64.csproj", "Magick.NET-Q16-HDRI-OpenMP-x64"), // source
("Spacebar.Cdn.Worker/Spacebar.Cdn.Worker.Q16.x86_64.csproj", "Magick.NET-Q16-OpenMP-x64"),
("Spacebar.Cdn.Worker/Spacebar.Cdn.Worker.Q8.x86_64.csproj", "Magick.NET-Q8-OpenMP-x64"),
("Spacebar.Cdn.Worker/Spacebar.Cdn.Worker.Q16-HDRI.aarch64.csproj", "Magick.NET-Q16-HDRI-OpenMP-arm64"),
("Spacebar.Cdn.Worker/Spacebar.Cdn.Worker.Q16.aarch64.csproj", "Magick.NET-Q16-OpenMP-arm64"),
("Spacebar.Cdn.Worker/Spacebar.Cdn.Worker.Q8.aarch64.csproj", "Magick.NET-Q8-OpenMP-arm64"),
("Spacebar.Cdn.Worker/Spacebar.Cdn.Worker.Q16-HDRI.AnyCPU.csproj", "Magick.NET-Q16-HDRI-AnyCPU"),
("Spacebar.Cdn.Worker/Spacebar.Cdn.Worker.Q16.AnyCPU.csproj", "Magick.NET-Q16-AnyCPU"),
("Spacebar.Cdn.Worker/Spacebar.Cdn.Worker.Q8.AnyCPU.csproj", "Magick.NET-Q8-AnyCPU"),
];
foreach (var target in replaceTargets) {
Console.WriteLine($" ==> {target.Project} -> {target.Dependency}");
await File.WriteAllTextAsync(target.Project, origContent.Replace(depToReplace, target.Dependency));
}
}
#endregion
Console.WriteLine("==> Getting outputs...");
var outNames = JsonSerializer
.Deserialize<string[]>(Util.GetCommandOutputSync("nix", $"eval --json .#packages.x86_64-linux --apply builtins.attrNames", silent: true, stderr: false))!
.Where(o => args.Length == 0 || args.Select(x => x.Replace('.', '-')).Any(o.Contains)).ToArray();
Console.WriteLine($"==> Updating dependencies for {outNames.Length} projects...");
#region Dependency resolution
var sortSw = Stopwatch.StartNew();
Console.WriteLine($"==> Sorting projects by dependencies...");
Console.WriteLine($" ==> Getting project references...");
(string Name, string[] References)[] byName = await Task.WhenAll(outNames
.Select(async x => {
await Task.Delay(Random.Shared.Next(outNames.Length));
var refJson = Util.GetCommandOutputSync("nix", $"eval --json .#packages.x86_64-linux.{x}.passthru.__sbDmProjectRefs.names", silent: true, stderr: false);
var references = string.IsNullOrEmpty(refJson) ? [] : JsonSerializer.Deserialize<string[]>(refJson)!.Select(x => x.Replace('.', '-')).ToArray();
if (references.Length > 0)
Console.WriteLine($" ==> Got {references.Length} project references for {x}");
// Console.WriteLine($" ==> Got {references.Length} project references for {x}: {string.Join(", ", references)}");
return (x, references);
}).ToList());
Console.WriteLine($" ==> Mapping to tree nodes...");
// create nodes first
var deps = byName.Select(x => new ProjectDependencyNode() {
Name = x.Name,
References = []
}).ToArray();
// actually fill in references
foreach (var d in deps) {
d.References = deps.Where(x => byName.First(bn => bn.Name == d.Name).References.Contains(x.Name)).ToArray();
Console.WriteLine($" ==> {d.Name} => {string.Join(", ", d.References.Select(x => x.GetShortName()))}");
}
Console.WriteLine($" ==> Sorting...");
deps = deps.OrderBy(x => x.GetDepth()).ThenBy(x => x.GetWeight()).ThenBy(x => x.Name).ToArray();
foreach (var d in deps) {
// just a nice thing when debugging
d.References = d.References.OrderBy(x => x.GetDepth()).ThenBy(x => x.GetWeight()).ThenBy(x => x.Name).ToArray();
Console.WriteLine($" ==> {d.GetNameWithWeights()} => {string.Join(", ", d.References.Select(x => $"{x.GetNameWithWeights(true)}"))}");
}
Console.WriteLine($" ==> Sorted dependency tree with {deps.Length} projects in {sortSw.Elapsed}");
#endregion
var maxNameLength = deps.Max(x => x.Name.Length);
foreach (var depthGroup in deps.GroupBy(x => x.GetDepth())) {
var tasks = depthGroup.Index().Select(indexedOutpEnt => Task.Run(async () => {
var (idx, outpEnt) = indexedOutpEnt;
var outp = outpEnt.Name;
var prefix = ConsoleUtils.ColoredString(
$"{outpEnt.GetDepth():00}.{idx:00}({outpEnt.GetWeight():000}) {outpEnt.Name.PadRight(maxNameLength)}>",
(byte)((outp.GetHashCode() >> 16) & 0xff),
(byte)((outp.GetHashCode() >> 8) & 0xff),
(byte)(outp.GetHashCode() & 0xff)
);
Console.WriteLine(prefix + ConsoleUtils.ColoredString($" ==> Updating {outp}...", 0x80, 0x80, 0xff));
var rootDir = outpEnt.GetRelativeRootDir();
var depsFile = outpEnt.GetDepsFile();
if (depsFile is null) {
Console.WriteLine(prefix + ConsoleUtils.ColoredString($" ==> No __nugetDeps attribute, skipping!", 0xff, 0x80, 0x80));
return;
}
Console.WriteLine(
prefix
+ ConsoleUtils.ColoredString($" ==> Got project root directory: ", 0x80, 0xff, 0xff)
+ ConsoleUtils.ColoredString($"{rootDir}", 0x80, 0xff, 0xff)
+ " - "
+ ConsoleUtils.ColoredString($"{depsFile.Value.StorePath.Replace(depsFile.Value.Name, "")}", 0x80, 0x80, 0xff)
+ ConsoleUtils.ColoredString($"{depsFile.Value.Name}", 0x80, 0xff, 0x80)
+ (File.Exists(depsFile.Value.LocalPath)
? ConsoleUtils.ColoredString($" (exists)", 0x80, 0xff, 0x80)
: ConsoleUtils.ColoredString($" (does not exist)", 0xf, 0x80, 0x80))
);
if (!File.Exists(depsFile.Value.LocalPath)) {
Console.WriteLine(prefix + ConsoleUtils.ColoredString($" ==> No NuGet deps file, skipping!", 0xff, 0x80, 0x80));
return;
}
Console.WriteLine(prefix + ConsoleUtils.ColoredString($" ==> Building fetch-deps script...", 0x80, 0xff, 0x80));
var fname = outpEnt.GetDepsUpdateScript();
Console.WriteLine(prefix + ConsoleUtils.ColoredString($" ==> Running fetch-deps script, writing into {depsFile.Value.LocalPath}...", 0x80, 0xff, 0x80));
RunCommandSync(fname, depsFile.Value.LocalPath);
var resolvedDeps = JsonSerializer.Deserialize<object[]>(await File.ReadAllTextAsync(depsFile.Value.LocalPath));
Console.WriteLine(prefix + ConsoleUtils.ColoredString($" ==> Locked {resolvedDeps.Length} dependencies!",
(byte)(resolvedDeps.Length == 0 ? 0xff : 0x80),
(byte)(resolvedDeps.Length == 0 ? 0x80 : 0xff),
0x80
));
RunCommandSync("nix", $"run nixpkgs#git -- add {depsFile.Value.LocalPath}");
})).ToList();
await Task.WhenAll(tasks);
}
static void RunCommandSync(string command, string args = "", bool silent = false) {
// Console.WriteLine($"Executing command (silent: {silent}): {command} {args}");
Util.RunCommandSync(command, args, silent);
}
public class ProjectDependencyNode : IComparable {
public required string Name;
public required ProjectDependencyNode[] References;
public int GetDepth() {
if (References.Length == 0) return 0;
return References.Max(x => x.GetDepth()) + 1;
}
public int GetWeight() {
if (References.Length == 0) return 1;
return References.Sum(x => x.GetWeight()) + 1;
}
public string GetNameWithWeights(bool compact = false) =>
compact
? $"{GetShortName()}:{GetDepth()}w{GetWeight()}"
: $"{Name} @ {GetDepth()} | {GetWeight()}";
public string GetShortName() => Name.Replace("Spacebar-", "Sb-")
.Replace("-Models-", "-Mdl-")
.Replace("-Interop-", "-Intp-")
.Replace("-Replication-", "-Rpl-")
.Replace("-Authentication-", "-Auth-");
public string GetRelativeRootDir() => JsonSerializer
.Deserialize<string>(Util.GetCommandOutputSync("nix", $"eval --json .#packages.x86_64-linux.{Name}.srcRoot", silent: true, stderr: false))!
.Split("/extra/admin-api/", 2)[1];
public string GetDepsUpdateScript() => Util.GetCommandOutputSync("nix", $"build .#{Name}.passthru.fetch-deps --no-link --print-out-paths", stderr: false);
public (string Name, string StorePath, string LocalPath)? GetDepsFile() {
var storePath = JsonSerializer.Deserialize<string>(Util.GetCommandOutputSync("nix", $"eval --json .#packages.x86_64-linux.{Name}.passthru.__nugetDeps", silent: true,
stderr: false));
return storePath == null
? null
: (
new FileInfo(storePath).Name,
storePath,
Path.Combine(GetRelativeRootDir(), new FileInfo(storePath).Name)
);
}
public int CompareTo(object? obj) => References.Any(x => x.Name == Name) ? 1 : 0;
}