private bool TryDetermineLocalCacheAndInitializePaths(string localCacheRoot, out string errorMessage) { errorMessage = null; LocalCacheResolver localCacheResolver = new LocalCacheResolver(this.enlistment); VstsInfoData vstsInfo = this.QueryVstsInfo(this.tracer, this.enlistment, this.retryConfig); string error; string localCacheKey; if (!localCacheResolver.TryGetLocalCacheKeyFromRepoInfoOrURL( this.tracer, vstsInfo, localCacheKey: out localCacheKey, errorMessage: out error)) { errorMessage = "Error determining local cache key: " + error; return(false); } EventMetadata metadata = this.CreateEventMetadata(); metadata.Add("localCacheRoot", localCacheRoot); metadata.Add("localCacheKey", localCacheKey); metadata.Add(TracingConstants.MessageKey.InfoMessage, "Initializing cache paths"); this.tracer.RelatedEvent(EventLevel.Informational, "CloneVerb_TryDetermineLocalCacheAndInitializePaths", metadata); this.enlistment.InitializeCachePathsFromKey(localCacheRoot, localCacheKey); return(true); }
public void CanGetLocalCacheKeyFromURL() { MockScalarEnlistment enlistment = CreateEnlistment("mock://repoUrl"); LocalCacheResolver localCacheResolver = new LocalCacheResolver(enlistment); LocalKeyShouldBeResolvedFromURL(localCacheResolver, vstsInfo: null); }
public void CanGetLocalCacheKeyFromRepoInfo() { List <string> repoIds = new List <string> { "df3216c6-6d33-476e-8d89-e877a6d74c79", "testId", "826847f5da3ef78114b2a9d5253ada9d95265c76" }; MockTracer tracer = new MockTracer(); MockScalarEnlistment enlistment = CreateEnlistment("mock://repoUrl"); LocalCacheResolver localCacheResolver = new LocalCacheResolver(enlistment); foreach (string repoId in repoIds) { VstsInfoData vstsInfo = new VstsInfoData(); vstsInfo.Repository = new VstsInfoData.RepositoryDetails(); vstsInfo.Repository.Id = repoId; localCacheResolver.TryGetLocalCacheKeyFromRepoInfoOrURL( tracer, vstsInfo, out string localCacheKey, out string errorMessage).ShouldBeTrue(); errorMessage.ShouldBeEmpty(); localCacheKey.ShouldEqual($"{RepoIdKeyPrefix}{repoId}"); } }
private static void LocalKeyShouldBeResolvedFromURL(LocalCacheResolver localCacheResolver, VstsInfoData vstsInfo, out string localCacheKey) { localCacheResolver.TryGetLocalCacheKeyFromRepoInfoOrURL( new MockTracer(), vstsInfo, out localCacheKey, out string errorMessage).ShouldBeTrue(); errorMessage.ShouldBeEmpty(); localCacheKey.Substring(0, UrlKeyPrefix.Length).ShouldEqual(UrlKeyPrefix); SHA1Util.IsValidShaFormat(localCacheKey.Substring(UrlKeyPrefix.Length)).ShouldBeTrue(); }
public void FallBackToUsingURLWhenRepoInfoEmpty() { MockScalarEnlistment enlistment = CreateEnlistment("mock://repoUrl"); LocalCacheResolver localCacheResolver = new LocalCacheResolver(enlistment); VstsInfoData vstsInfo = new VstsInfoData(); LocalKeyShouldBeResolvedFromURL(localCacheResolver, vstsInfo); vstsInfo.Repository = new VstsInfoData.RepositoryDetails(); LocalKeyShouldBeResolvedFromURL(localCacheResolver, vstsInfo); vstsInfo.Repository.Id = string.Empty; LocalKeyShouldBeResolvedFromURL(localCacheResolver, vstsInfo); vstsInfo.Repository.Id = " "; LocalKeyShouldBeResolvedFromURL(localCacheResolver, vstsInfo); }
private bool TryDetermineLocalCacheAndInitializePaths( ITracer tracer, GVFSEnlistment enlistment, ServerGVFSConfig serverGVFSConfig, CacheServerInfo currentCacheServer, string localCacheRoot, out string errorMessage) { errorMessage = null; LocalCacheResolver localCacheResolver = new LocalCacheResolver(enlistment); string error; string localCacheKey; if (!localCacheResolver.TryGetLocalCacheKeyFromLocalConfigOrRemoteCacheServers( tracer, serverGVFSConfig, currentCacheServer, localCacheRoot, localCacheKey: out localCacheKey, errorMessage: out error)) { errorMessage = "Error determining local cache key: " + error; return(false); } EventMetadata metadata = new EventMetadata(); metadata.Add("localCacheRoot", localCacheRoot); metadata.Add("localCacheKey", localCacheKey); metadata.Add(TracingConstants.MessageKey.InfoMessage, "Initializing cache paths"); tracer.RelatedEvent(EventLevel.Informational, "CloneVerb_TryDetermineLocalCacheAndInitializePaths", metadata); enlistment.InitializeCachePathsFromKey(localCacheRoot, localCacheKey); return(true); }
private void EnsureLocalCacheIsHealthy( ITracer tracer, GVFSEnlistment enlistment, RetryConfig retryConfig, ServerGVFSConfig serverGVFSConfig, CacheServerInfo cacheServer) { if (!Directory.Exists(enlistment.LocalCacheRoot)) { try { tracer.RelatedInfo($"{nameof(this.EnsureLocalCacheIsHealthy)}: Local cache root: {enlistment.LocalCacheRoot} missing, recreating it"); Directory.CreateDirectory(enlistment.LocalCacheRoot); } catch (Exception e) { EventMetadata metadata = new EventMetadata(); metadata.Add("Exception", e.ToString()); metadata.Add("enlistment.LocalCacheRoot", enlistment.LocalCacheRoot); tracer.RelatedError(metadata, $"{nameof(this.EnsureLocalCacheIsHealthy)}: Exception while trying to create local cache root"); this.ReportErrorAndExit(tracer, "Failed to create local cache: " + enlistment.LocalCacheRoot); } } // Validate that the GitObjectsRoot directory is on disk, and that the GVFS repo is configured to use it. // If the directory is missing (and cannot be found in the mapping file) a new key for the repo will be added // to the mapping file and used for BOTH the GitObjectsRoot and BlobSizesRoot PhysicalFileSystem fileSystem = new PhysicalFileSystem(); if (Directory.Exists(enlistment.GitObjectsRoot)) { bool gitObjectsRootInAlternates = false; string alternatesFilePath = this.GetAlternatesPath(enlistment); if (File.Exists(alternatesFilePath)) { try { using (Stream stream = fileSystem.OpenFileStream( alternatesFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, callFlushFileBuffers: false)) { using (StreamReader reader = new StreamReader(stream)) { while (!reader.EndOfStream) { string alternatesLine = reader.ReadLine(); if (string.Equals(alternatesLine, enlistment.GitObjectsRoot, StringComparison.OrdinalIgnoreCase)) { gitObjectsRootInAlternates = true; } } } } } catch (Exception e) { EventMetadata exceptionMetadata = new EventMetadata(); exceptionMetadata.Add("Exception", e.ToString()); tracer.RelatedError(exceptionMetadata, $"{nameof(this.EnsureLocalCacheIsHealthy)}: Exception while trying to validate alternates file"); this.ReportErrorAndExit(tracer, $"Failed to validate that alternates file includes git objects root: {e.Message}"); } } else { tracer.RelatedInfo($"{nameof(this.EnsureLocalCacheIsHealthy)}: Alternates file not found"); } if (!gitObjectsRootInAlternates) { tracer.RelatedInfo($"{nameof(this.EnsureLocalCacheIsHealthy)}: GitObjectsRoot ({enlistment.GitObjectsRoot}) missing from alternates files, recreating alternates"); string error; if (!this.TryCreateAlternatesFile(fileSystem, enlistment, out error)) { this.ReportErrorAndExit(tracer, $"Failed to update alternates file to include git objects root: {error}"); } } } else { tracer.RelatedInfo($"{nameof(this.EnsureLocalCacheIsHealthy)}: GitObjectsRoot ({enlistment.GitObjectsRoot}) missing, determining new root"); if (cacheServer == null) { cacheServer = CacheServerResolver.GetCacheServerFromConfig(enlistment); } string error; if (serverGVFSConfig == null) { if (retryConfig == null) { if (!RetryConfig.TryLoadFromGitConfig(tracer, enlistment, out retryConfig, out error)) { this.ReportErrorAndExit(tracer, "Failed to determine GVFS timeout and max retries: " + error); } } serverGVFSConfig = this.QueryGVFSConfig(tracer, enlistment, retryConfig); } string localCacheKey; LocalCacheResolver localCacheResolver = new LocalCacheResolver(enlistment); if (!localCacheResolver.TryGetLocalCacheKeyFromLocalConfigOrRemoteCacheServers( tracer, serverGVFSConfig, cacheServer, enlistment.LocalCacheRoot, localCacheKey: out localCacheKey, errorMessage: out error)) { this.ReportErrorAndExit(tracer, $"Previous git objects root ({enlistment.GitObjectsRoot}) not found, and failed to determine new local cache key: {error}"); } EventMetadata metadata = new EventMetadata(); metadata.Add("localCacheRoot", enlistment.LocalCacheRoot); metadata.Add("localCacheKey", localCacheKey); metadata.Add(TracingConstants.MessageKey.InfoMessage, "Initializing and persisting updated paths"); tracer.RelatedEvent(EventLevel.Informational, "GVFSVerb_EnsureLocalCacheIsHealthy_InitializePathsFromKey", metadata); enlistment.InitializeCachePathsFromKey(enlistment.LocalCacheRoot, localCacheKey); tracer.RelatedInfo($"{nameof(this.EnsureLocalCacheIsHealthy)}: Creating GitObjectsRoot ({enlistment.GitObjectsRoot}), GitPackRoot ({enlistment.GitPackRoot}), and BlobSizesRoot ({enlistment.BlobSizesRoot})"); try { Directory.CreateDirectory(enlistment.GitObjectsRoot); Directory.CreateDirectory(enlistment.GitPackRoot); } catch (Exception e) { EventMetadata exceptionMetadata = new EventMetadata(); exceptionMetadata.Add("Exception", e.ToString()); exceptionMetadata.Add("enlistment.LocalCacheRoot", enlistment.LocalCacheRoot); exceptionMetadata.Add("enlistment.GitObjectsRoot", enlistment.GitObjectsRoot); exceptionMetadata.Add("enlistment.GitPackRoot", enlistment.GitPackRoot); exceptionMetadata.Add("enlistment.BlobSizesRoot", enlistment.BlobSizesRoot); tracer.RelatedError(exceptionMetadata, $"{nameof(this.InitializeLocalCacheAndObjectsPaths)}: Exception while trying to create objects, pack, and sizes folders"); this.ReportErrorAndExit(tracer, "Failed to create objects, pack, and sizes folders"); } tracer.RelatedInfo($"{nameof(this.EnsureLocalCacheIsHealthy)}: Creating new alternates file"); if (!this.TryCreateAlternatesFile(fileSystem, enlistment, out error)) { this.ReportErrorAndExit(tracer, $"Failed to update alterates file with new objects path: {error}"); } tracer.RelatedInfo($"{nameof(this.EnsureLocalCacheIsHealthy)}: Saving git objects root ({enlistment.GitObjectsRoot}) in repo metadata"); RepoMetadata.Instance.SetGitObjectsRoot(enlistment.GitObjectsRoot); tracer.RelatedInfo($"{nameof(this.EnsureLocalCacheIsHealthy)}: Saving blob sizes root ({enlistment.BlobSizesRoot}) in repo metadata"); RepoMetadata.Instance.SetBlobSizesRoot(enlistment.BlobSizesRoot); } // Validate that the BlobSizesRoot folder is on disk. // Note that if a user performed an action that resulted in the entire .gvfscache being deleted, the code above // for validating GitObjectsRoot will have already taken care of generating a new key and setting a new enlistment.BlobSizesRoot path if (!Directory.Exists(enlistment.BlobSizesRoot)) { tracer.RelatedInfo($"{nameof(this.EnsureLocalCacheIsHealthy)}: BlobSizesRoot ({enlistment.BlobSizesRoot}) not found, re-creating"); try { Directory.CreateDirectory(enlistment.BlobSizesRoot); } catch (Exception e) { EventMetadata exceptionMetadata = new EventMetadata(); exceptionMetadata.Add("Exception", e.ToString()); exceptionMetadata.Add("enlistment.BlobSizesRoot", enlistment.BlobSizesRoot); tracer.RelatedError(exceptionMetadata, $"{nameof(this.InitializeLocalCacheAndObjectsPaths)}: Exception while trying to create blob sizes folder"); this.ReportErrorAndExit(tracer, "Failed to create blob sizes folder"); } } }
private Result DoClone(string fullEnlistmentRootPathParameter, string normalizedEnlistmentRootPath) { Result cloneResult = null; cloneResult = this.TryCreateEnlistment(fullEnlistmentRootPathParameter, normalizedEnlistmentRootPath, out this.enlistment); if (!cloneResult.Success) { this.tracer.RelatedError($"Error while creating enlistment: {cloneResult.ErrorMessage}"); return(cloneResult); } this.tracer.AddLogFileEventListener( ScalarEnlistment.GetNewScalarLogFileName(this.enlistment.ScalarLogsRoot, ScalarConstants.LogFileTypes.Clone), EventLevel.Informational, Keywords.Any); this.tracer.WriteStartEvent( this.enlistment.EnlistmentRoot, this.enlistment.RepoUrl, this.CacheServerUrl, this.AddVerbDataToMetadata(new EventMetadata { { nameof(this.Branch), this.Branch }, { nameof(this.LocalCacheRoot), this.LocalCacheRoot }, { nameof(this.SingleBranch), this.SingleBranch }, { nameof(this.FullClone), this.FullClone }, { nameof(this.NoFetchCommitsAndTrees), this.NoFetchCommitsAndTrees }, { nameof(this.Unattended), this.Unattended }, { nameof(ScalarPlatform.Instance.IsElevated), ScalarPlatform.Instance.IsElevated() }, { "ProcessID", Process.GetCurrentProcess().Id }, { nameof(this.EnlistmentRootPathParameter), this.EnlistmentRootPathParameter }, { nameof(fullEnlistmentRootPathParameter), fullEnlistmentRootPathParameter }, })); this.cacheServerResolver = new CacheServerResolver(this.tracer, this.enlistment); this.cacheServer = this.cacheServerResolver.ParseUrlOrFriendlyName(this.CacheServerUrl); string resolvedLocalCacheRoot; if (string.IsNullOrWhiteSpace(this.LocalCacheRoot)) { if (!LocalCacheResolver.TryGetDefaultLocalCacheRoot(this.enlistment, out resolvedLocalCacheRoot, out string localCacheRootError)) { this.ReportErrorAndExit( this.tracer, $"Failed to determine the default location for the local Scalar cache: `{localCacheRootError}`"); } } else { resolvedLocalCacheRoot = Path.GetFullPath(this.LocalCacheRoot); } this.Output.WriteLine("Clone parameters:"); this.Output.WriteLine(" Repo URL: " + this.enlistment.RepoUrl); this.Output.WriteLine(" Branch: " + (string.IsNullOrWhiteSpace(this.Branch) ? "Default" : this.Branch)); this.Output.WriteLine(" Cache Server: " + this.cacheServer); this.Output.WriteLine(" Local Cache: " + resolvedLocalCacheRoot); this.Output.WriteLine(" Destination: " + this.enlistment.EnlistmentRoot); this.Output.WriteLine(" FullClone: " + this.FullClone); string authErrorMessage; if (!this.TryAuthenticate(this.tracer, this.enlistment, out authErrorMessage)) { this.ReportErrorAndExit(this.tracer, "Cannot clone because authentication failed: " + authErrorMessage); } this.retryConfig = this.GetRetryConfig(this.tracer, this.enlistment, TimeSpan.FromMinutes(RetryConfig.FetchAndCloneTimeoutMinutes)); this.serverScalarConfig = this.QueryScalarConfig(this.tracer, this.enlistment, this.retryConfig); this.cacheServer = this.ResolveCacheServer(this.tracer, this.cacheServer, this.cacheServerResolver, this.serverScalarConfig); this.ValidateClientVersions(this.tracer, this.enlistment, this.serverScalarConfig, showWarnings: true); using (this.objectRequestor = new GitObjectsHttpRequestor(this.tracer, this.enlistment, this.cacheServer, this.retryConfig)) { cloneResult = this.CreateScalarDirctories(resolvedLocalCacheRoot); if (!cloneResult.Success) { this.tracer.RelatedError(cloneResult.ErrorMessage); return(cloneResult); } this.ShowStatusWhileRunning( () => { cloneResult = this.CreateClone(); return(cloneResult.Success); }, "Cloning"); if (!cloneResult.Success) { this.tracer.RelatedError(cloneResult.ErrorMessage); return(cloneResult); } if (!this.NoFetchCommitsAndTrees) { ReturnCode result = this.Execute <RunVerb>( this.enlistment, verb => { verb.MaintenanceTask = ScalarConstants.VerbParameters.Maintenance.FetchTaskName; verb.SkipVersionCheck = true; verb.ResolvedCacheServer = this.cacheServer; verb.ServerScalarConfig = this.serverScalarConfig; }); if (result != ReturnCode.Success) { this.Output.WriteLine("\r\nError while fetching commits and trees @ {0}", fullEnlistmentRootPathParameter); return(cloneResult); } } this.ShowStatusWhileRunning( () => { cloneResult = this.CheckoutRepo(); return(cloneResult.Success); }, "Populating working directory"); } if (cloneResult.Success) { cloneResult = this.TryRegisterRepo(); } return(cloneResult); }
public override void Execute() { int exitCode = 0; this.ValidatePathParameter(this.EnlistmentRootPathParameter); this.ValidatePathParameter(this.LocalCacheRoot); string fullEnlistmentRootPathParameter; string normalizedEnlistmentRootPath = this.GetCloneRoot(out fullEnlistmentRootPathParameter); if (!string.IsNullOrWhiteSpace(this.LocalCacheRoot)) { string fullLocalCacheRootPath = Path.GetFullPath(this.LocalCacheRoot); string errorMessage; string normalizedLocalCacheRootPath; if (!GVFSPlatform.Instance.FileSystem.TryGetNormalizedPath(fullLocalCacheRootPath, out normalizedLocalCacheRootPath, out errorMessage)) { this.ReportErrorAndExit($"Failed to determine normalized path for '--local-cache-path' path {fullLocalCacheRootPath}: {errorMessage}"); } if (normalizedLocalCacheRootPath.StartsWith( Path.Combine(normalizedEnlistmentRootPath, GVFSConstants.WorkingDirectoryRootName), StringComparison.OrdinalIgnoreCase)) { this.ReportErrorAndExit("'--local-cache-path' cannot be inside the src folder"); } } this.CheckKernelDriverSupported(normalizedEnlistmentRootPath); this.CheckNotInsideExistingRepo(normalizedEnlistmentRootPath); this.BlockEmptyCacheServerUrl(this.CacheServerUrl); try { GVFSEnlistment enlistment; Result cloneResult = new Result(false); CacheServerInfo cacheServer = null; ServerGVFSConfig serverGVFSConfig = null; using (JsonTracer tracer = new JsonTracer(GVFSConstants.GVFSEtwProviderName, "GVFSClone")) { cloneResult = this.TryCreateEnlistment(fullEnlistmentRootPathParameter, normalizedEnlistmentRootPath, out enlistment); if (cloneResult.Success) { tracer.AddLogFileEventListener( GVFSEnlistment.GetNewGVFSLogFileName(enlistment.GVFSLogsRoot, GVFSConstants.LogFileTypes.Clone), EventLevel.Informational, Keywords.Any); tracer.WriteStartEvent( enlistment.EnlistmentRoot, enlistment.RepoUrl, this.CacheServerUrl, new EventMetadata { { "Branch", this.Branch }, { "LocalCacheRoot", this.LocalCacheRoot }, { "SingleBranch", this.SingleBranch }, { "NoMount", this.NoMount }, { "NoPrefetch", this.NoPrefetch }, { "Unattended", this.Unattended }, { "IsElevated", GVFSPlatform.Instance.IsElevated() }, { "NamedPipeName", enlistment.NamedPipeName }, { nameof(this.EnlistmentRootPathParameter), this.EnlistmentRootPathParameter }, { nameof(fullEnlistmentRootPathParameter), fullEnlistmentRootPathParameter }, }); CacheServerResolver cacheServerResolver = new CacheServerResolver(tracer, enlistment); cacheServer = cacheServerResolver.ParseUrlOrFriendlyName(this.CacheServerUrl); string resolvedLocalCacheRoot; if (string.IsNullOrWhiteSpace(this.LocalCacheRoot)) { string localCacheRootError; if (!LocalCacheResolver.TryGetDefaultLocalCacheRoot(enlistment, out resolvedLocalCacheRoot, out localCacheRootError)) { this.ReportErrorAndExit( tracer, $"Failed to determine the default location for the local GVFS cache: `{localCacheRootError}`"); } } else { resolvedLocalCacheRoot = Path.GetFullPath(this.LocalCacheRoot); } this.Output.WriteLine("Clone parameters:"); this.Output.WriteLine(" Repo URL: " + enlistment.RepoUrl); this.Output.WriteLine(" Branch: " + (string.IsNullOrWhiteSpace(this.Branch) ? "Default" : this.Branch)); this.Output.WriteLine(" Cache Server: " + cacheServer); this.Output.WriteLine(" Local Cache: " + resolvedLocalCacheRoot); this.Output.WriteLine(" Destination: " + enlistment.EnlistmentRoot); string authErrorMessage; if (!this.TryAuthenticate(tracer, enlistment, out authErrorMessage)) { this.ReportErrorAndExit(tracer, "Cannot clone because authentication failed: " + authErrorMessage); } RetryConfig retryConfig = this.GetRetryConfig(tracer, enlistment, TimeSpan.FromMinutes(RetryConfig.FetchAndCloneTimeoutMinutes)); serverGVFSConfig = this.QueryGVFSConfig(tracer, enlistment, retryConfig); cacheServer = this.ResolveCacheServer(tracer, cacheServer, cacheServerResolver, serverGVFSConfig); this.ValidateClientVersions(tracer, enlistment, serverGVFSConfig, showWarnings: true); this.ShowStatusWhileRunning( () => { cloneResult = this.TryClone(tracer, enlistment, cacheServer, retryConfig, serverGVFSConfig, resolvedLocalCacheRoot); return(cloneResult.Success); }, "Cloning", normalizedEnlistmentRootPath); } if (!cloneResult.Success) { tracer.RelatedError(cloneResult.ErrorMessage); } } if (cloneResult.Success) { if (!this.NoPrefetch) { ReturnCode result = this.Execute <PrefetchVerb>( enlistment, verb => { verb.Commits = true; verb.SkipVersionCheck = true; verb.ResolvedCacheServer = cacheServer; verb.ServerGVFSConfig = serverGVFSConfig; }); if (result != ReturnCode.Success) { this.Output.WriteLine("\r\nError during prefetch @ {0}", fullEnlistmentRootPathParameter); exitCode = (int)result; } } if (this.NoMount) { this.Output.WriteLine("\r\nIn order to mount, first cd to within your enlistment, then call: "); this.Output.WriteLine("gvfs mount"); } else { this.Execute <MountVerb>( enlistment, verb => { verb.SkipMountedCheck = true; verb.SkipVersionCheck = true; verb.ResolvedCacheServer = cacheServer; verb.DownloadedGVFSConfig = serverGVFSConfig; }); } } else { this.Output.WriteLine("\r\nCannot clone @ {0}", fullEnlistmentRootPathParameter); this.Output.WriteLine("Error: {0}", cloneResult.ErrorMessage); exitCode = (int)ReturnCode.GenericError; } } catch (AggregateException e) { this.Output.WriteLine("Cannot clone @ {0}:", fullEnlistmentRootPathParameter); foreach (Exception ex in e.Flatten().InnerExceptions) { this.Output.WriteLine("Exception: {0}", ex.ToString()); } exitCode = (int)ReturnCode.GenericError; } catch (VerbAbortedException) { throw; } catch (Exception e) { this.ReportErrorAndExit("Cannot clone @ {0}: {1}", fullEnlistmentRootPathParameter, e.ToString()); } Environment.Exit(exitCode); }
private void EnsureLocalCacheIsHealthy( ITracer tracer, GVFSEnlistment enlistment, RetryConfig retryConfig, GVFSConfig gvfsConfig, CacheServerInfo cacheServer) { if (!Directory.Exists(enlistment.LocalCacheRoot)) { try { tracer.RelatedInfo($"{nameof(this.EnsureLocalCacheIsHealthy)}: Local cache root: {enlistment.LocalCacheRoot} missing, recreating it"); Directory.CreateDirectory(enlistment.LocalCacheRoot); } catch (Exception e) { EventMetadata metadata = new EventMetadata(); metadata.Add("Exception", e.ToString()); metadata.Add("enlistment.LocalCacheRoot", enlistment.LocalCacheRoot); tracer.RelatedError(metadata, $"{nameof(this.EnsureLocalCacheIsHealthy)}: Exception while trying to create local cache root"); this.ReportErrorAndExit(tracer, "Failed to create local cache: " + enlistment.LocalCacheRoot); } } PhysicalFileSystem fileSystem = new PhysicalFileSystem(); if (Directory.Exists(enlistment.GitObjectsRoot)) { bool gitObjectsRootInAlternates = false; string alternatesFilePath = this.GetAlternatesPath(enlistment); if (File.Exists(alternatesFilePath)) { try { using (Stream stream = fileSystem.OpenFileStream( alternatesFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, callFlushFileBuffers: false)) { using (StreamReader reader = new StreamReader(stream)) { while (!reader.EndOfStream) { string alternatesLine = reader.ReadLine(); if (string.Equals(alternatesLine, enlistment.GitObjectsRoot, StringComparison.OrdinalIgnoreCase)) { gitObjectsRootInAlternates = true; } } } } } catch (Exception e) { EventMetadata exceptionMetadata = new EventMetadata(); exceptionMetadata.Add("Exception", e.ToString()); tracer.RelatedError(exceptionMetadata, $"{nameof(this.EnsureLocalCacheIsHealthy)}: Exception while trying to validate alternates file"); this.ReportErrorAndExit(tracer, $"Failed to validate that alternates file includes git objects root: {e.Message}"); } } else { tracer.RelatedInfo($"{nameof(this.EnsureLocalCacheIsHealthy)}: Alternates file not found"); } if (!gitObjectsRootInAlternates) { tracer.RelatedInfo($"{nameof(this.EnsureLocalCacheIsHealthy)}: GitObjectsRoot ({enlistment.GitObjectsRoot}) missing from alternates files, recreating alternates"); string error; if (!this.TryCreateAlternatesFile(fileSystem, enlistment, out error)) { this.ReportErrorAndExit(tracer, $"Failed to update alternates file to include git objects root: {error}"); } } } else { tracer.RelatedInfo($"{nameof(this.EnsureLocalCacheIsHealthy)}: GitObjectsRoot ({enlistment.GitObjectsRoot}) missing, determining new root"); if (cacheServer == null) { cacheServer = CacheServerResolver.GetCacheServerFromConfig(enlistment); } string error; if (gvfsConfig == null) { if (retryConfig == null) { if (!RetryConfig.TryLoadFromGitConfig(tracer, enlistment, out retryConfig, out error)) { this.ReportErrorAndExit(tracer, "Failed to determine GVFS timeout and max retries: " + error); } } gvfsConfig = this.QueryGVFSConfig(tracer, enlistment, retryConfig); } string localCacheKey; LocalCacheResolver localCacheResolver = new LocalCacheResolver(enlistment); if (!localCacheResolver.TryGetLocalCacheKeyFromLocalConfigOrRemoteCacheServers( tracer, gvfsConfig, cacheServer, enlistment.LocalCacheRoot, localCacheKey: out localCacheKey, errorMessage: out error)) { this.ReportErrorAndExit(tracer, $"Previous git objects root ({enlistment.GitObjectsRoot}) not found, and failed to determine new local cache key: {error}"); } EventMetadata metadata = new EventMetadata(); metadata.Add("localCacheRoot", enlistment.LocalCacheRoot); metadata.Add("localCacheKey", localCacheKey); metadata.Add(TracingConstants.MessageKey.InfoMessage, "Initializing and persisting updated paths"); tracer.RelatedEvent(EventLevel.Informational, "GVFSVerb_InitializeLocalCacheAndObjectsPaths", metadata); enlistment.InitializeLocalCacheAndObjectsPathsFromKey(enlistment.LocalCacheRoot, localCacheKey); tracer.RelatedInfo($"{nameof(this.EnsureLocalCacheIsHealthy)}: Creating GitObjectsRoot ({enlistment.GitObjectsRoot}) and GitPackRoot ({enlistment.GitPackRoot})"); try { Directory.CreateDirectory(enlistment.GitObjectsRoot); Directory.CreateDirectory(enlistment.GitPackRoot); } catch (Exception e) { EventMetadata exceptionMetadata = new EventMetadata(); exceptionMetadata.Add("Exception", e.ToString()); exceptionMetadata.Add("enlistment.LocalCacheRoot", enlistment.LocalCacheRoot); exceptionMetadata.Add("enlistment.GitObjectsRoot", enlistment.GitObjectsRoot); exceptionMetadata.Add("enlistment.GitPackRoot", enlistment.GitPackRoot); tracer.RelatedError(exceptionMetadata, $"{nameof(this.InitializeLocalCacheAndObjectsPaths)}: Exception while trying to create objects and pack folders"); this.ReportErrorAndExit(tracer, "Failed to create objects and pack folders"); } tracer.RelatedInfo($"{nameof(this.EnsureLocalCacheIsHealthy)}: Creating new alternates file"); if (!this.TryCreateAlternatesFile(fileSystem, enlistment, out error)) { this.ReportErrorAndExit(tracer, $"Failed to update alterates file with new objects path: {error}"); } tracer.RelatedInfo($"{nameof(this.EnsureLocalCacheIsHealthy)}: Saving git objects root ({enlistment.GitObjectsRoot}) in repo metadata"); RepoMetadata.Instance.SetGitObjectsRoot(enlistment.GitObjectsRoot); } }
public override void Execute() { int exitCode = 0; this.ValidatePathParameter(this.EnlistmentRootPath); this.ValidatePathParameter(this.LocalCacheRoot); this.EnlistmentRootPath = this.GetCloneRoot(); if (!string.IsNullOrWhiteSpace(this.LocalCacheRoot)) { if (Path.GetFullPath(this.LocalCacheRoot).StartsWith( Path.Combine(this.EnlistmentRootPath, GVFSConstants.WorkingDirectoryRootName), StringComparison.OrdinalIgnoreCase)) { this.ReportErrorAndExit("'--local-cache-path' cannot be inside the src folder"); } } this.CheckNTFSVolume(); this.CheckGVFltHealthy(); this.CheckNotInsideExistingRepo(); this.BlockEmptyCacheServerUrl(this.CacheServerUrl); try { GVFSEnlistment enlistment; Result cloneResult = new Result(false); CacheServerInfo cacheServer = null; GVFSConfig gvfsConfig = null; using (JsonEtwTracer tracer = new JsonEtwTracer(GVFSConstants.GVFSEtwProviderName, "GVFSClone")) { cloneResult = this.TryCreateEnlistment(out enlistment); if (cloneResult.Success) { tracer.AddLogFileEventListener( GVFSEnlistment.GetNewGVFSLogFileName(enlistment.GVFSLogsRoot, GVFSConstants.LogFileTypes.Clone), EventLevel.Informational, Keywords.Any); tracer.WriteStartEvent( enlistment.EnlistmentRoot, enlistment.RepoUrl, this.CacheServerUrl, new EventMetadata { { "Branch", this.Branch }, { "LocalCacheRoot", this.LocalCacheRoot }, { "SingleBranch", this.SingleBranch }, { "NoMount", this.NoMount }, { "NoPrefetch", this.NoPrefetch }, { "Unattended", this.Unattended }, { "IsElevated", ProcessHelper.IsAdminElevated() }, }); CacheServerResolver cacheServerResolver = new CacheServerResolver(tracer, enlistment); cacheServer = cacheServerResolver.ParseUrlOrFriendlyName(this.CacheServerUrl); string error; string resolvedLocalCacheRoot; if (string.IsNullOrWhiteSpace(this.LocalCacheRoot)) { if (!LocalCacheResolver.TryGetDefaultLocalCacheRoot(enlistment, out resolvedLocalCacheRoot, out error)) { this.ReportErrorAndExit(tracer, "Cannot clone, error determining local cache path: " + error); } } else { resolvedLocalCacheRoot = Path.GetFullPath(this.LocalCacheRoot); } this.Output.WriteLine("Clone parameters:"); this.Output.WriteLine(" Repo URL: " + enlistment.RepoUrl); this.Output.WriteLine(" Branch: " + (string.IsNullOrWhiteSpace(this.Branch) ? "Default" : this.Branch)); this.Output.WriteLine(" Cache Server: " + cacheServer); this.Output.WriteLine(" Local Cache: " + resolvedLocalCacheRoot); this.Output.WriteLine(" Destination: " + enlistment.EnlistmentRoot); string authErrorMessage = null; if (!this.ShowStatusWhileRunning( () => enlistment.Authentication.TryRefreshCredentials(tracer, out authErrorMessage), "Authenticating")) { this.ReportErrorAndExit(tracer, "Cannot clone because authentication failed"); } RetryConfig retryConfig = this.GetRetryConfig(tracer, enlistment, TimeSpan.FromMinutes(RetryConfig.FetchAndCloneTimeoutMinutes)); gvfsConfig = this.QueryGVFSConfig(tracer, enlistment, retryConfig); cacheServer = this.ResolveCacheServer(tracer, cacheServer, cacheServerResolver, gvfsConfig); this.ValidateClientVersions(tracer, enlistment, gvfsConfig, showWarnings: true); this.ShowStatusWhileRunning( () => { cloneResult = this.TryClone(tracer, enlistment, cacheServer, retryConfig, gvfsConfig, resolvedLocalCacheRoot); return(cloneResult.Success); }, "Cloning"); } if (!cloneResult.Success) { tracer.RelatedError(cloneResult.ErrorMessage); } } if (cloneResult.Success) { if (!this.NoPrefetch) { this.Execute <PrefetchVerb>( this.EnlistmentRootPath, verb => { verb.Commits = true; verb.SkipVersionCheck = true; verb.ResolvedCacheServer = cacheServer; verb.GVFSConfig = gvfsConfig; }); } if (this.NoMount) { this.Output.WriteLine("\r\nIn order to mount, first cd to within your enlistment, then call: "); this.Output.WriteLine("gvfs mount"); } else { this.Execute <MountVerb>( this.EnlistmentRootPath, verb => { verb.SkipMountedCheck = true; verb.SkipVersionCheck = true; verb.ResolvedCacheServer = cacheServer; verb.DownloadedGVFSConfig = gvfsConfig; }); } } else { this.Output.WriteLine("\r\nCannot clone @ {0}", this.EnlistmentRootPath); this.Output.WriteLine("Error: {0}", cloneResult.ErrorMessage); exitCode = (int)ReturnCode.GenericError; } } catch (AggregateException e) { this.Output.WriteLine("Cannot clone @ {0}:", this.EnlistmentRootPath); foreach (Exception ex in e.Flatten().InnerExceptions) { this.Output.WriteLine("Exception: {0}", ex.ToString()); } exitCode = (int)ReturnCode.GenericError; } catch (VerbAbortedException) { throw; } catch (Exception e) { this.ReportErrorAndExit("Cannot clone @ {0}: {1}", this.EnlistmentRootPath, e.ToString()); } Environment.Exit(exitCode); }
private static void LocalKeyShouldBeResolvedFromURL(LocalCacheResolver localCacheResolver, VstsInfoData vstsInfo) { LocalKeyShouldBeResolvedFromURL(localCacheResolver, vstsInfo, out _); }
private Result DoClone(string fullEnlistmentRootPathParameter, string normalizedEnlistmentRootPath) { Result cloneResult = null; cloneResult = this.TryCreateEnlistment(fullEnlistmentRootPathParameter, normalizedEnlistmentRootPath, out this.enlistment); if (!cloneResult.Success) { this.tracer.RelatedError($"Error while creating enlistment: {cloneResult.ErrorMessage}"); return(cloneResult); } this.tracer.AddLogFileEventListener( ScalarEnlistment.GetNewScalarLogFileName(this.enlistment.ScalarLogsRoot, ScalarConstants.LogFileTypes.Clone), EventLevel.Informational, Keywords.Any); this.tracer.WriteStartEvent( this.enlistment.EnlistmentRoot, this.enlistment.RepoUrl, this.CacheServerUrl, this.AddVerbDataToMetadata(new EventMetadata { { nameof(this.Branch), this.Branch }, { nameof(this.LocalCacheRoot), this.LocalCacheRoot }, { nameof(this.SingleBranch), this.SingleBranch }, { nameof(this.FullClone), this.FullClone }, { nameof(this.NoFetchCommitsAndTrees), this.NoFetchCommitsAndTrees }, { nameof(this.Unattended), this.Unattended }, { nameof(ScalarPlatform.Instance.IsElevated), ScalarPlatform.Instance.IsElevated() }, { "ProcessID", Process.GetCurrentProcess().Id }, { nameof(this.EnlistmentRootPathParameter), this.EnlistmentRootPathParameter }, { nameof(fullEnlistmentRootPathParameter), fullEnlistmentRootPathParameter }, })); this.cacheServerResolver = new CacheServerResolver(this.tracer, this.enlistment); this.cacheServer = this.cacheServerResolver.ParseUrlOrFriendlyName(this.CacheServerUrl); string resolvedLocalCacheRoot; if (string.IsNullOrWhiteSpace(this.LocalCacheRoot)) { if (!LocalCacheResolver.TryGetDefaultLocalCacheRoot(this.enlistment, out resolvedLocalCacheRoot, out string localCacheRootError)) { this.ReportErrorAndExit( this.tracer, $"Failed to determine the default location for the local Scalar cache: `{localCacheRootError}`"); } } else { resolvedLocalCacheRoot = Path.GetFullPath(this.LocalCacheRoot); } // Determine what features of Git we have available to guide how we init/clone the repository var gitFeatures = GitFeatureFlags.None; string gitBinPath = ScalarPlatform.Instance.GitInstallation.GetInstalledGitBinPath(); this.tracer.RelatedInfo("Attempting to determine Git version for installation '{0}'", gitBinPath); if (GitProcess.TryGetVersion(gitBinPath, out var gitVersion, out string gitVersionError)) { this.tracer.RelatedInfo("Git installation '{0}' has version '{1}", gitBinPath, gitVersion); gitFeatures = gitVersion.GetFeatures(); }