private TeeWorkspace MatchExactWorkspace(TeeWorkspace[] teeWorkspaces, string name, TfsVCTeeWorkspaceMapping[] definitionMappings, string sourcesDirectory) { ArgUtil.NotNullOrEmpty(name, nameof(name)); ArgUtil.NotNullOrEmpty(sourcesDirectory, nameof(sourcesDirectory)); // Short-circuit early if the sources directory is empty. // // Consider the sources directory to be empty if it only contains a .tf directory exists. This can // indicate the workspace is in a corrupted state and the tf commands (e.g. status) will not return // reliable information. An easy way to reproduce this is to delete the workspace directory, then // run "tf status" on that workspace. The .tf directory will be recreated but the contents will be // in a corrupted state. if (!Directory.Exists(sourcesDirectory) || !Directory.EnumerateFileSystemEntries(sourcesDirectory).Any(x => !x.EndsWith($"{Path.DirectorySeparatorChar}.tf"))) { Trace.Info($"Sources directory does not exist or is empty."); return(null); } string machineName = Environment.MachineName; Trace.Info("Attempting to find a matching workspace."); Trace.Info($"Expected workspace name '{name}', machine name '{machineName}', number of mappings '{definitionMappings?.Length ?? 0}'."); foreach (TeeWorkspace teeWorkspace in teeWorkspaces ?? new TeeWorkspace[0]) { // Compare the works name, machine name, and number of mappings. Trace.Info($"Candidate workspace name '{teeWorkspace.Name}', machine name '{teeWorkspace.Computer}', number of mappings '{teeWorkspace.Mappings?.Length ?? 0}'."); if (!string.Equals(teeWorkspace.Name, name, StringComparison.Ordinal) || !string.Equals(teeWorkspace.Computer, machineName, StringComparison.Ordinal) || (teeWorkspace.Mappings?.Length ?? 0) != (definitionMappings?.Length ?? 0)) { continue; } // TODO: Is there such a thing as a single level cloak? // Sort the TEE mappings. List <TeeMapping> sortedTeeMappings = (teeWorkspace.Mappings ?? new TeeMapping[0]) .OrderBy(x => !x.Cloak) // Cloaks first .ThenBy(x => !x.Recursive) // Then recursive maps .ThenBy(x => x.ServerPath) // Then sort by server path .ToList(); sortedTeeMappings.ForEach(x => Trace.Info($"TEE mapping: cloak '{x.Cloak}', recursive '{x.Recursive}', server path '{x.ServerPath}', local path '{x.LocalPath}'.")); // Sort the definition mappings. List <TfsVCTeeWorkspaceMapping> sortedDefinitionMappings = (definitionMappings ?? new TfsVCTeeWorkspaceMapping[0]) .OrderBy(x => x.MappingType != TfsVCTeeMappingType.Cloak) // Cloaks first .ThenBy(x => !RecursiveServerPath(x.ServerPath)) // Then recursive maps .ThenBy(x => NormalizeServerPath(x.ServerPath)) // Then sort by the normalized server path .ToList(); sortedDefinitionMappings.ForEach(x => Trace.Info($"Definition mapping: cloak '{x.MappingType == TfsVCTeeMappingType.Cloak}', recursive '{RecursiveServerPath(x.ServerPath)}', server path '{NormalizeServerPath(x.ServerPath)}', local path '{ResolveMappingLocalPath(x, sourcesDirectory)}'.")); // Compare the mappings, bool allMatch = true; for (int i = 0; i < sortedTeeMappings.Count; i++) { TeeMapping teeMapping = sortedTeeMappings[i]; TfsVCTeeWorkspaceMapping definitionMapping = sortedDefinitionMappings[i]; if (teeMapping.Cloak) { // The TEE mapping is a cloak. // Verify the definition mapping is a cloak and the server paths match. if (definitionMapping.MappingType != TfsVCTeeMappingType.Cloak || !string.Equals(teeMapping.ServerPath, definitionMapping.ServerPath, StringComparison.Ordinal)) { allMatch = false; // Mapping comparison failed. break; } } else { // The TEE mapping is a map. // Verify the definition mapping is a map and the local paths match. if (definitionMapping.MappingType != TfsVCTeeMappingType.Map || !string.Equals(teeMapping.LocalPath, ResolveMappingLocalPath(definitionMapping, sourcesDirectory), StringComparison.Ordinal)) { allMatch = false; // Mapping comparison failed. break; } if (teeMapping.Recursive) { // The TEE mapping is a recursive map. // Verify the server paths match. if (!string.Equals(teeMapping.ServerPath, definitionMapping.ServerPath, StringComparison.Ordinal)) { allMatch = false; // Mapping comparison failed. break; } } else { // The TEE mapping is a single-level map. // Verify the definition mapping is a single-level map and the normalized server paths match. if (RecursiveServerPath(definitionMapping.ServerPath) || !string.Equals(teeMapping.ServerPath, NormalizeServerPath(definitionMapping.ServerPath), StringComparison.Ordinal)) { allMatch = false; // Mapping comparison failed. break; } } } } if (allMatch) { Trace.Info("Matching workspace found."); return(teeWorkspace); } } Trace.Info("Matching workspace not found."); return(null); }
private static string ResolveMappingLocalPath(TfsVCTeeWorkspaceMapping definitionMapping, string sourcesDirectory) { return(Path.Combine( sourcesDirectory, (definitionMapping.LocalPath ?? string.Empty).Trim('/', '\\').Replace('\\', Path.DirectorySeparatorChar))); }