public override void Log(ILogMessage message) { switch (message.Level) { case LogLevel.Verbose: _logger.Detailed(message.Message); break; case LogLevel.Debug: _logger.Detailed(message.Message); break; case LogLevel.Information: _logger.Detailed(message.Message); break; case LogLevel.Minimal: _logger.Normal(message.Message); break; case LogLevel.Warning: _logger.Normal(message.Message); break; case LogLevel.Error: _logger.Error(message.Message); break; default: throw new ArgumentOutOfRangeException($"Invalid log level {message.Level}"); } }
public async Task Invoke(PackageInProject currentPackage, NuGetVersion newVersion, PackageSource packageSource, NuGetSources allSources) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { _logger.Normal("Cannot run NuGet.exe package update as OS Platform is not Windows"); return; } var dirName = currentPackage.Path.Info.DirectoryName; var nuget = NuGetPath.FindExecutable(); if (string.IsNullOrWhiteSpace(nuget)) { _logger.Normal("Cannot find NuGet exe for package update"); return; } var sources = allSources.CommandLine("-Source"); var arguments = $"update packages.config -Id {currentPackage.Id} -Version {newVersion} {sources}"; _logger.Detailed(arguments); await _externalProcess.Run(dirName, nuget, arguments, true); }
public async Task Invoke(FileInfo file, NuGetSources sources) { _logger.Normal($"Nuget restore on {file.DirectoryName} {file.Name}"); if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { _logger.Normal("Cannot run NuGet.exe file restore as OS Platform is not Windows"); return; } var nuget = NuGetPath.FindExecutable(); if (string.IsNullOrWhiteSpace(nuget)) { _logger.Normal("Cannot find NuGet exe for solution restore"); return; } var sourcesCommandLine = sources.CommandLine("-Source"); var arguments = $"restore {file.Name} {sourcesCommandLine}"; _logger.Detailed($"{nuget} {arguments}"); var processOutput = await _externalProcess.Run(file.DirectoryName, nuget, arguments, ensureSuccess : false); if (processOutput.Success) { _logger.Detailed($"Nuget restore on {file.Name} complete"); } else { _logger.Detailed($"Nuget restore failed on {file.DirectoryName} {file.Name}:\n{processOutput.Output}\n{processOutput.ErrorOutput}"); } }
private async Task <IReadOnlyCollection <PackageUpdateSet> > ApplyFilters( IReadOnlyCollection <PackageUpdateSet> all, Func <PackageUpdateSet, Task <bool> > remoteCheck) { var filteredByInOut = FilteredByIncludeExclude(all); var filteredLocally = filteredByInOut .Where(MatchesMinAge) .ToList(); if (filteredLocally.Count < filteredByInOut.Count) { var agoFormat = TimeSpanFormat.Ago(_settings.MinimumAge); _logger.Normal($"Filtered by minimum package age '{agoFormat}' from {filteredByInOut.Count} to {filteredLocally.Count}"); } var remoteFiltered = await ApplyRemoteFilter(filteredLocally, remoteCheck); if (remoteFiltered.Count < filteredLocally.Count) { _logger.Normal($"Filtered by remote branch check branch from {filteredLocally.Count} to {remoteFiltered.Count}"); } return(remoteFiltered); }
private PackageInProject XmlToPackage(XElement el, PackagePath path, XNamespace ns) { try { var id = el.Attribute("Include")?.Value; var version = el.Attribute("Version")?.Value ?? el.Element(ns + "Version")?.Value; if (string.IsNullOrWhiteSpace(version)) { _logger.Normal($"Skipping package '{id}' with no version specified."); return(null); } var versionParseSuccess = NuGetVersion.TryParse(version, out var nugetVersion); if (!versionParseSuccess) { _logger.Normal($"Skipping package '{id}' with version '{version}' that could not be parsed."); return(null); } return(new PackageInProject(new PackageIdentity(id, nugetVersion), path)); } catch (Exception ex) { _logger.Error($"Could not read package from {el} in file {path.FullName}", ex); return(null); } }
private ISettingsReader FindPlatformSettingsReader( Platform?platformFromSettings, Uri apiEndpoint) { if (platformFromSettings.HasValue) { var reader = _settingReaders .FirstOrDefault(s => s.Platform == platformFromSettings.Value); if (reader != null) { _nuKeeperLogger.Normal($"Collaboration platform specified as '{reader.Platform}'"); } return(reader); } else { var reader = _settingReaders .FirstOrDefault(s => s.CanRead(apiEndpoint)); if (reader != null) { _nuKeeperLogger.Normal($"Matched uri '{apiEndpoint}' to collaboration platform '{reader.Platform}'"); } return(reader); } }
public void Report(PackageLookupResult lookupResult) { var highestVersion = lookupResult.Major?.Identity?.Version; if (highestVersion == null) { return; } var allowing = lookupResult.AllowedChange == VersionChange.Major ? string.Empty : $" Allowing {lookupResult.AllowedChange} version updates."; var highestMatchVersion = lookupResult.Selected()?.Identity?.Version; var packageId = lookupResult.Major.Identity.Id; if (highestMatchVersion == null) { _logger.Normal($"Package {packageId} version {highestVersion} is available but is not allowed.{allowing}"); return; } if (highestVersion > highestMatchVersion) { _logger.Normal($"Selected update of package {packageId} to version {highestMatchVersion}, but version {highestVersion} is also available.{allowing}"); } else { _logger.Detailed($"Selected update of package {packageId} to highest version, {highestMatchVersion}.{allowing}"); } }
public async Task <IReadOnlyList <Organization> > GetOrganizations() { var groups = await _client.GetAllGroups(); _logger.Normal($"Read {groups.Count} groups"); return(groups.Select(grp => new Organization(grp.Path ?? grp.Name)).ToList()); }
public void Clone(Uri pullEndpoint) { _logger.Normal($"Git clone {pullEndpoint} to {WorkingFolder.FullPath}"); Repository.Clone(pullEndpoint.AbsoluteUri, WorkingFolder.FullPath, new CloneOptions { CredentialsProvider = UsernamePasswordCredentials, OnTransferProgress = OnTransferProgress }); _logger.Detailed("Git clone complete"); }
public async Task Invoke(FileInfo file, NuGetSources sources) { _logger.Normal($"Nuget restore on {file.DirectoryName} {file.Name}"); var nuget = _nuGetPath.Executable; if (string.IsNullOrWhiteSpace(nuget)) { _logger.Normal("Cannot find NuGet.exe for solution restore"); return; } var sourcesCommandLine = sources.CommandLine("-Source"); var restoreCommand = $"restore {file.Name} {sourcesCommandLine} -NonInteractive"; ProcessOutput processOutput; if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { if (await _monoExecutor.CanRun()) { processOutput = await _monoExecutor.Run(file.DirectoryName, nuget, restoreCommand, ensureSuccess : false); } else { _logger.Error("Cannot run NuGet.exe. It requires either Windows OS Platform or Mono installation"); return; } } else { processOutput = await _externalProcess.Run(file.DirectoryName, nuget, restoreCommand, ensureSuccess : false); } if (processOutput.Success) { _logger.Detailed($"Nuget restore on {file.Name} complete"); } else { _logger.Detailed( $"Nuget restore failed on {file.DirectoryName} {file.Name}:\n{processOutput.Output}\n{processOutput.ErrorOutput}"); } }
public void Clone(Uri pullEndpoint, string branchName) { _logger.Normal($"Git clone {pullEndpoint}, branch {branchName ?? "default"}, to {WorkingFolder.FullPath}"); Repository.Clone(pullEndpoint.AbsoluteUri, WorkingFolder.FullPath, new CloneOptions { CredentialsProvider = UsernamePasswordCredentials, OnTransferProgress = OnTransferProgress, BranchName = branchName }); _logger.Detailed("Git clone complete"); }
public async Task Invoke(PackageInProject currentPackage, NuGetVersion newVersion, PackageSource packageSource, NuGetSources allSources) { var projectPath = currentPackage.Path.Info.DirectoryName; var nuget = _nuGetPath.Executable; if (string.IsNullOrWhiteSpace(nuget)) { _logger.Normal("Cannot find NuGet.exe for package update"); return; } var sources = allSources.CommandLine("-Source"); var updateCommand = $"update packages.config -Id {currentPackage.Id} -Version {newVersion} {sources} -NonInteractive"; if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { if (await _monoExecutor.CanRun()) { await _monoExecutor.Run(projectPath, nuget, updateCommand, true); } else { _logger.Error("Cannot run NuGet.exe. It requires either Windows OS Platform or Mono installation"); } } else { await _externalProcess.Run(projectPath, nuget, updateCommand, true); } }
public IEnumerable <GitRemote> GetRemotes(Uri repositoryUri) { if (!IsGitRepo(repositoryUri)) { return(Enumerable.Empty <GitRemote>()); } var discover = Repository.Discover(repositoryUri.AbsolutePath); var gitRemotes = new List <GitRemote>(); using (var repo = new Repository(discover)) { foreach (var remote in repo.Network.Remotes) { Uri.TryCreate(remote.Url, UriKind.Absolute, out repositoryUri); if (repositoryUri != null) { var gitRemote = new GitRemote { Name = remote.Name, Url = repositoryUri }; gitRemotes.Add(gitRemote); } else { _logger.Normal($"Cannot parse {remote.Url} to URI. SSH remote is currently not supported"); } } return(gitRemotes); } }
private async Task <int> DoTargetUpdates( IGitDriver git, RepositoryData repository, IReadOnlyCollection <PackageUpdateSet> targetUpdates, NuGetSources sources, SettingsContainer settings) { if (targetUpdates.Count == 0) { _logger.Minimal("No updates can be applied. Exiting."); return(0); } await _solutionsRestore.CheckRestore(targetUpdates, settings.WorkingFolder ?? git.WorkingFolder, sources); var updatesDone = await _packageUpdater.MakeUpdatePullRequests(git, repository, targetUpdates, sources, settings); if (updatesDone < targetUpdates.Count) { _logger.Minimal($"Attempted {targetUpdates.Count} updates and did {updatesDone}"); } else { _logger.Normal($"Done {updatesDone} updates"); } return(updatesDone); }
private async Task <int> MakeUpdatePullRequests( IGitDriver git, RepositoryData repository, NuGetSources sources, SettingsContainer settings, IReadOnlyCollection <PackageUpdateSet> updates) { _logger.Normal(UpdatesLogger.OldVersionsToBeUpdated(updates)); git.Checkout(repository.DefaultBranch); // branch var branchName = BranchNamer.MakeName(updates); _logger.Detailed($"Using branch name: '{branchName}'"); git.CheckoutNewBranch(branchName); foreach (var updateSet in updates) { await _updateRunner.Update(updateSet, sources); var commitMessage = CommitWording.MakeCommitMessage(updateSet); git.Commit(commitMessage); } git.Push("nukeeper_push", branchName); var title = CommitWording.MakePullRequestTitle(updates); var body = CommitWording.MakeCommitDetails(updates); await _gitHub.CreatePullRequest(repository, title, body, branchName, settings.SourceControlServerSettings.Labels); git.Checkout(repository.DefaultBranch); return(updates.Count); }
private GitRemote CreateGitRemoteFromString(string remote) { var linkParser = new Regex(@"\b(?:https?://|www\.)\S+\b", RegexOptions.Compiled | RegexOptions.IgnoreCase); var match = linkParser.Match(remote); if (match.Success) { if (Uri.TryCreate(match.Value, UriKind.Absolute, out Uri repositoryUri)) { var remoteName = remote.Split(new [] { "\t" }, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault(); if (!string.IsNullOrWhiteSpace(remoteName)) { return(new GitRemote { Name = remoteName, Url = repositoryUri }); } } else { _logger.Normal($"Cannot parse {match.Value} to URI. SSH remote is currently not supported"); } } return(null); }
private async Task <(int UpdatesMade, bool?ThresholdReached)> DoTargetUpdates( IGitDriver git, RepositoryData repository, IReadOnlyCollection <PackageUpdateSet> targetUpdates, NuGetSources sources, SettingsContainer settings ) { if (targetUpdates.Count == 0) { return(0, null); } await _solutionRestore.CheckRestore(targetUpdates, settings.WorkingFolder ?? git.WorkingFolder, sources); var(updatesDone, thresholdReached) = await _packageUpdater.MakeUpdatePullRequests(git, repository, targetUpdates, sources, settings); if (updatesDone < targetUpdates.Count) { _logger.Minimal($"Attempted {targetUpdates.Count} updates and did {updatesDone}"); } else { _logger.Normal($"Done {updatesDone} updates"); } return(updatesDone, thresholdReached); }
public async Task <int> Run( IGitDriver git, RepositoryData repository, SettingsContainer settings) { GitInit(git, repository); var sources = _nugetSourcesReader.Read(git.WorkingFolder, settings.UserSettings.NuGetSources); var updates = await _updateFinder.FindPackageUpdateSets( git.WorkingFolder, sources, settings.UserSettings.AllowedChange); _logger.Detailed($"Report mode is {settings.UserSettings.ReportMode}"); switch (settings.UserSettings.ReportMode) { case ReportMode.Off: break; case ReportMode.On: // report and continue _availableUpdatesReporter.Report(repository.Pull.Name, updates); break; case ReportMode.ReportOnly: // report and exit _availableUpdatesReporter.Report(repository.Pull.Name, updates); _logger.Normal("Exiting after reports only"); return(0); default: throw new Exception($"Unknown report mode: '{settings.UserSettings.ReportMode}'"); } if (updates.Count == 0) { _logger.Minimal("No potential updates found. Well done. Exiting."); return(0); } var targetUpdates = await _updateSelection.SelectTargets( repository.Push, updates, settings.PackageFilters); return(await DoTargetUpdates(git, repository, targetUpdates, sources, settings)); }
public async Task Clone(Uri pullEndpoint, string branchName) { _logger.Normal($"Git clone {pullEndpoint}, branch {branchName ?? "default"}, to {WorkingFolder.FullPath}"); var branchparam = branchName == null ? "" : $" -b {branchName}"; await StartGitProcess($"clone{branchparam} {CreateCredentialsUri(pullEndpoint, _gitCredentials)} .", true); // Clone into current folder _logger.Detailed("Git clone complete"); }
public PackageInProject?Read( string?id, string?version, PackagePath path, IEnumerable <string>?projectReferences) { if (path == null) { return(null); } if (string.IsNullOrWhiteSpace(id)) { _logger.Normal($"Skipping package with no id specified in file '{path.FullName}'."); return(null); } if (string.IsNullOrWhiteSpace(version)) { _logger.Normal($"Skipping package '{id}' with no version specified in file '{path.FullName}'."); return(null); } var packageVersionRange = PackageVersionRange.Parse(id, version); if (packageVersionRange == null) { _logger.Normal($"Skipping package '{id}' with version '{version}' that could not be parsed in file '{path.FullName}'."); return(null); } var pip = new PackageInProject(packageVersionRange, path, projectReferences); var singleVersion = pip.Identity; if (singleVersion == null) { _logger.Normal($"Skipping package '{id}' with version range '{version}' that is not a single version in file '{path.FullName}'."); return(null); } return(pip); }
private async Task <ForkData> FindUserForkOrUpstream(string userName, ForkData pullFork) { var userFork = await TryFindUserFork(userName, pullFork); if (userFork != null) { return(userFork); } // as a fallback, we want to pull and push from the same origin repo. var canUseOriginRepo = await IsPushableRepo(pullFork); if (canUseOriginRepo) { _logger.Normal($"No fork for user {userName}. Using upstream fork for user {pullFork.Owner} at {pullFork.Uri}"); return(pullFork); } NoPushableForkFound(pullFork.Name); return(null); }
public async Task <ProcessOutput> Run(string workingDirectory, string command, string arguments, bool ensureSuccess) { _logger.Normal($"Using Mono to run '{command}'"); if (!await CanRun()) { _logger.Error($"Cannot run '{command}' on Mono since Mono installation was not found"); throw new InvalidOperationException("Mono installation was not found"); } return(await _externalProcess.Run(workingDirectory, "mono", $"{command} {arguments}", ensureSuccess)); }
public static void Log(this INuKeeperLogger logger, LogData data) { if (!string.IsNullOrWhiteSpace(data.Terse)) { logger.Minimal(data.Terse); } if (!string.IsNullOrWhiteSpace(data.Info)) { logger.Normal(data.Info); } }
public async Task <IReadOnlyList <Organization> > GetOrganizations() { var orgs = await _client.Organization.GetAll(); _logger.Normal($"Read {orgs.Count} organisations"); return(orgs); }
private async Task <int> MakeUpdatePullRequests( IGitDriver git, RepositoryData repository, NuGetSources sources, SettingsContainer settings, IReadOnlyCollection <PackageUpdateSet> updates) { _logger.Normal(UpdatesLogger.OldVersionsToBeUpdated(updates)); await git.Checkout(repository.DefaultBranch); // branch var branchWithChanges = BranchNamer.MakeName(updates, settings.BranchSettings.BranchNamePrefix); _logger.Detailed($"Using branch name: '{branchWithChanges}'"); await git.CheckoutNewBranch(branchWithChanges); foreach (var updateSet in updates) { await _updateRunner.Update(updateSet, sources); var commitMessage = _collaborationFactory.CommitWorder.MakeCommitMessage(updateSet); await git.Commit(commitMessage); } await git.Push(repository.Remote, branchWithChanges); var title = _collaborationFactory.CommitWorder.MakePullRequestTitle(updates); var body = _collaborationFactory.CommitWorder.MakeCommitDetails(updates); string qualifiedBranch; if (!repository.IsFork) //check if we are on a fork, if so qualify the branch name { qualifiedBranch = branchWithChanges; } else { qualifiedBranch = repository.Push.Owner + ":" + branchWithChanges; } var pullRequestRequest = new PullRequestRequest(qualifiedBranch, title, repository.DefaultBranch, settings.BranchSettings.DeleteBranchAfterMerge) { Body = body }; await _collaborationFactory.CollaborationPlatform.OpenPullRequest(repository.Pull, pullRequestRequest, settings.SourceControlServerSettings.Labels); await git.Checkout(repository.DefaultBranch); return(updates.Count); }
public PackageInProject Read( string id, string version, PackagePath path, IEnumerable <string> projectReferences) { if (string.IsNullOrWhiteSpace(id)) { _logger.Normal($"Skipping package with no id specified in file '{path.FullName}'."); return(null); } if (string.IsNullOrWhiteSpace(version)) { // TODO this is very spammy when using MSBuild Directory.build.props or .targets files for versioning NuGets. Should find a better way for this. _logger.Detailed($"Skipping package '{id}' with no version specified in file '{path.FullName}'."); return(null); } var packageVersionRange = PackageVersionRange.Parse(id, version); if (packageVersionRange == null) { _logger.Normal($"Skipping package '{id}' with version '{version}' that could not be parsed in file '{path.FullName}'."); return(null); } var pip = new PackageInProject(packageVersionRange, path, projectReferences); var singleVersion = pip.Identity; if (singleVersion == null) { _logger.Normal($"Skipping package '{id}' with version range '{version}' that is not a single version in file '{path.FullName}'."); return(null); } return(pip); }
public async Task <IReadOnlyCollection <PackageUpdateSet> > FindPackageUpdateSets( IFolder workingFolder, NuGetSources sources, VersionChange allowedChange, UsePrerelease usePrerelease, Regex includes = null, Regex excludes = null) { var packages = FindPackages(workingFolder); _logger.Normal($"Found {packages.Count} packages"); var filtered = FilteredByIncludeExclude(packages, includes, excludes); _logger.Log(PackagesFoundLogger.Log(filtered)); // look for updates to these packages var updates = await _packageUpdatesLookup.FindUpdatesForPackages( filtered, sources, allowedChange, usePrerelease); _logger.Log(UpdatesLogger.Log(updates)); return(updates); }
private async Task <ForkData> FindUpstreamRepoOnly(ForkData pullFork) { // Only want to pull and push from the same origin repo. var canUseOriginRepo = await IsPushableRepo(pullFork); if (canUseOriginRepo) { _logger.Normal($"Using upstream fork as push, for project {pullFork.Owner} at {pullFork.Uri}"); return(pullFork); } NoPushableForkFound(pullFork.Name); return(null); }
private async Task <RepositoryData> BuildGitRepositorySpec( RepositorySettings repository, string userName) { var pullFork = new ForkData(repository.RepositoryUri, repository.RepositoryOwner, repository.RepositoryName); var pushFork = await _collaborationFactory.ForkFinder.FindPushFork(userName, pullFork); if (pushFork == null) { _logger.Normal($"No pushable fork found for {repository.RepositoryUri}"); return(null); } return(new RepositoryData(pullFork, pushFork)); }
private IReadOnlyCollection <PackageUpdateSet> ApplyFilters( IReadOnlyCollection <PackageUpdateSet> all) { var filtered = all .Where(MatchesMinAge) .ToList(); if (filtered.Count < all.Count) { var agoFormat = TimeSpanFormat.Ago(_settings.MinimumAge); _logger.Normal($"Filtered by minimum package age '{agoFormat}' from {all.Count} to {filtered.Count}"); } return(filtered); }