public override bool TryInstallGitCommandHooks(GVFSContext context, string executingDirectory, string hookName, string commandHookPath, out string errorMessage) { // The GitHooksLoader requires the following setup to invoke a hook: // Copy GithooksLoader.exe to hook-name.exe // Create a text file named hook-name.hooks that lists the applications to execute for the hook, one application per line string gitHooksloaderPath = Path.Combine(executingDirectory, GVFSConstants.DotGit.Hooks.LoaderExecutable); if (!HooksInstaller.TryHooksInstallationAction( () => HooksInstaller.CopyHook(context, gitHooksloaderPath, commandHookPath + GVFSPlatform.Instance.Constants.ExecutableExtension), out errorMessage)) { errorMessage = "Failed to copy " + GVFSConstants.DotGit.Hooks.LoaderExecutable + " to " + commandHookPath + GVFSPlatform.Instance.Constants.ExecutableExtension + "\n" + errorMessage; return(false); } if (!HooksInstaller.TryHooksInstallationAction( () => WindowsGitHooksInstaller.CreateHookCommandConfig(context, hookName, commandHookPath), out errorMessage)) { errorMessage = "Failed to create " + commandHookPath + GVFSConstants.GitConfig.HooksExtension + "\n" + errorMessage; return(false); } return(true); }
public void MergeHooksDataThrowsOnFoundGVFSHooks() { Assert.Throws <HooksInstaller.HooksConfigurationException>( () => HooksInstaller.MergeHooksData( new string[] { "first", GVFSPlatform.Instance.Constants.GVFSHooksExecutableName }, Filename, this.expectedAbsoluteGvfsHookPath)); }
public void MergeHooksDataThrowsOnFoundGSDHooks() { Assert.Throws<HooksInstaller.HooksConfigurationException>( () => HooksInstaller.MergeHooksData( new string[] { "first", GSDPlatform.Instance.Constants.GSDHooksExecutableName }, Filename, GSDConstants.DotGit.Hooks.PreCommandHookName)); }
public void MergeHooksDataEmptyConfig() { string result = HooksInstaller.MergeHooksData(new string[] { }, Filename, GVFSConstants.DotGit.Hooks.PreCommandHookName); IEnumerable <string> resultLines = result .Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries) .Where(line => !line.StartsWith("#")); resultLines.Single().ShouldEqual(this.expectedAbsoluteGvfsHookPath); }
public void MergeHooksDataEmptyConfig() { string result = HooksInstaller.MergeHooksData(new string[] { }, Filename, GSDConstants.DotGit.Hooks.PreCommandHookName); IEnumerable<string> resultLines = result .Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries) .Where(line => !line.StartsWith("#")); resultLines.Single().ShouldEqual(GSDPlatform.Instance.Constants.GSDHooksExecutableName); }
public void MergeHooksDataDiscardBlankLines() { string result = HooksInstaller.MergeHooksData(new string[] { "first", "second", string.Empty, " " }, Filename, GVFSConstants.DotGit.Hooks.PreCommandHookName); IEnumerable <string> resultLines = result .Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries) .Where(line => !line.StartsWith("#")); resultLines.Count().ShouldEqual(3); resultLines.ElementAt(0).ShouldEqual("first"); resultLines.ElementAt(1).ShouldEqual("second"); resultLines.ElementAt(2).ShouldEqual(this.expectedAbsoluteGvfsHookPath); }
public void MergeHooksDataPostCommandFirst() { string result = HooksInstaller.MergeHooksData(new string[] { "first", "second" }, Filename, GVFSConstants.DotGit.Hooks.PostCommandHookName); IEnumerable <string> resultLines = result .Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries) .Where(line => !line.StartsWith("#")); resultLines.Count().ShouldEqual(3); resultLines.ElementAt(0).ShouldEqual(GVFSConstants.GVFSHooksExecutableName); resultLines.ElementAt(1).ShouldEqual("first"); resultLines.ElementAt(2).ShouldEqual("second"); }
private static string MergeHooks(GVFSContext context, string configSettingName, string hookName) { GitProcess configProcess = new GitProcess(context.Enlistment); string filename; string[] defaultHooksLines = { }; if (configProcess.TryGetFromConfig(configSettingName, forceOutsideEnlistment: true, value: out filename) && filename != null) { filename = filename.Trim(' ', '\n'); defaultHooksLines = File.ReadAllLines(filename); } return(HooksInstaller.MergeHooksData(defaultHooksLines, filename, hookName)); }
public void Mount(EventLevel verbosity, Keywords keywords) { this.currentState = MountState.Mounting; // We must initialize repo metadata before starting the pipe server so it // can immediately handle status requests string error; if (!RepoMetadata.TryInitialize(this.tracer, this.enlistment.DotGVFSRoot, out error)) { this.FailMountAndExit("Failed to load repo metadata: " + error); } string gitObjectsRoot; if (!RepoMetadata.Instance.TryGetGitObjectsRoot(out gitObjectsRoot, out error)) { this.FailMountAndExit("Failed to determine git objects root from repo metadata: " + error); } string localCacheRoot; if (!RepoMetadata.Instance.TryGetLocalCacheRoot(out localCacheRoot, out error)) { this.FailMountAndExit("Failed to determine local cache path from repo metadata: " + error); } string blobSizesRoot; if (!RepoMetadata.Instance.TryGetBlobSizesRoot(out blobSizesRoot, out error)) { this.FailMountAndExit("Failed to determine blob sizes root from repo metadata: " + error); } this.tracer.RelatedEvent( EventLevel.Informational, "CachePathsLoaded", new EventMetadata { { "gitObjectsRoot", gitObjectsRoot }, { "localCacheRoot", localCacheRoot }, { "blobSizesRoot", blobSizesRoot }, }); this.enlistment.InitializeCachePaths(localCacheRoot, gitObjectsRoot, blobSizesRoot); using (NamedPipeServer pipeServer = this.StartNamedPipe()) { this.tracer.RelatedEvent( EventLevel.Informational, $"{nameof(this.Mount)}_StartedNamedPipe", new EventMetadata { { "NamedPipeName", this.enlistment.NamedPipeName } }); this.context = this.CreateContext(); if (this.context.Unattended) { this.tracer.RelatedEvent(EventLevel.Critical, GVFSConstants.UnattendedEnvironmentVariable, null); } this.ValidateMountPoints(); string errorMessage; if (!HooksInstaller.TryUpdateHooks(this.context, out errorMessage)) { this.FailMountAndExit(errorMessage); } GVFSPlatform.Instance.ConfigureVisualStudio(this.enlistment.GitBinPath, this.tracer); this.MountAndStartWorkingDirectoryCallbacks(this.cacheServer); Console.Title = "GVFS " + ProcessHelper.GetCurrentProcessVersion() + " - " + this.enlistment.EnlistmentRoot; this.tracer.RelatedEvent( EventLevel.Informational, "Mount", new EventMetadata { // Use TracingConstants.MessageKey.InfoMessage rather than TracingConstants.MessageKey.CriticalMessage // as this message should not appear as an error { TracingConstants.MessageKey.InfoMessage, "Virtual repo is ready" }, }, Keywords.Telemetry); this.currentState = MountState.Ready; this.unmountEvent.WaitOne(); } }
public void MergeHooksDataThrowsOnFoundGVFSHooks() { Assert.Throws <HooksInstaller.HooksConfigurationException>( () => HooksInstaller.MergeHooksData(new string[] { "first", "gvfs.hooks.exe" }, Filename, GVFSConstants.DotGit.Hooks.PreCommandHookName)); }
protected override void Execute(GSDEnlistment enlistment) { string errorMessage = null; string mountExecutableLocation = null; using (JsonTracer tracer = new JsonTracer(GSDConstants.GSDEtwProviderName, "ExecuteMount")) { PhysicalFileSystem fileSystem = new PhysicalFileSystem(); GitRepo gitRepo = new GitRepo(tracer, enlistment, fileSystem); GSDContext context = new GSDContext(tracer, fileSystem, gitRepo, enlistment); if (!HooksInstaller.InstallHooks(context, out errorMessage)) { this.ReportErrorAndExit("Error installing hooks: " + errorMessage); } CacheServerInfo cacheServer = this.ResolvedCacheServer ?? CacheServerResolver.GetCacheServerFromConfig(enlistment); tracer.AddLogFileEventListener( GSDEnlistment.GetNewGSDLogFileName(enlistment.GSDLogsRoot, GSDConstants.LogFileTypes.MountVerb), EventLevel.Verbose, Keywords.Any); tracer.WriteStartEvent( enlistment.EnlistmentRoot, enlistment.RepoUrl, cacheServer.Url, new EventMetadata { { "Unattended", this.Unattended }, { "IsElevated", GSDPlatform.Instance.IsElevated() }, { "NamedPipeName", enlistment.NamedPipeName }, { nameof(this.EnlistmentRootPathParameter), this.EnlistmentRootPathParameter }, }); RetryConfig retryConfig = null; ServerGSDConfig serverGSDConfig = this.DownloadedGSDConfig; if (!this.SkipVersionCheck) { string authErrorMessage; if (!this.TryAuthenticate(tracer, enlistment, out authErrorMessage)) { this.Output.WriteLine(" WARNING: " + authErrorMessage); this.Output.WriteLine(" Mount will proceed, but new files cannot be accessed until GSD can authenticate."); } if (serverGSDConfig == null) { if (retryConfig == null) { retryConfig = this.GetRetryConfig(tracer, enlistment); } serverGSDConfig = this.QueryGSDConfig(tracer, enlistment, retryConfig); } this.ValidateClientVersions(tracer, enlistment, serverGSDConfig, showWarnings: true); CacheServerResolver cacheServerResolver = new CacheServerResolver(tracer, enlistment); cacheServer = cacheServerResolver.ResolveNameFromRemote(cacheServer.Url, serverGSDConfig); this.Output.WriteLine("Configured cache server: " + cacheServer); } this.InitializeLocalCacheAndObjectsPaths(tracer, enlistment, retryConfig, serverGSDConfig, cacheServer); if (!this.ShowStatusWhileRunning( () => { return(this.PerformPreMountValidation(tracer, enlistment, out mountExecutableLocation, out errorMessage)); }, "Validating repo")) { this.ReportErrorAndExit(tracer, errorMessage); } if (!this.SkipVersionCheck) { string error; if (!RepoMetadata.TryInitialize(tracer, enlistment.DotGSDRoot, out error)) { this.ReportErrorAndExit(tracer, error); } try { GitProcess git = new GitProcess(enlistment); this.LogEnlistmentInfoAndSetConfigValues(tracer, git, enlistment); } finally { RepoMetadata.Shutdown(); } } if (!this.ShowStatusWhileRunning( () => { return(this.TryMount(tracer, enlistment, mountExecutableLocation, out errorMessage)); }, "Mounting")) { this.ReportErrorAndExit(tracer, errorMessage); } if (!this.Unattended) { tracer.RelatedInfo($"{nameof(this.Execute)}: Registering for automount"); if (this.ShowStatusWhileRunning( () => { return(this.RegisterMount(enlistment, out errorMessage)); }, "Registering for automount")) { tracer.RelatedInfo($"{nameof(this.Execute)}: Registered for automount"); } else { this.Output.WriteLine(" WARNING: " + errorMessage); tracer.RelatedInfo($"{nameof(this.Execute)}: Failed to register for automount"); } } } }
private Result CreateClone( ITracer tracer, GSDEnlistment enlistment, GitObjectsHttpRequestor objectRequestor, GitRefs refs, string branch) { Result initRepoResult = this.TryInitRepo(tracer, refs, enlistment); if (!initRepoResult.Success) { return(initRepoResult); } PhysicalFileSystem fileSystem = new PhysicalFileSystem(); string errorMessage; if (!this.TryCreateAlternatesFile(fileSystem, enlistment, out errorMessage)) { return(new Result("Error configuring alternate: " + errorMessage)); } GitRepo gitRepo = new GitRepo(tracer, enlistment, fileSystem); GSDContext context = new GSDContext(tracer, fileSystem, gitRepo, enlistment); GSDGitObjects gitObjects = new GSDGitObjects(context, objectRequestor); if (!this.TryDownloadCommit( refs.GetTipCommitId(branch), enlistment, objectRequestor, gitObjects, gitRepo, out errorMessage)) { return(new Result(errorMessage)); } if (!GSDVerb.TrySetRequiredGitConfigSettings(enlistment) || !GSDVerb.TrySetOptionalGitConfigSettings(enlistment)) { return(new Result("Unable to configure git repo")); } CacheServerResolver cacheServerResolver = new CacheServerResolver(tracer, enlistment); if (!cacheServerResolver.TrySaveUrlToLocalConfig(objectRequestor.CacheServer, out errorMessage)) { return(new Result("Unable to configure cache server: " + errorMessage)); } GitProcess git = new GitProcess(enlistment); string originBranchName = "origin/" + branch; GitProcess.Result createBranchResult = git.CreateBranchWithUpstream(branch, originBranchName); if (createBranchResult.ExitCodeIsFailure) { return(new Result("Unable to create branch '" + originBranchName + "': " + createBranchResult.Errors + "\r\n" + createBranchResult.Output)); } File.WriteAllText( Path.Combine(enlistment.WorkingDirectoryBackingRoot, GSDConstants.DotGit.Head), "ref: refs/heads/" + branch); if (!this.TryDownloadRootGitAttributes(enlistment, gitObjects, gitRepo, out errorMessage)) { return(new Result(errorMessage)); } this.CreateGitScript(enlistment); string installHooksError; if (!HooksInstaller.InstallHooks(context, out installHooksError)) { tracer.RelatedError(installHooksError); return(new Result(installHooksError)); } // TODO: Move this to be after the mount? GitProcess.Result forceCheckoutResult = git.ForceCheckout(branch); if (forceCheckoutResult.ExitCodeIsFailure && forceCheckoutResult.Errors.IndexOf("unable to read tree") > 0) { // It is possible to have the above TryDownloadCommit() fail because we // already have the commit and root tree we intend to check out, but // don't have a tree further down the working directory. If we fail // checkout here, its' because we don't have these trees and the // read-object hook is not available yet. Force downloading the commit // again and retry the checkout. if (!this.TryDownloadCommit( refs.GetTipCommitId(branch), enlistment, objectRequestor, gitObjects, gitRepo, out errorMessage, checkLocalObjectCache: false)) { return(new Result(errorMessage)); } forceCheckoutResult = git.ForceCheckout(branch); } if (!RepoMetadata.TryInitialize(tracer, enlistment.DotGSDRoot, out errorMessage)) { tracer.RelatedError(errorMessage); return(new Result(errorMessage)); } try { RepoMetadata.Instance.SaveCloneMetadata(tracer, enlistment); this.LogEnlistmentInfoAndSetConfigValues(tracer, git, enlistment); } catch (Exception e) { tracer.RelatedError(e.ToString()); return(new Result(e.Message)); } finally { RepoMetadata.Shutdown(); } return(new Result(true)); }