public Local(ILogger logger, string overrideRootPath = null) { _repo = overrideRootPath ?? LocalHelpers.GetRootDir(GitExecutable, logger); _logger = logger; _gitClient = new LocalGitClient(GitExecutable, _logger); _fileManager = new GitFileManager(_gitClient, _logger); }
/// <summary> /// Updates local copies of the files. /// </summary> /// <param name="filesToCommit">Files to update locally</param> /// <param name="repoUri">Base path of the repo</param> /// <param name="branch">Unused</param> /// <param name="commitMessage">Unused</param> /// <returns></returns> public async Task CommitFilesAsync(List <GitFile> filesToCommit, string repoUri, string branch, string commitMessage) { string repoDir = LocalHelpers.GetRootDir(_gitExecutable, _logger); try { using (LibGit2Sharp.Repository localRepo = new LibGit2Sharp.Repository(repoDir)) { foreach (GitFile file in filesToCommit) { switch (file.Operation) { case GitFileOperation.Add: string parentDirectory = Directory.GetParent(file.FilePath).FullName; if (!Directory.Exists(parentDirectory)) { Directory.CreateDirectory(parentDirectory); } string fullPath = Path.Combine(repoUri, file.FilePath); using (var streamWriter = new StreamWriter(fullPath)) { string finalContent; switch (file.ContentEncoding) { case ContentEncoding.Utf8: finalContent = file.Content; break; case ContentEncoding.Base64: byte[] bytes = Convert.FromBase64String(file.Content); finalContent = Encoding.UTF8.GetString(bytes); break; default: throw new DarcException($"Unknown file content encoding {file.ContentEncoding}"); } finalContent = NormalizeLineEndings(fullPath, finalContent); await streamWriter.WriteAsync(finalContent); LibGit2SharpHelpers.AddFileToIndex(localRepo, file, fullPath, _logger); } break; case GitFileOperation.Delete: if (File.Exists(file.FilePath)) { File.Delete(file.FilePath); } break; } } } } catch (Exception exc) { throw new DarcException($"Something went wrong when checking out {repoUri} in {repoDir}", exc); } }
public override async Task <int> ExecuteAsync() { DependencyType type = _options.Type.ToLower() == "toolset" ? DependencyType.Toolset : DependencyType.Product; Local local = new Local(LocalHelpers.GetGitDir(Logger), Logger); DependencyDetail dependency = new DependencyDetail { Name = _options.Name, Version = _options.Version ?? string.Empty, RepoUri = _options.RepoUri ?? string.Empty, Commit = _options.Commit ?? string.Empty }; try { await local.AddDependencyAsync(dependency, type); return(Constants.SuccessCode); } catch (Exception exc) { Logger.LogError(exc, $"Failed to add dependency '{dependency.Name}' to repository."); return(Constants.ErrorCode); } }
public override async Task <int> ExecuteAsync() { try { Local local = new Local(LocalHelpers.GetGitDir(Logger), Logger); IEnumerable <DependencyDetail> dependencies = await local.GetDependenciesAsync( _options.AssetName); DarcSettings darcSettings = null; if (_options.Remote) { if (string.IsNullOrEmpty(_options.RepoUri)) { Logger.LogError("If '--remote' is set '--repo-uri' is required."); return(Constants.ErrorCode); } darcSettings = LocalSettings.GetDarcSettings( _options, Logger, _options.RepoUri); Remote remote = new Remote(darcSettings, Logger); dependencies = await remote.GetDependenciesAsync( _options.RepoUri, _options.Branch, _options.AssetName); } List <DependencyGraph> graph = await CreateGraphAsync(dependencies, darcSettings); if (graph == null) { return(Constants.ErrorCode); } if (_options.Flat) { LogFlatDependencyGraph(graph); } else { LogDependencyGraph(graph); } return(Constants.SuccessCode); } catch (Exception exc) { Logger.LogError(exc, "Something failed while getting the dependency graph."); return(Constants.ErrorCode); } }
private static string GetRepoPath( string repoUri, string commit, IEnumerable <string> remotesMap, string reposFolder, ILogger logger, string gitExecutable) { string repoPath = null; if (remotesMap != null) { if (_remotesMapping == null) { _remotesMapping = CreateRemotesMapping(remotesMap); } if (!_remotesMapping.ContainsKey(repoPath)) { throw new DarcException($"A key matching '{repoUri}' was not " + $"found in the mapping. Please make sure to include it..."); } repoPath = _remotesMapping[repoPath]; } else { string folder = null; if (!string.IsNullOrEmpty(reposFolder)) { folder = reposFolder; } else { // If a repo folder or a mapping was not set we use the current parent's // parent folder. string parent = LocalHelpers.GetRootDir(gitExecutable, logger); folder = Directory.GetParent(parent).FullName; } repoPath = LocalHelpers.GetRepoPathFromFolder(gitExecutable, folder, commit, logger); if (string.IsNullOrEmpty(repoPath)) { throw new DarcException($"Commit '{commit}' was not found in any " + $"folder in '{folder}'. Make sure a folder for '{repoUri}' exists " + $"and it has all the latest changes..."); } } return(repoPath); }
public JsonResult Delete(int id) { try { core.DeleteProducto(id); LocalHelpers.ShowDataDeleteSuccessMessage(); return(Json(new { result = "Ok" }, JsonRequestBehavior.AllowGet)); } catch (Exception ex) { LocalHelpers.ShowMessage("Ocurrio el siguiente error: " + ex, MessageType.Error); return(Json(new { result = string.Format("Err:{0}", ex.Message) }, JsonRequestBehavior.AllowGet)); } }
public JsonResult Delete(String tipoDocumento, String numeroDocumento) { try { core.DeleteRestaurante(tipoDocumento, numeroDocumento); LocalHelpers.ShowDataDeleteSuccessMessage(); return(Json(new { result = "Ok" }, JsonRequestBehavior.AllowGet)); } catch (Exception ex) { LocalHelpers.ShowMessage("Ocurrio el siguiente error: " + ex, MessageType.Error); return(Json(new { result = string.Format("Err:{0}", ex.Message) }, JsonRequestBehavior.AllowGet)); } }
public Task <List <GitFile> > GetFilesAtCommitAsync(string repoUri, string commit, string path) { string repoDir = LocalHelpers.GetRootDir(_gitExecutable, _logger); string sourceFolder = Path.Combine(repoDir, path); return(Task.Run(() => Directory.GetFiles( sourceFolder, "*.*", SearchOption.AllDirectories).Select( file => { return new GitFile( file.Remove(0, repoDir.Length + 1).Replace("\\", "/"), File.ReadAllText(file) ); } ).ToList())); }
public override async Task <int> ExecuteAsync() { Local local = new Local(LocalHelpers.GetGitDir(Logger), Logger); try { IEnumerable <DependencyDetail> dependencies = await local.GetDependenciesAsync(_options.Name); if (!string.IsNullOrEmpty(_options.Name)) { DependencyDetail dependency = dependencies.Where(d => d.Name.Equals(_options.Name, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault(); if (dependency == null) { throw new Exception($"A dependency with name '{_options.Name}' was not found..."); } LogDependency(dependency); } foreach (DependencyDetail dependency in dependencies) { LogDependency(dependency); Console.WriteLine(); } return(Constants.SuccessCode); } catch (Exception exc) { if (!string.IsNullOrEmpty(_options.Name)) { Logger.LogError(exc, $"Something failed while querying for local dependency '{_options.Name}'."); } else { Logger.LogError(exc, "Something failed while querying for local dependencies."); } return(Constants.ErrorCode); } }
/// <summary> /// Verify that the repository has a correct dependency structure. /// </summary> /// <param name="options">Command line options</param> /// <returns>Process exit code.</returns> public override async Task <int> ExecuteAsync() { Local local = new Local(LocalHelpers.GetGitDir(Logger), Logger); try { if (!(await local.Verify())) { Console.WriteLine("Dependency verification failed."); return(Constants.ErrorCode); } Console.WriteLine("Dependency verification succeeded."); return(Constants.SuccessCode); } catch (Exception e) { Logger.LogError(e, "Error: Failed to verify repository dependency state."); return(Constants.ErrorCode); } }
public ActionResult Edit(ProductoModel model) { try { if (model.ProductoId > 0) { //core.ModifyProducto(model); LocalHelpers.ShowDataUpdateSuccessMessage(); } else { core.AddProducto(model); LocalHelpers.ShowDataInsertSuccessMessage(); } } catch (Exception ex) { LocalHelpers.ShowMessage(ex.Message, MessageType.Error); } return(RedirectToAction("Index")); }
/// <summary> /// Normalize line endings of content. /// </summary> /// <param name="filePath">Path of file</param> /// <param name="content">Content to normalize</param> /// <returns>Normalized content</returns> /// <remarks> /// Normalize based on the following rules: /// - Auto CRLF is assumed. /// - Check the git attributes the file to determine whether it has a specific setting for the file. If so, use that. /// - If no setting, or if auto, then determine whether incoming content differs in line ends vs. the /// OS setting, and replace if needed. /// </remarks> private string NormalizeLineEndings(string filePath, string content) { const string crlf = "\r\n"; const string lf = "\n"; // Check gitAttributes to determine whether the file has eof handling set. string eofAttr = LocalHelpers.ExecuteCommand(_gitExecutable, $"check-attr eol -- {filePath}", _logger); if (string.IsNullOrEmpty(eofAttr) || eofAttr.Contains("eol: unspecified") || eofAttr.Contains("eol: auto")) { if (Environment.NewLine != crlf) { return(content.Replace(crlf, Environment.NewLine)); } else if (Environment.NewLine == crlf && !content.Contains(crlf)) { return(content.Replace(lf, Environment.NewLine)); } } else if (eofAttr.Contains("eol: crlf")) { // Test to avoid adding extra \r. if (!content.Contains(crlf)) { return(content.Replace(lf, crlf)); } } else if (eofAttr.Contains("eol: lf")) { return(content.Replace(crlf, lf)); } else { throw new DarcException($"Unknown eof setting '{eofAttr}' for file '{filePath};"); } return(content); }
public override async Task <int> ExecuteAsync() { try { IEnumerable <DependencyDetail> rootDependencies = null; DependencyGraph graph; RemoteFactory remoteFactory = new RemoteFactory(_options); if (!_options.Local) { NodeDiff diffOption = NodeDiff.None; // Check node diff options switch (_options.DeltaFrom.ToLowerInvariant()) { case "none": break; case "newest-in-channel": diffOption = NodeDiff.LatestInChannel; break; case "newest-in-graph": diffOption = NodeDiff.LatestInGraph; break; default: Console.WriteLine("Unknown --delta-from option, please see help."); return(Constants.ErrorCode); } // If the repo uri and version are set, then call the graph // build operation based on those. Both should be set in this case. // If they are not set, then gather the initial set based on the local repository, // and then call the graph build with that root set. if (!string.IsNullOrEmpty(_options.RepoUri)) { if (string.IsNullOrEmpty(_options.Version)) { Console.WriteLine("If --repo is set, --version should be supplied"); return(Constants.ErrorCode); } Console.WriteLine($"Getting root dependencies from {_options.RepoUri}@{_options.Version}..."); // Grab root dependency set. The graph build can do this, but // if an original asset name is passed, then this will do the initial filtering. IRemote rootRepoRemote = await remoteFactory.GetRemoteAsync(_options.RepoUri, Logger); rootDependencies = await rootRepoRemote.GetDependenciesAsync( _options.RepoUri, _options.Version, _options.AssetName); } else { if (!string.IsNullOrEmpty(_options.Version)) { Console.WriteLine("If --version is supplied, then --repo is required"); return(Constants.ErrorCode); } Console.WriteLine($"Getting root dependencies from local repository..."); // Grab root dependency set from local repo Local local = new Local(Logger); rootDependencies = await local.GetDependenciesAsync( _options.AssetName); } Console.WriteLine($"Building repository dependency graph..."); rootDependencies = FilterToolsetDependencies(rootDependencies); if (!rootDependencies.Any()) { Console.WriteLine($"No root dependencies found, exiting."); return(Constants.ErrorCode); } DependencyGraphBuildOptions graphBuildOptions = new DependencyGraphBuildOptions() { IncludeToolset = _options.IncludeToolset, LookupBuilds = diffOption != NodeDiff.None || !_options.SkipBuildLookup, NodeDiff = diffOption }; // Build graph graph = await DependencyGraph.BuildRemoteDependencyGraphAsync( remoteFactory, rootDependencies, _options.RepoUri ?? LocalHelpers.GetRootDir(Logger), _options.Version ?? LocalHelpers.GetGitCommit(Logger), graphBuildOptions, Logger); } else { Console.WriteLine($"Getting root dependencies from local repository..."); Local local = new Local(Logger); rootDependencies = await local.GetDependenciesAsync( _options.AssetName); rootDependencies = FilterToolsetDependencies(rootDependencies); if (!rootDependencies.Any()) { Console.WriteLine($"No root dependencies found, exiting."); return(Constants.ErrorCode); } Console.WriteLine($"Building repository dependency graph from local information..."); DependencyGraphBuildOptions graphBuildOptions = new DependencyGraphBuildOptions() { IncludeToolset = _options.IncludeToolset, LookupBuilds = false, NodeDiff = NodeDiff.None }; // Build graph using only local resources graph = await DependencyGraph.BuildLocalDependencyGraphAsync( rootDependencies, graphBuildOptions, Logger, LocalHelpers.GetRootDir(Logger), LocalHelpers.GetGitCommit(Logger), _options.ReposFolder, _options.RemotesMap); } if (_options.Flat) { await LogFlatDependencyGraph(graph); } else { await LogDependencyGraph(graph); } if (!string.IsNullOrEmpty(_options.GraphVizOutputFile)) { await LogGraphViz(graph); } return(Constants.SuccessCode); } catch (Exception exc) { Logger.LogError(exc, "Something failed while getting the dependency graph."); return(Constants.ErrorCode); } }
public UxManager(string gitLocation, ILogger logger) { _editorPath = LocalHelpers.GetEditorPath(gitLocation, logger); _rootDir = LocalHelpers.GetRootDir(gitLocation, logger); _logger = logger; }
/// <summary> /// Cloning big repos takes a considerable amount of time when checking out the files. When /// working on batched subscription, the operation could take more than an hour causing the /// GitHub token to expire. By doing sparse and shallow checkout, we only deal with the files /// we need avoiding to check the complete repo shaving time from the overall push process /// </summary> /// <param name="filesToCommit">Collection of files to update.</param> /// <param name="repoUri">The repository to push the files to.</param> /// <param name="branch">The branch to push the files to.</param> /// <param name="commitMessage">The commmit message.</param> /// <returns></returns> protected async Task CommitFilesAsync( List <GitFile> filesToCommit, string repoUri, string branch, string commitMessage, ILogger logger, string pat, string dotnetMaestroName, string dotnetMaestroEmail) { logger.LogInformation("Pushing files to {branch}", branch); string tempRepoFolder = Path.Combine(TemporaryRepositoryPath, Path.GetRandomFileName()); string remote = "origin"; try { string clonedRepo = null; logger.LogInformation("Sparse and shallow checkout of branch {branch} in {repoUri}...", branch, repoUri); clonedRepo = LocalHelpers.SparseAndShallowCheckout(GitExecutable, repoUri, branch, tempRepoFolder, logger, remote, dotnetMaestroName, dotnetMaestroEmail, pat); foreach (GitFile file in filesToCommit) { string filePath = Path.Combine(clonedRepo, file.FilePath); if (file.Operation == GitFileOperation.Add) { if (!File.Exists(filePath)) { string parentFolder = Directory.GetParent(filePath).FullName; Directory.CreateDirectory(parentFolder); } using (FileStream stream = File.Create(filePath)) { byte[] contentBytes = GetUtf8ContentBytes(file.Content, file.ContentEncoding); await stream.WriteAsync(contentBytes, 0, contentBytes.Length); } } else if (file.Operation == GitFileOperation.Delete) { File.Delete(filePath); } LocalHelpers.ExecuteCommand(GitExecutable, $"add {filePath}", logger, clonedRepo); } LocalHelpers.ExecuteCommand(GitExecutable, $"commit -m \"{commitMessage}\"", logger, clonedRepo); LocalHelpers.ExecuteCommand(GitExecutable, $"-c core.askpass= -c credential.helper= push {remote} {branch}", logger, clonedRepo); } catch (Exception exc) { // This was originally a DarcException. Making it an actual Exception so we get to see in AppInsights if something failed while // commiting the changes throw new Exception($"Something went wrong when pushing the files to repo {repoUri} in branch {branch}", exc); } finally { try { // .git/objects hierarchy are marked as read-only so we need to unset the read-only attribute otherwise an UnauthorizedAccessException is thrown. GitFileManager.NormalizeAttributes(tempRepoFolder); Directory.Delete(tempRepoFolder, true); } catch (DirectoryNotFoundException) { // If the directory wasn't found, that means that the clone operation above failed // but this error isn't interesting at all. } catch (Exception exc) { throw new Exception($"Something went wrong while trying to delete the folder {tempRepoFolder}", exc); } } }
private static async Task <IEnumerable <DependencyDetail> > GetDependenciesAsync( IRemoteFactory remoteFactory, bool remote, ILogger logger, string repoUri, string commit, bool includeToolset, IEnumerable <string> remotesMap, string reposFolder, string testPath = null) { try { IEnumerable <DependencyDetail> dependencies = null; if (!string.IsNullOrEmpty(testPath)) { testPath = Path.Combine( testPath, repoUri, commit); if (Directory.Exists(testPath)) { Local local = new Local(logger, testPath); dependencies = await local.GetDependenciesAsync(); } } else if (remote) { IRemote remoteClient = await remoteFactory.GetRemoteAsync(repoUri, logger); dependencies = await remoteClient.GetDependenciesAsync( repoUri, commit); } else { string repoPath = GetRepoPath(repoUri, commit, remotesMap, reposFolder, logger); if (!string.IsNullOrEmpty(repoPath)) { Local local = new Local(logger); string fileContents = LocalHelpers.GitShow( repoPath, commit, VersionFiles.VersionDetailsXml, logger); dependencies = local.GetDependenciesFromFileContents(fileContents); } } if (!includeToolset) { dependencies = dependencies.Where(dependency => dependency.Type != DependencyType.Toolset); } return(dependencies); } catch (DependencyFileNotFoundException) { // This is not an error. Dependencies can be specified with explicit shas that // may not have eng/Version.Details.xml at that point. logger.LogWarning($"{repoUri}@{commit} does not have an eng/Version.Details.xml."); return(null); } catch (Exception exc) { logger.LogError(exc, $"Something failed while trying the fetch the " + $"dependencies of repo '{repoUri}' at sha " + $"'{commit}'"); throw; } }
public void Start() { // log version Lib.Log("Version : " + Lib.KerbalismVersion + " - Build : " + Lib.KerbalismDevBuild); if (LocalHelpers.GenerateEnglishLoc) { LocalHelpers.GenerateLoc(); } if (LocalHelpers.UpdateNonEnglishLoc) { LocalHelpers.RegenerateNonEnglishLoc(); } Lib.Log("Forcing KSP to load resources..."); PartResourceLibrary.Instance.LoadDefinitions(); // parse settings Settings.Parse(); // parse profile Profile.Parse(); // detect features Features.Detect(); // get configs from DB UrlDir.UrlFile root = null; foreach (UrlDir.UrlConfig url in GameDatabase.Instance.root.AllConfigs) { root = url.parent; break; } // inject MM patches on-the-fly, so that profile/features can be queried with NEEDS[] Inject(root, "Profile", Lib.UppercaseFirst(Settings.Profile)); if (Features.Reliability) { Inject(root, "Feature", "Reliability"); } if (Features.Deploy) { Inject(root, "Feature", "Deploy"); } if (Features.SpaceWeather) { Inject(root, "Feature", "SpaceWeather"); } if (Features.Automation) { Inject(root, "Feature", "Automation"); } if (Features.Science) { Inject(root, "Feature", "Science"); } if (Features.Radiation) { Inject(root, "Feature", "Radiation"); } if (Features.Shielding) { Inject(root, "Feature", "Shielding"); } if (Features.LivingSpace) { Inject(root, "Feature", "LivingSpace"); } if (Features.Comfort) { Inject(root, "Feature", "Comfort"); } if (Features.Poisoning) { Inject(root, "Feature", "Poisoning"); } if (Features.Pressure) { Inject(root, "Feature", "Pressure"); } if (Features.Habitat) { Inject(root, "Feature", "Habitat"); } if (Features.Supplies) { Inject(root, "Feature", "Supplies"); } // inject harmony patches Harmony harmony = new Harmony("Kerbalism"); harmony.PatchAll(Assembly.GetExecutingAssembly()); var methods = harmony.GetPatchedMethods(); // register loading callbacks if (HighLogic.LoadedScene == GameScenes.LOADING) { GameEvents.OnPartLoaderLoaded.Add(SaveHabitatData); } }
private static string GetRepoPath( DependencyDetail dependency, IEnumerable <string> remotesMap, string reposFolder, ILogger logger) { string repoPath = null; if (remotesMap != null) { if (_remotesMapping == null) { _remotesMapping = CreateRemotesMapping(remotesMap); } if (!_remotesMapping.ContainsKey(repoPath)) { throw new DarcException($"A key matching '{dependency.RepoUri}' was not " + $"found in the mapping. Please make sure to include it..."); } repoPath = _remotesMapping[repoPath]; } else { string folder = null; if (!string.IsNullOrEmpty(reposFolder)) { folder = reposFolder; } else { // If a repo folder or a mapping was not set we use the current parent's // parent folder. string gitDir = LocalHelpers.GetGitDir(logger); string parent = Directory.GetParent(gitDir).FullName; folder = Directory.GetParent(parent).FullName; } // There are cases when the sha is not specified in Version.Details.xml // since owners want that Maestro++ fills this in. Without a sha we // cannot walk the graph. We do not fail the process but display/return // a dependency with no sha and for that graph path that would be the end of the walk if (string.IsNullOrEmpty(dependency.Commit)) { return(null); } repoPath = LocalHelpers.GetRepoPathFromFolder(folder, dependency.Commit, logger); if (string.IsNullOrEmpty(repoPath)) { throw new DarcException($"Commit '{dependency.Commit}' was not found in any " + $"folder in '{folder}'. Make sure a folder for '{dependency.RepoUri}' exists " + $"and it has all the latest changes..."); } } return(repoPath); }
private static async Task <IEnumerable <DependencyDetail> > GetDependenciesAsync( DarcSettings darcSettings, bool remote, ILogger logger, DependencyGraphNode node, IEnumerable <string> remotesMap, string reposFolder, string testPath = null) { try { IEnumerable <DependencyDetail> dependencies = null; if (!string.IsNullOrEmpty(testPath)) { testPath = Path.Combine( testPath, node.DependencyDetail.RepoUri, node.DependencyDetail.Commit); if (!string.IsNullOrEmpty(node.DependencyDetail.Commit) && Directory.Exists(testPath)) { Local local = new Local( Path.Combine( testPath, ".git"), logger); dependencies = await local.GetDependenciesAsync(); } } else if (remote) { Remote remoteClient = new Remote(darcSettings, logger); dependencies = await remoteClient.GetDependenciesAsync( node.DependencyDetail.RepoUri, node.DependencyDetail.Commit); } else { string repoPath = GetRepoPath(node.DependencyDetail, remotesMap, reposFolder, logger); if (!string.IsNullOrEmpty(repoPath)) { // Local's constructor expects the repo's .git folder to be passed in. In this // particular case we could pass any folder under 'repoPath' or even a fake one // but we use .git to keep things consistent to what Local expects Local local = new Local($"{repoPath}/.git", logger); string fileContents = LocalHelpers.GitShow( repoPath, node.DependencyDetail.Commit, VersionFiles.VersionDetailsXml, logger); dependencies = local.GetDependenciesFromFileContents(fileContents); } } return(dependencies); } catch (Exception exc) { logger.LogError(exc, $"Something failed while trying the fetch the " + $"dependencies of repo '{node.DependencyDetail.RepoUri}' at sha " + $"'{node.DependencyDetail.Commit}'"); throw; } }
public UxManager(ILogger logger) { _editorPath = LocalHelpers.GetEditorPath(logger); _rootDir = LocalHelpers.GetRootDir(logger); _logger = logger; }
/// <summary> /// Download and install git to the a temporary location. /// Git is used by DarcLib, and the Service Fabric nodes do not have it installed natively. /// /// The file is assumed to be on a public endpoint. /// We return the git client executable so that this call may be easily wrapped in RetryAsync /// </summary> public async Task <string> GetPathToLocalGitAsync() { // Determine whether we need to do any downloading at all. if (!string.IsNullOrEmpty(_gitExecutable) && File.Exists(_gitExecutable)) { // We should also mke sure that the git executable that exists runs properly try { LocalHelpers.CheckGitInstallation(_gitExecutable, _logger); return(_gitExecutable); } catch { _logger.LogWarning($"Something went wrong with validating git executable at {_gitExecutable}. Downloading new version."); } } await _semaphoreSlim.WaitAsync(); try { // Determine whether another thread ended up getting the lock and downloaded git // in the meantime. if (string.IsNullOrEmpty(_gitExecutable) || !File.Exists(_gitExecutable)) { using (_operations.BeginOperation($"Installing a local copy of git")) { string gitLocation = _configuration.GetValue <string>("GitDownloadLocation", null); string[] pathSegments = new Uri(gitLocation, UriKind.Absolute).Segments; string remoteFileName = pathSegments[pathSegments.Length - 1]; string gitRoot = _tempFiles.GetFilePath("git-portable"); string targetPath = Path.Combine(gitRoot, Path.GetFileNameWithoutExtension(remoteFileName)); string gitZipFile = Path.Combine(gitRoot, remoteFileName); _logger.LogInformation($"Downloading git from '{gitLocation}' to '{gitZipFile}'"); if (Directory.Exists(targetPath)) { Directory.Delete(targetPath, true); } Directory.CreateDirectory(targetPath); using (HttpClient client = new HttpClient()) using (FileStream outStream = new FileStream(gitZipFile, FileMode.Create, FileAccess.Write)) using (var inStream = await client.GetStreamAsync(gitLocation)) { await inStream.CopyToAsync(outStream); } _logger.LogInformation($"Extracting '{gitZipFile}' to '{targetPath}'"); ZipFile.ExtractToDirectory(gitZipFile, targetPath, overwriteFiles: true); _gitExecutable = Path.Combine(targetPath, "bin", "git.exe"); } } } finally { _semaphoreSlim.Release(); } // Will throw if something is wrong with the git executable, forcing a retry LocalHelpers.CheckGitInstallation(_gitExecutable, _logger); return(_gitExecutable); }
/// <summary> /// Update local dependencies based on a specific channel. /// </summary> /// <param name="options">Command line options</param> /// <returns>Process exit code.</returns> public override async Task <int> ExecuteAsync() { try { DarcSettings darcSettings = darcSettings = LocalSettings.GetDarcSettings(_options, Logger); // TODO: PAT only used for pulling the arcade eng/common dir, // so hardcoded to GitHub PAT right now. Must be more generic in the future. darcSettings.GitType = GitRepoType.GitHub; LocalSettings localSettings = LocalSettings.LoadSettingsFile(_options); darcSettings.PersonalAccessToken = localSettings != null && !string.IsNullOrEmpty(localSettings.GitHubToken) ? localSettings.GitHubToken : _options.GitHubPat; Remote remote = new Remote(darcSettings, Logger); Local local = new Local(LocalHelpers.GetGitDir(Logger), Logger); List <DependencyDetail> dependenciesToUpdate = new List <DependencyDetail>(); bool someUpToDate = false; string finalMessage = $"Local dependencies updated from channel '{_options.Channel}'."; // First we need to figure out what to query for. Load Version.Details.xml and // find all repository uris, optionally restricted by the input dependency parameter. IEnumerable <DependencyDetail> dependencies = await local.GetDependenciesAsync(_options.Name); if (!dependencies.Any()) { Console.WriteLine("Found no dependencies to update."); return(Constants.ErrorCode); } if (!string.IsNullOrEmpty(_options.Name) && !string.IsNullOrEmpty(_options.Version)) { DependencyDetail dependency = dependencies.First(); dependency.Version = _options.Version; dependenciesToUpdate.Add(dependency); Console.WriteLine($"Updating '{dependency.Name}': '{dependency.Version}' => '{_options.Version}'"); finalMessage = $"Local dependency {_options.Name} updated to version '{_options.Version}'."; } else if (!string.IsNullOrEmpty(_options.PackagesFolder)) { try { dependenciesToUpdate.AddRange(GetDependenciesFromPackagesFolder(_options.PackagesFolder, dependencies)); } catch (DarcException exc) { Logger.LogError(exc, $"Error: Failed to update dependencies based on folder '{_options.PackagesFolder}'"); return(Constants.ErrorCode); } finalMessage = $"Local dependencies updated based on packages folder {_options.PackagesFolder}."; } else { // Start channel query. var channel = remote.GetChannelAsync(_options.Channel); // Limit the number of BAR queries by grabbing the repo URIs and making a hash set. var repositoryUrisForQuery = dependencies.Select(dependency => dependency.RepoUri).ToHashSet(); ConcurrentDictionary <string, Task <Build> > getLatestBuildTaskDictionary = new ConcurrentDictionary <string, Task <Build> >(); Channel channelInfo = await channel; if (channelInfo == null) { Console.WriteLine($"Could not find a channel named '{_options.Channel}'."); return(Constants.ErrorCode); } foreach (string repoToQuery in repositoryUrisForQuery) { var latestBuild = remote.GetLatestBuildAsync(repoToQuery, channelInfo.Id.Value); getLatestBuildTaskDictionary.TryAdd(repoToQuery, latestBuild); } // Now walk dependencies again and attempt the update foreach (DependencyDetail dependency in dependencies) { Build build; try { build = await getLatestBuildTaskDictionary[dependency.RepoUri]; } catch (ApiErrorException e) when(e.Response.StatusCode == System.Net.HttpStatusCode.NotFound) { Logger.LogTrace($"No build of '{dependency.RepoUri}' found on channel '{_options.Channel}'."); continue; } Asset buildAsset = build.Assets.Where(asset => asset.Name.Equals(dependency.Name, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); if (buildAsset == null) { Logger.LogTrace($"Dependency '{dependency.Name}' not found in latest build of '{dependency.RepoUri}' on '{_options.Channel}', skipping."); continue; } if (buildAsset.Version == dependency.Version && buildAsset.Name == dependency.Name && build.Repository == dependency.RepoUri && build.Commit == dependency.Commit) { // No changes someUpToDate = true; continue; } DependencyDetail updatedDependency = new DependencyDetail { // TODO: Not needed, but not currently provided in Build info. Will be available on next rollout. Branch = null, Commit = build.Commit, // If casing changes, ensure that the dependency name gets updated. Name = buildAsset.Name, RepoUri = build.Repository, Version = buildAsset.Version }; dependenciesToUpdate.Add(updatedDependency); // Print out what we are going to do. Console.WriteLine($"Updating '{dependency.Name}': '{dependency.Version}' => '{updatedDependency.Version}'" + $" (from build '{build.BuildNumber}' of '{build.Repository}')"); // Notify on casing changes. if (buildAsset.Name != dependency.Name) { Console.WriteLine($" Dependency name normalized to '{updatedDependency.Name}'"); } dependenciesToUpdate.Add(updatedDependency); } } if (!dependenciesToUpdate.Any()) { // If we found some dependencies already up to date, // then we consider this a success. Otherwise, we didn't even // find matching dependencies so we should let the user know. if (someUpToDate) { Console.WriteLine($"All dependencies are up to date."); return(Constants.SuccessCode); } else { Console.WriteLine($"Found no dependencies to update."); return(Constants.ErrorCode); } } if (_options.DryRun) { return(Constants.SuccessCode); } // Now call the local updater to run the update await local.UpdateDependenciesAsync(dependenciesToUpdate, remote); Console.WriteLine(finalMessage); return(Constants.SuccessCode); } catch (Exception e) { Logger.LogError(e, $"Error: Failed to update dependencies to channel {_options.Channel}"); return(Constants.ErrorCode); } }