protected async Task <IEnvironment> Initialize(NPath repoPath, NPath environmentPath = null, bool enableEnvironmentTrace = false) { TaskManager = new TaskManager(); SyncContext = new ThreadSynchronizationContext(TaskManager.Token); TaskManager.UIScheduler = new SynchronizationContextTaskScheduler(SyncContext); Environment = new IntegrationTestEnvironment(repoPath, SolutionDirectory, environmentPath, enableEnvironmentTrace); var gitSetup = new GitInstaller(Environment, TaskManager.Token); await gitSetup.SetupIfNeeded(); Environment.GitExecutablePath = gitSetup.GitExecutablePath; Platform = new Platform(Environment); GitEnvironment = Platform.GitEnvironment; ProcessManager = new ProcessManager(Environment, GitEnvironment, TaskManager.Token); Platform.Initialize(ProcessManager, TaskManager); GitClient = new GitClient(Environment, ProcessManager, TaskManager); RepositoryManager = GitHub.Unity.RepositoryManager.CreateInstance(Platform, TaskManager, GitClient, repoPath); RepositoryManager.Initialize(); Environment.Repository = new Repository("TestRepo", repoPath); Environment.Repository.Initialize(RepositoryManager); RepositoryManager.Start(); DotGitPath = repoPath.Combine(".git"); if (DotGitPath.FileExists()) { DotGitPath = DotGitPath.ReadAllLines() .Where(x => x.StartsWith("gitdir:")) .Select(x => x.Substring(7).Trim().ToNPath()) .First(); } BranchesPath = DotGitPath.Combine("refs", "heads"); RemotesPath = DotGitPath.Combine("refs", "remotes"); DotGitIndex = DotGitPath.Combine("index"); DotGitHead = DotGitPath.Combine("HEAD"); DotGitConfig = DotGitPath.Combine("config"); return(Environment); }
public void GitIsInstalledIfMissing() { var installDetails = new GitInstaller.GitInstallDetails(TestBasePath, Environment) { GitPackageFeed = $"http://localhost:{server.Port}/{GitInstaller.GitInstallDetails.GitPackageName}", }; var gitInstaller = new GitInstaller(Environment, ProcessManager, TaskManager.Token, installDetails: installDetails); var result = gitInstaller.RunSynchronously(); result.Should().NotBeNull(); var expectedInstallationPath = TestBasePath.Combine("Git"); Assert.AreEqual(expectedInstallationPath, result.GitInstallationPath); result.GitExecutablePath.Should().Be(expectedInstallationPath.Combine("cmd", "git" + Environment.ExecutableExtension)); }
public void NoLocalGit_NoDownload_DoesntThrow() { var cacheContainer = Substitute.For <ICacheContainer>(); var environment = CreateCleanEnvironment(cacheContainer, TestBasePath, true); var platform = new Platform(environment); var taskManager = InitializeTaskManager(); var processManager = new ProcessManager(environment, platform.GitEnvironment, taskManager.Token); GitInstaller.GitInstallDetails.GitPackageFeed = "fail"; var currentState = environment.GitDefaultInstallation.GetDefaults(); var gitInstaller = new GitInstaller(environment, processManager, taskManager.Token, currentState); var newState = gitInstaller.RunSynchronously(); Assert.AreEqual(currentState, newState); }
protected void SetupGit(NPath pathToSetupGitInto, string testName) { var autoResetEvent = new AutoResetEvent(false); var installDetails = new GitInstaller.GitInstallDetails(pathToSetupGitInto, Environment.IsWindows); var zipArchivesPath = pathToSetupGitInto.Combine("downloads").CreateDirectory(); Logger.Trace($"Saving git zips into {zipArchivesPath} and unzipping to {pathToSetupGitInto}"); AssemblyResources.ToFile(ResourceType.Platform, "git.zip", zipArchivesPath, Environment); AssemblyResources.ToFile(ResourceType.Platform, "git.zip.md5", zipArchivesPath, Environment); AssemblyResources.ToFile(ResourceType.Platform, "git-lfs.zip", zipArchivesPath, Environment); AssemblyResources.ToFile(ResourceType.Platform, "git-lfs.zip.md5", zipArchivesPath, Environment); var gitInstaller = new GitInstaller(Environment, ProcessManager, TaskManager, installDetails); NPath? result = null; Exception ex = null; var setupTask = gitInstaller.SetupGitIfNeeded().Finally((success, state) => { result = state.GitExecutablePath; autoResetEvent.Set(); }); setupTask.Start(); if (!autoResetEvent.WaitOne(TimeSpan.FromMinutes(5))) { throw new TimeoutException($"Test setup unzipping {zipArchivesPath} to {pathToSetupGitInto} timed out"); } if (result == null) { if (ex != null) { throw ex; } throw new Exception("Did not install git"); } Environment.GitExecutablePath = result.Value; GitClient = new GitClient(Environment, ProcessManager, TaskManager.Token); }
//[Test] public void GitInstallMac() { var filesystem = Substitute.For <IFileSystem>(); DefaultEnvironment.OnMac = true; DefaultEnvironment.OnWindows = false; var gitInstallationPath = TestBasePath.Combine("GitInstall").CreateDirectory(); var installDetails = new GitInstaller.GitInstallDetails(gitInstallationPath, Environment.IsWindows) { GitZipMd5Url = $"http://localhost:{server.Port}/{new Uri(GitInstaller.GitInstallDetails.DefaultGitZipMd5Url).AbsolutePath}", GitZipUrl = $"http://localhost:{server.Port}/{new Uri(GitInstaller.GitInstallDetails.DefaultGitZipUrl).AbsolutePath}", GitLfsZipMd5Url = $"http://localhost:{server.Port}/{new Uri(GitInstaller.GitInstallDetails.DefaultGitLfsZipMd5Url).AbsolutePath}", GitLfsZipUrl = $"http://localhost:{server.Port}/{new Uri(GitInstaller.GitInstallDetails.DefaultGitLfsZipUrl).AbsolutePath}", }; TestBasePath.Combine("git").CreateDirectory(); var gitInstaller = new GitInstaller(Environment, ProcessManager, TaskManager, installDetails); var startTask = gitInstaller.SetupGitIfNeeded(); var endTask = new FuncTask <GitInstaller.GitInstallationState, GitInstaller.GitInstallationState>(TaskManager.Token, (s, state) => state); startTask.OnEnd += (thisTask, path, success, exception) => thisTask.GetEndOfChain().Then(endTask); startTask.Start(); GitInstaller.GitInstallationState result = null; Assert.DoesNotThrow(async() => result = await endTask.Task); result.Should().NotBeNull(); Assert.AreEqual(gitInstallationPath.Combine(installDetails.PackageNameWithVersion), result.GitInstallationPath); result.GitExecutablePath.Should().Be(gitInstallationPath.Combine("bin", "git" + Environment.ExecutableExtension)); result.GitLfsExecutablePath.Should().Be(gitInstallationPath.Combine(installDetails.PackageNameWithVersion, "libexec", "git-core", "git-lfs" + Environment.ExecutableExtension)); var isCustomGitExec = result.GitExecutablePath != result.GitExecutablePath; Environment.GitExecutablePath = result.GitExecutablePath; Environment.GitLfsExecutablePath = result.GitLfsExecutablePath; Environment.IsCustomGitExecutable = isCustomGitExec; var procTask = new SimpleProcessTask(TaskManager.Token, "something") .Configure(ProcessManager); procTask.Process.StartInfo.EnvironmentVariables["PATH"].Should().StartWith(gitInstallationPath.ToString()); }
public void GitIsInstalledIfMissing() { using (var test = StartTest(withHttpServer: true)) { var manifestFeed = $"http://localhost:{test.HttpServer.Port}/git/{GitInstaller.GitInstallDetails.ManifestName}"; var installDetails = new GitInstaller.GitInstallDetails(test.TestPath, test.Environment) { GitManifestFeed = manifestFeed }; var gitInstaller = new GitInstaller(test.Platform, installDetails: installDetails); var result = gitInstaller.RunSynchronously(); result.Should().NotBeNull(); var expectedInstallationPath = test.TestPath.Combine("Git"); Assert.AreEqual(expectedInstallationPath, result.GitInstallationPath); result.GitExecutablePath.Should().Be(expectedInstallationPath.Combine("cmd", "git" + test.Environment.ExecutableExtension)); } }
// force update given or all registered clones // @handled @logs public static void Update(PyRevitClone clone) { // current user config logger.Debug("Updating pyRevit clone \"{0}\"", clone.Name); if (clone.IsRepoDeploy) { var res = GitInstaller.ForcedUpdate(clone.ClonePath); if (res <= UpdateStatus.Conflicts) { throw new PyRevitException(string.Format("Error updating clone \"{0}\"", clone.Name)); } } else { // re-deploying is how the no-git clones get updated ReDeployClone(clone); } }
public void GitInstallTest() { var gitInstallationPath = TestBasePath.Combine("GitInstall").CreateDirectory(); var installDetails = new GitInstallDetails(gitInstallationPath, DefaultEnvironment.OnWindows) { GitZipMd5Url = $"http://localhost:{server.Port}/{new UriString(GitInstallDetails.DefaultGitZipMd5Url).Filename}", GitZipUrl = $"http://localhost:{server.Port}/{new UriString(GitInstallDetails.DefaultGitZipUrl).Filename}", GitLfsZipMd5Url = $"http://localhost:{server.Port}/{new UriString(GitInstallDetails.DefaultGitLfsZipMd5Url).Filename}", GitLfsZipUrl = $"http://localhost:{server.Port}/{new UriString(GitInstallDetails.DefaultGitLfsZipUrl).Filename}", }; TestBasePath.Combine("git").CreateDirectory(); //var gitArchivePath = AssemblyResources.ToFile(ResourceType.Platform, "git.zip", zipArchivesPath, Environment); //var gitLfsArchivePath = AssemblyResources.ToFile(ResourceType.Platform, "git-lfs.zip", zipArchivesPath, Environment); var gitInstaller = new GitInstaller(Environment, CancellationToken.None, installDetails); var autoResetEvent = new AutoResetEvent(false); bool? result = null; NPath resultPath = null; Exception ex = null; gitInstaller.SetupGitIfNeeded(new ActionTask <NPath>(CancellationToken.None, (b, path) => { result = true; resultPath = path; autoResetEvent.Set(); }), new ActionTask(CancellationToken.None, (b, exception) => { result = false; ex = exception; autoResetEvent.Set(); })); autoResetEvent.WaitOne(); result.HasValue.Should().BeTrue(); result.Value.Should().BeTrue(); resultPath.Should().NotBeNull(); ex.Should().BeNull(); }
public void NoLocalGit_NoDownload_DoesntThrow() { var cacheContainer = Substitute.For <ICacheContainer>(); using (var test = StartTest(cacheContainer: cacheContainer)) { var manifestFeed = "fail"; var currentState = test.Environment.GitDefaultInstallation.GetDefaults(); var gitInstaller = new GitInstaller(test.Platform, currentState, new GitInstaller.GitInstallDetails(test.TestPath, test.Environment) { GitManifestFeed = manifestFeed } ); var newState = gitInstaller.RunSynchronously(); Assert.AreEqual(currentState, newState); } }
protected void SetupGit(NPath pathToSetupGitInto, string testName) { var installDetails = new GitInstaller.GitInstallDetails(pathToSetupGitInto, Environment.IsWindows); var gitInstaller = new GitInstaller(Environment, ProcessManager, TaskManager.Token, installDetails); var state = gitInstaller.SetDefaultPaths(new GitInstaller.GitInstallationState()); Environment.GitInstallationState = state; GitClient = new GitClient(Environment, ProcessManager, TaskManager.Token); if (installDetails.GitExecutablePath.FileExists() && installDetails.GitLfsExecutablePath.FileExists()) { return; } installDetails.GitInstallationPath.DeleteIfExists(); installDetails.GitZipPath.EnsureParentDirectoryExists(); installDetails.GitLfsZipPath.EnsureParentDirectoryExists(); AssemblyResources.ToFile(ResourceType.Platform, "git.zip", installDetails.GitZipPath.Parent, Environment); AssemblyResources.ToFile(ResourceType.Platform, "git.json", installDetails.GitZipPath.Parent, Environment); AssemblyResources.ToFile(ResourceType.Platform, "git-lfs.zip", installDetails.GitZipPath.Parent, Environment); AssemblyResources.ToFile(ResourceType.Platform, "git-lfs.json", installDetails.GitZipPath.Parent, Environment); var tempZipExtractPath = TestBasePath.Combine("setup", "git_zip_extract_zip_paths").EnsureDirectoryExists(); var extractPath = tempZipExtractPath.Combine("git").CreateDirectory(); var path = new UnzipTask(TaskManager.Token, installDetails.GitZipPath, extractPath, null, Environment.FileSystem) .Catch(e => true) .RunSynchronously(); var source = path; installDetails.GitInstallationPath.EnsureParentDirectoryExists(); source.Move(installDetails.GitInstallationPath); extractPath = tempZipExtractPath.Combine("git-lfs").CreateDirectory(); path = new UnzipTask(TaskManager.Token, installDetails.GitLfsZipPath, extractPath, null, Environment.FileSystem) .Catch(e => true) .RunSynchronously(); installDetails.GitLfsInstallationPath.EnsureParentDirectoryExists(); path.Move(installDetails.GitLfsInstallationPath); }
//[Test] public void MacSkipsInstallWhenSettingsGitExists() { DefaultEnvironment.OnMac = true; DefaultEnvironment.OnWindows = false; var filesystem = Substitute.For <IFileSystem>(); filesystem.FileExists(Arg.Any <string>()).Returns(true); filesystem.DirectoryExists(Arg.Any <string>()).Returns(true); filesystem.DirectorySeparatorChar.Returns('/'); Environment.FileSystem = filesystem; var gitInstallationPath = "/usr/local".ToNPath(); var gitExecutablePath = gitInstallationPath.Combine("bin/git"); var gitLfsInstallationPath = gitInstallationPath; var gitLfsExecutablePath = gitLfsInstallationPath.Combine("bin/git-lfs"); var installDetails = new GitInstaller.GitInstallDetails(gitInstallationPath, Environment.IsWindows) { GitPackageFeed = $"http://localhost:{server.Port}/unity/git/mac/{GitInstaller.GitInstallDetails.GitPackageName}", GitLfsPackageFeed = $"http://localhost:{server.Port}/unity/git/mac/{GitInstaller.GitInstallDetails.GitLfsPackageName}", }; var ret = new string[] { gitLfsExecutablePath }; filesystem.GetFiles(Arg.Any <string>(), Arg.Is <string>(installDetails.GitLfsExecutablePath.FileName), Arg.Any <SearchOption>()) .Returns(ret); var settings = Substitute.For <ISettings>(); var settingsRet = gitExecutablePath.ToString(); settings.Get(Arg.Is <string>(Constants.GitInstallPathKey), Arg.Any <string>()).Returns(settingsRet); var installer = new GitInstaller(Environment, ProcessManager, TaskManager.Token, installDetails); var result = installer.SetupGitIfNeeded(); Assert.AreEqual(gitInstallationPath, result.GitInstallationPath); Assert.AreEqual(gitLfsInstallationPath, result.GitLfsInstallationPath); Assert.AreEqual(gitExecutablePath, result.GitExecutablePath); Assert.AreEqual(gitLfsExecutablePath, result.GitLfsExecutablePath); }
public void GitInstallWindows() { var gitInstallationPath = TestBasePath.Combine("GitInstall").CreateDirectory(); var installDetails = new GitInstaller.GitInstallDetails(gitInstallationPath, DefaultEnvironment.OnWindows) { GitPackageFeed = $"http://localhost:{server.Port}/unity/git/windows/{GitInstaller.GitInstallDetails.GitPackageName}", GitLfsPackageFeed = $"http://localhost:{server.Port}/unity/git/windows/{GitInstaller.GitInstallDetails.GitLfsPackageName}", }; TestBasePath.Combine("git").CreateDirectory(); var zipHelper = Substitute.For <IZipHelper>(); zipHelper.Extract(Arg.Any <string>(), Arg.Do <string>(x => { var n = x.ToNPath(); n.EnsureDirectoryExists(); if (n.FileName == "git-lfs") { n.Combine("git-lfs" + Environment.ExecutableExtension).WriteAllText(""); } }), Arg.Any <CancellationToken>(), Arg.Any <Func <long, long, bool> >()).Returns(true); ZipHelper.Instance = zipHelper; var gitInstaller = new GitInstaller(Environment, ProcessManager, TaskManager.Token, installDetails: installDetails); var state = gitInstaller.SetupGitIfNeeded(); state.Should().NotBeNull(); Assert.AreEqual(gitInstallationPath.Combine(GitInstaller.GitInstallDetails.GitDirectory), state.GitInstallationPath); state.GitExecutablePath.Should().Be(gitInstallationPath.Combine(GitInstaller.GitInstallDetails.GitDirectory, "cmd", "git" + Environment.ExecutableExtension)); state.GitLfsExecutablePath.Should().Be(gitInstallationPath.Combine(GitInstaller.GitInstallDetails.GitLfsDirectory, "git-lfs" + Environment.ExecutableExtension)); Environment.GitInstallationState = state; var procTask = new SimpleProcessTask(TaskManager.Token, "something") .Configure(ProcessManager); procTask.Process.StartInfo.EnvironmentVariables["PATH"].Should().StartWith(gitInstallationPath.ToString()); }
protected async Task InitializePlatform(NPath repoPath, NPath environmentPath, bool enableEnvironmentTrace) { InitializeTaskManager(); CacheContainer = Substitute.For <ICacheContainer>(); Environment = new IntegrationTestEnvironment(CacheContainer, repoPath, SolutionDirectory, environmentPath, enableEnvironmentTrace); var gitSetup = new GitInstaller(Environment, TaskManager.Token); await gitSetup.SetupIfNeeded(); Environment.GitExecutablePath = gitSetup.GitExecutablePath; Platform = new Platform(Environment); GitEnvironment = Platform.GitEnvironment; ProcessManager = new ProcessManager(Environment, GitEnvironment, TaskManager.Token); Platform.Initialize(ProcessManager, TaskManager); GitClient = new GitClient(Environment, ProcessManager, TaskManager); }
public void GitLfsIsInstalledIfMissing() { var tempZipExtractPath = TestBasePath.Combine("Temp", "git_zip_extract_zip_paths"); var gitInstallationPath = TestBasePath.Combine("GitInstall").Combine(GitInstaller.GitInstallDetails.GitDirectory); var gitLfsInstallationPath = TestBasePath.Combine("GitInstall").Combine(GitInstaller.GitInstallDetails.GitLfsDirectory); var gitZipUri = new UriString($"http://localhost:{server.Port}/unity/git/windows/git-slim.zip"); var downloader = new Downloader(Environment.FileSystem); downloader.QueueDownload(gitZipUri, tempZipExtractPath); downloader.RunSynchronously(); var gitExtractPath = tempZipExtractPath.Combine("git").CreateDirectory(); ZipHelper.Instance.Extract(tempZipExtractPath.Combine(gitZipUri.Filename), gitExtractPath, TaskManager.Token, null); var source = gitExtractPath; var target = gitInstallationPath; target.DeleteIfExists(); target.EnsureParentDirectoryExists(); source.Move(target); var installDetails = new GitInstaller.GitInstallDetails(gitInstallationPath.Parent, DefaultEnvironment.OnWindows) { GitPackageFeed = "http://nope", GitLfsPackageFeed = $"http://localhost:{server.Port}/unity/git/windows/{GitInstaller.GitInstallDetails.GitLfsPackageName}", }; var gitInstaller = new GitInstaller(Environment, ProcessManager, TaskManager.Token, installDetails: installDetails); var result = gitInstaller.SetupGitIfNeeded(); result.Should().NotBeNull(); Assert.AreEqual(gitInstallationPath, result.GitInstallationPath); result.GitExecutablePath.Should().Be(gitInstallationPath.Combine("cmd", "git" + Environment.ExecutableExtension)); result.GitLfsExecutablePath.Should().Be(gitLfsInstallationPath.Combine("git-lfs" + Environment.ExecutableExtension)); }
// get origin remote url // @handled @logs public static string GetOrigin(string clonePath) { VerifyCloneValidity(clonePath); return(GitInstaller.GetRemoteUrl(clonePath, PyRevitConsts.DefaultCloneRemoteName)); }
// get checkedout branch in git repo // @handled @logs public static string GetCommit(string clonePath) { VerifyCloneValidity(clonePath); return(GitInstaller.GetHeadCommit(clonePath)); }
// get checkedout branch in git repo // @handled @logs public static string GetBranch(string clonePath) { VerifyCloneValidity(clonePath); return(GitInstaller.GetCheckedoutBranch(clonePath)); }
// install pyRevit by cloning from git repo // @handled @logs public static void DeployFromRepo(string cloneName, string deploymentName = null, string branchName = null, string repoUrl = null, string destPath = null, string username = null, string password = null) { string repoSourcePath = repoUrl ?? PyRevitLabsConsts.OriginalRepoGitPath; string repoBranch = branchName != null ? branchName : PyRevitLabsConsts.TragetBranch; logger.Debug("Repo source determined as \"{0}:{1}\"", repoSourcePath, repoBranch); // determine destination path if not provided if (destPath is null) { destPath = Path.Combine(PyRevitLabsConsts.PyRevitPath, PyRevitConsts.DefaultCloneInstallName); } logger.Debug("Destination path determined as \"{0}\"", destPath); // make sure destPath exists CommonUtils.EnsurePath(destPath); // check existing destination path if (CommonUtils.VerifyPath(destPath)) { logger.Debug("Destination path already exists {0}", destPath); destPath = Path.Combine(destPath, cloneName); logger.Debug("Using subpath {0}", destPath); if (CommonUtils.VerifyPath(destPath)) { throw new PyRevitException(string.Format("Destination path already exists \"{0}\"", destPath)); } } // start the clone process LibGit2Sharp.Repository repo = null; if (deploymentName != null) { // TODO: Add core checkout option. Figure out how to checkout certain folders in libgit2sharp throw new NotImplementedException("Deployment with git clones not implemented yet."); } else { repo = GitInstaller.Clone(repoSourcePath, repoBranch, destPath, username, password); } // Check installation if (repo != null) { // make sure to delete the repo if error occured after cloning var clonedPath = repo.Info.WorkingDirectory; try { PyRevitClone.VerifyCloneValidity(clonedPath); logger.Debug("Clone successful \"{0}\"", clonedPath); RegisterClone(cloneName, clonedPath); } catch (Exception ex) { logger.Debug(string.Format("Exception occured after clone complete. Deleting clone \"{0}\" | {1}", clonedPath, ex.Message)); try { CommonUtils.DeleteDirectory(clonedPath); } catch (Exception delEx) { logger.Error(string.Format("Error post-install cleanup on \"{0}\" | {1}", clonedPath, delEx.Message)); } // cleanup completed, now baloon up the exception throw ex; } } else { throw new PyRevitException(string.Format("Error installing pyRevit. Null repo error on \"{0}\"", repoUrl)); } }
// get origin remote url // @handled @logs public static string GetOrigin(string installPath) { return(GitInstaller.GetRemoteUrl(installPath, PyRevitConsts.DefaultExtensionRemoteName)); }
// installs extension from repo url // @handled @logs public static void InstallExtension(string extensionName, PyRevitExtensionTypes extensionType, string repoPath, string destPath = null, string branchName = null, string username = null, string password = null) { // make sure extension is not installed already try { var existExt = GetInstalledExtension(extensionName); if (existExt != null) { throw new PyRevitException(string.Format("Extension \"{0}\" is already installed under \"{1}\"", existExt.Name, existExt.InstallPath)); } } catch { // extension is not installed so everything is fine } // determine repo folder name // Name.extension for UI Extensions // Name.lib for Library Extensions string extDestDirName = PyRevitExtension.MakeConfigName(extensionName, extensionType); // determine destination destPath = destPath ?? PyRevitConsts.DefaultExtensionsPath; string finalExtRepoPath = Path.Combine(destPath, extDestDirName).NormalizeAsPath(); // determine branch name branchName = branchName ?? PyRevitConsts.DefaultExtensionRepoDefaultBranch; logger.Debug("Extension branch name determined as \"{0}\"", branchName); logger.Debug("Installing extension into \"{0}\"", finalExtRepoPath); // start the clone process var repo = GitInstaller.Clone(repoPath, branchName, finalExtRepoPath, username, password); // Check installation if (repo != null) { // make sure to delete the repo if error occured after cloning var clonedPath = repo.Info.WorkingDirectory; if (GitInstaller.IsValidRepo(clonedPath)) { logger.Debug("Clone successful \"{0}\"", clonedPath); RegisterExtensionSearchPath(destPath.NormalizeAsPath()); } else { logger.Debug("Invalid repo after cloning. Deleting clone \"{0}\"", repoPath); try { CommonUtils.DeleteDirectory(repoPath); } catch (Exception delEx) { logger.Error(string.Format("Error post-install cleanup on \"{0}\" | {1}", repoPath, delEx.Message)); } } } else { throw new PyRevitException(string.Format("Error installing extension. Null repo error on \"{0}\"", repoPath)); } }