From 3ab0efe3c1776d0191d3d2f322caefcd16cebd9e Mon Sep 17 00:00:00 2001 From: Siggi Simonarson Date: Thu, 19 Dec 2024 16:24:26 -0800 Subject: [PATCH] Add directory and symlink support to the compare UI (#8099) --- app/compare/compare_execution_log_files.tsx | 109 +++++++++++++++----- 1 file changed, 82 insertions(+), 27 deletions(-) diff --git a/app/compare/compare_execution_log_files.tsx b/app/compare/compare_execution_log_files.tsx index 53299a69599..00c23990119 100644 --- a/app/compare/compare_execution_log_files.tsx +++ b/app/compare/compare_execution_log_files.tsx @@ -7,7 +7,7 @@ import { tools } from "../../proto/spawn_ts_proto"; import format from "../format/format"; import error_service from "../errors/error_service"; import * as varint from "varint"; -import { ArrowRight, File } from "lucide-react"; +import { ArrowRight, File, FileSymlink } from "lucide-react"; import DigestComponent from "../components/digest/digest"; import Link from "../components/link/link"; import InvocationModel from "../invocation/invocation_model"; @@ -105,17 +105,17 @@ export default class CompareExecutionLogFilesComponent extends React.Component

No execution log actions for this invocation.; } - const filesA = this.state.logA?.filter((l) => l.type == "file") || []; - const filesB = this.state.logB?.filter((l) => l.type == "file") || []; + const filesA = this.state.logA?.filter((l) => l.type == "file" && l.file).map((l) => l.file!) || []; + const filesB = this.state.logB?.filter((l) => l.type == "file" && l.file).map((l) => l.file!) || []; - let newFiles: tools.protos.ExecLogEntry[] = []; - let unchangdFiles: tools.protos.ExecLogEntry[] = []; - let deletedFiles: tools.protos.ExecLogEntry[] = []; - let changedFiles: { a: tools.protos.ExecLogEntry; b: tools.protos.ExecLogEntry }[] = []; + const dirsA = this.state.logA?.filter((l) => l.type == "directory" && l.directory).map((l) => l.directory!) || []; + const dirsB = this.state.logB?.filter((l) => l.type == "directory" && l.directory).map((l) => l.directory!) || []; - let fileMap = new Map(); + for (let dir of dirsA) { + for (let file of dir.files) { + if (!file.path.startsWith(dir.path + "/")) { + file.path = dir.path + "/" + file.path; + } + filesA.push(file); + } + } + + for (let dir of dirsB) { + for (let file of dir.files) { + if (!file.path.startsWith(dir.path + "/")) { + file.path = dir.path + "/" + file.path; + } + filesB.push(file); + } + } + + const symlinksA = + this.state.logA + ?.filter((l) => l.type == "unresolvedSymlink" && l.unresolvedSymlink) + .map((l) => l.unresolvedSymlink!) || []; + + const symlinksB = + this.state.logB + ?.filter((l) => l.type == "unresolvedSymlink" && l.unresolvedSymlink) + .map((l) => l.unresolvedSymlink!) || []; + + for (let symlink of symlinksA) { + filesA.push( + new tools.protos.ExecLogEntry.File({ + path: symlink.path, + // Little hack to make symlinks comparable in the existing UI. + digest: new tools.protos.Digest({ hash: symlink.targetPath, hashFunctionName: "symlink" }), + }) + ); + } + + for (let symlink of symlinksB) { + filesB.push( + new tools.protos.ExecLogEntry.File({ + path: symlink.path, + // Little hack to make symlinks comparable in the existing UI. + digest: new tools.protos.Digest({ hash: symlink.targetPath, hashFunctionName: "symlink" }), + }) + ); + } + + let newFiles: tools.protos.ExecLogEntry.File[] = []; + let unchangdFiles: tools.protos.ExecLogEntry.File[] = []; + let deletedFiles: tools.protos.ExecLogEntry.File[] = []; + let changedFiles: { a: tools.protos.ExecLogEntry.File; b: tools.protos.ExecLogEntry.File }[] = []; + + let fileMap = new Map(); for (let a of filesA) { - fileMap.set(a.file?.path || "", a); + fileMap.set(a.path || "", a); } for (let b of filesB) { - let a = fileMap.get(b.file?.path || ""); + let a = fileMap.get(b.path || ""); if (!a) { newFiles.push(b); continue; } - if (a && a.file?.digest?.hash != b.file?.digest?.hash) { + if (a && a.digest?.hash != b.digest?.hash) { changedFiles.push({ a, b }); - fileMap.delete(b.file?.path || ""); + fileMap.delete(b.path || ""); continue; } - if (a && a.file?.digest?.hash == b.file?.digest?.hash) { + if (a && a.digest?.hash == b.digest?.hash) { unchangdFiles.push(b); - fileMap.delete(b.file?.path || ""); + fileMap.delete(b.path || ""); continue; } } @@ -259,24 +310,28 @@ export default class CompareExecutionLogFilesComponent extends React.Component

(

- + {diff.a.digest?.hashFunctionName == "symlink" ? ( + + ) : ( + + )}
- {diff.a.file?.path} - {diff.a.file?.digest && } + {diff.a.path} + {diff.a.digest && } - {diff.b.file?.digest && } + {diff.b.digest && }
))}