Skip to content

(GH-181) Add Dependency Injection #265

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 40 commits into from
Aug 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
30359b5
(GH-181) Update OctokitExtensions to use IGitHubClient interface
akordowski Jul 30, 2020
8162678
(GH-181) Update GitHubProvider to use IGitHubClient and ILogger inter…
akordowski Jul 30, 2020
0832333
(GH-181) Update ReleaseNotesBuilder to use ILogger interface
akordowski Jul 30, 2020
dca3a8f
(GH-181) Update ReleaseNotesExporter to use ILogger interface
akordowski Jul 30, 2020
41bd9c4
(GH-181) Fix unit tests
akordowski Jul 30, 2020
6bfcb05
(GH-181) Add ConfigureAwait
akordowski Jul 31, 2020
b238e10
(GH-181) Add Dependency Injection
akordowski Jul 31, 2020
ffab159
(GH-181) Rename GithHubProvider to VcsService
akordowski Aug 5, 2020
9b7103f
(GH-181) Extract method from VcsService to VcsProvider
akordowski Aug 5, 2020
f496f50
(GH-181) Update unit tests
akordowski Aug 5, 2020
5510d77
(GH-181) Update classes, add unit tests
akordowski Aug 5, 2020
a8c4f5a
(GH-181) Extract method from VcsService to VcsProvider
akordowski Aug 6, 2020
c1ba1a0
(GH-181) Update and add unit tests
akordowski Aug 6, 2020
03a5b8a
(GH-181) Extract method from VcsService to VcsProvider
akordowski Aug 6, 2020
27b0431
(GH-181) Update and add unit tests
akordowski Aug 6, 2020
fe67d27
(GH-181) Update ReleaseNotesBuilderIntegrationTests
akordowski Aug 6, 2020
cd2d8f3
(GH-181) Remove duplication in ReleaseNotesBuilder
akordowski Aug 6, 2020
af0f4bd
(GH-181) Refactor ReleaseNotesExporter
akordowski Aug 6, 2020
5357d25
(GH-181) Update and add unit tests
akordowski Aug 6, 2020
c62d570
(GH-181) Decouple ReleaseNotesBuilder and ReleaseNotesExporter from V…
akordowski Aug 6, 2020
104bffa
(GH-181) Update and add unit tests
akordowski Aug 6, 2020
ef1618d
(GH-181) Fix LGTM warning
akordowski Aug 6, 2020
48acb2a
(GH-181) Refactor VcsService
akordowski Aug 7, 2020
565c712
(GH-181) Update and add unit tests
akordowski Aug 7, 2020
e6dca14
(GH-181) Update build script
akordowski Aug 8, 2020
43ec9db
(GH-181) Refactor VcsService to remove unnecessary dependencies
akordowski Aug 9, 2020
4b6e0d1
(GH-181) Update GitHubProvider
akordowski Aug 9, 2020
4f5dfce
(GH-181) Update and add unit tests
akordowski Aug 10, 2020
bad07ef
(GH-181) Move CLI options to GitReleaseManager.Core library
akordowski Aug 10, 2020
1ccfce6
(GH-181) Add FileSystem to DI
akordowski Aug 10, 2020
f48ddaa
(GH-181) Move command methods to Commands
akordowski Aug 10, 2020
4680397
(GH-181) Update commands
akordowski Aug 11, 2020
ccd96d5
(GH-181) Add commands unit tests
akordowski Aug 11, 2020
5be4ed8
(GH-181) Update VcsService, IssueComment
akordowski Aug 11, 2020
42d2b37
(GH-181) Update GitHubProvider, GitHubProfile
akordowski Aug 11, 2020
1ca6ec9
(GH-181) Add GitHubProvider integration tests
akordowski Aug 11, 2020
fc7dd3c
(GH-181) Fix unit tests
akordowski Aug 11, 2020
715956c
(GH-181) Add pagination to GitHubProvider
akordowski Aug 13, 2020
844ecb4
(GH-181) Update unit tests
akordowski Aug 13, 2020
711d6ca
(GH-181) Add copyright header to new files
gep13 Aug 24, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Source/GitReleaseManager.Cli/GitReleaseManager.Cli.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@
<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.8.0" />
<PackageReference Include="Destructurama.Attributed" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.6" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Octokit" Version="0.48.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
<PackageReference Include="Serilog.Sinks.Debug" Version="1.0.1" PrivateAssets="All"/>
<PackageReference Include="Serilog.Sinks.Debug" Version="1.0.1" PrivateAssets="All" />
<PackageReference Include="Serilog.Sinks.File" Version="4.1.0" />
<PackageReference Include="seriloganalyzer" Version="0.15.0" />
</ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion Source/GitReleaseManager.Cli/Logging/LogConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace GitReleaseManager.Cli.Logging
using System.Diagnostics;
using System.Text;
using Destructurama;
using GitReleaseManager.Cli.Options;
using GitReleaseManager.Core.Options;
using Octokit;
using Serilog;
using Serilog.Events;
Expand Down
211 changes: 69 additions & 142 deletions Source/GitReleaseManager.Cli/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,53 +7,51 @@
namespace GitReleaseManager.Cli
{
using System;
using System.IO;
using System.Net;
using System.Reflection;
using System.Threading.Tasks;
using AutoMapper;
using CommandLine;
using GitReleaseManager.Cli.Logging;
using GitReleaseManager.Cli.Options;
using GitReleaseManager.Core;
using GitReleaseManager.Core.Commands;
using GitReleaseManager.Core.Configuration;
using GitReleaseManager.Core.Helpers;
using GitReleaseManager.Core.Options;
using GitReleaseManager.Core.Provider;
using GitReleaseManager.Core.ReleaseNotes;
using Microsoft.Extensions.DependencyInjection;
using Octokit;
using Serilog;

public static class Program
{
private static FileSystem _fileSystem;
private static IMapper _mapper;
private static IVcsProvider _vcsProvider;
private static IServiceProvider _serviceProvider;

private static async Task<int> Main(string[] args)
{
// Just add the TLS 1.2 protocol to the Service Point manager until
// we've upgraded to latest Octokit.
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;

_fileSystem = new FileSystem();

_mapper = AutoMapperConfiguration.Configure();

try
{
return await Parser.Default.ParseArguments<CreateSubOptions, DiscardSubOptions, AddAssetSubOptions, CloseSubOptions, OpenSubOptions, PublishSubOptions, ExportSubOptions, InitSubOptions, ShowConfigSubOptions, LabelSubOptions>(args)
.WithParsed<BaseSubOptions>(LogConfiguration.ConfigureLogging)
.WithParsed<BaseSubOptions>(CreateFiglet)
.WithParsed<BaseSubOptions>(LogOptions)
.WithParsed<BaseVcsOptions>(ReportUsernamePasswordDeprecation)
.WithParsed<BaseVcsOptions>(RegisterServices)
.MapResult(
(CreateSubOptions opts) => CreateReleaseAsync(opts),
(DiscardSubOptions opts) => DiscardReleaseAsync(opts),
(AddAssetSubOptions opts) => AddAssetsAsync(opts),
(CloseSubOptions opts) => CloseMilestoneAsync(opts),
(OpenSubOptions opts) => OpenMilestoneAsync(opts),
(PublishSubOptions opts) => PublishReleaseAsync(opts),
(ExportSubOptions opts) => ExportReleasesAsync(opts),
(InitSubOptions opts) => CreateSampleConfigFileAsync(opts),
(ShowConfigSubOptions opts) => ShowConfigAsync(opts),
(LabelSubOptions opts) => CreateLabelsAsync(opts),
(CreateSubOptions opts) => ExecuteCommand(opts),
(DiscardSubOptions opts) => ExecuteCommand(opts),
(AddAssetSubOptions opts) => ExecuteCommand(opts),
(CloseSubOptions opts) => ExecuteCommand(opts),
(OpenSubOptions opts) => ExecuteCommand(opts),
(PublishSubOptions opts) => ExecuteCommand(opts),
(ExportSubOptions opts) => ExecuteCommand(opts),
(InitSubOptions opts) => ExecuteCommand(opts),
(ShowConfigSubOptions opts) => ExecuteCommand(opts),
(LabelSubOptions opts) => ExecuteCommand(opts),
errs => Task.FromResult(1)).ConfigureAwait(false);
}
catch (AggregateException ex)
Expand All @@ -74,6 +72,53 @@ private static async Task<int> Main(string[] args)
finally
{
Log.CloseAndFlush();
DisposeServices();
}
}

private static void RegisterServices(BaseVcsOptions options)
{
var fileSystem = new FileSystem();
var logger = Log.ForContext<VcsService>();
var mapper = AutoMapperConfiguration.Configure();
var configuration = ConfigurationProvider.Provide(options.TargetDirectory ?? Environment.CurrentDirectory, fileSystem);

var credentials = string.IsNullOrWhiteSpace(options.Token)
? new Credentials(options.UserName, options.Password)
: new Credentials(options.Token);

var gitHubClient = new GitHubClient(new ProductHeaderValue("GitReleaseManager")) { Credentials = credentials };

var serviceCollection = new ServiceCollection()
.AddSingleton(logger)
.AddSingleton(mapper)
.AddSingleton(configuration)
.AddSingleton(configuration.Export)
.AddSingleton<ICommand<AddAssetSubOptions>, AddAssetsCommand>()
.AddSingleton<ICommand<CloseSubOptions>, CloseCommand>()
.AddSingleton<ICommand<CreateSubOptions>, CreateCommand>()
.AddSingleton<ICommand<DiscardSubOptions>, DiscardCommand>()
.AddSingleton<ICommand<ExportSubOptions>, ExportCommand>()
.AddSingleton<ICommand<InitSubOptions>, InitCommand>()
.AddSingleton<ICommand<LabelSubOptions>, LabelCommand>()
.AddSingleton<ICommand<OpenSubOptions>, OpenCommand>()
.AddSingleton<ICommand<PublishSubOptions>, PublishCommand>()
.AddSingleton<ICommand<ShowConfigSubOptions>, ShowConfigCommand>()
.AddSingleton<IFileSystem, FileSystem>()
.AddSingleton<IReleaseNotesExporter, ReleaseNotesExporter>()
.AddSingleton<IReleaseNotesBuilder, ReleaseNotesBuilder>()
.AddSingleton<IGitHubClient>(gitHubClient)
.AddSingleton<IVcsProvider, GitHubProvider>()
.AddSingleton<IVcsService, VcsService>();

_serviceProvider = serviceCollection.BuildServiceProvider();
}

private static void DisposeServices()
{
if (_serviceProvider is IDisposable serviceProvider)
{
serviceProvider.Dispose();
}
}

Expand Down Expand Up @@ -145,129 +190,11 @@ private static int GetConsoleWidth()
}
}

private static async Task<int> CreateReleaseAsync(CreateSubOptions subOptions)
{
Log.Information("Creating release...");
_vcsProvider = GetVcsProvider(subOptions);

Core.Model.Release release;
if (!string.IsNullOrEmpty(subOptions.Milestone))
{
Log.Verbose("Milestone {Milestone} was specified", subOptions.Milestone);
var releaseName = subOptions.Name;
if (string.IsNullOrWhiteSpace(releaseName))
{
Log.Verbose("No Release Name was specified, using {Milestone}.", subOptions.Milestone);
releaseName = subOptions.Milestone;
}

release = await _vcsProvider.CreateReleaseFromMilestone(subOptions.RepositoryOwner, subOptions.RepositoryName, subOptions.Milestone, releaseName, subOptions.TargetCommitish, subOptions.AssetPaths, subOptions.Prerelease).ConfigureAwait(false);
}
else
{
Log.Verbose("No milestone was specified, switching to release creating from input file");
release = await _vcsProvider.CreateReleaseFromInputFile(subOptions.RepositoryOwner, subOptions.RepositoryName, subOptions.Name, subOptions.InputFilePath, subOptions.TargetCommitish, subOptions.AssetPaths, subOptions.Prerelease).ConfigureAwait(false);
}

Log.Information("Drafted release is available at:\n{HtmlUrl}", release.HtmlUrl);
Log.Verbose("Body:\n{Body}", release.Body);
return 0;
}

private static async Task<int> DiscardReleaseAsync(DiscardSubOptions subOptions)
private static Task<int> ExecuteCommand<TOptions>(TOptions options)
where TOptions : BaseSubOptions
{
Log.Information("Discarding release {Milestone}", subOptions.Milestone);
_vcsProvider = GetVcsProvider(subOptions);

await _vcsProvider.DiscardRelease(subOptions.RepositoryOwner, subOptions.RepositoryName, subOptions.Milestone);

return 0;
}

private static async Task<int> AddAssetsAsync(AddAssetSubOptions subOptions)
{
Log.Information("Uploading assets");
_vcsProvider = GetVcsProvider(subOptions);

await _vcsProvider.AddAssets(subOptions.RepositoryOwner, subOptions.RepositoryName, subOptions.TagName, subOptions.AssetPaths).ConfigureAwait(false);

return 0;
}

private static async Task<int> CloseMilestoneAsync(CloseSubOptions subOptions)
{
Log.Information("Closing milestone {Milestone}", subOptions.Milestone);
_vcsProvider = GetVcsProvider(subOptions);

await _vcsProvider.CloseMilestone(subOptions.RepositoryOwner, subOptions.RepositoryName, subOptions.Milestone).ConfigureAwait(false);

return 0;
}

private static async Task<int> OpenMilestoneAsync(OpenSubOptions subOptions)
{
Log.Information("Opening milestone {Milestone}", subOptions.Milestone);
_vcsProvider = GetVcsProvider(subOptions);

await _vcsProvider.OpenMilestone(subOptions.RepositoryOwner, subOptions.RepositoryName, subOptions.Milestone).ConfigureAwait(false);

return 0;
}

private static async Task<int> PublishReleaseAsync(PublishSubOptions subOptions)
{
_vcsProvider = GetVcsProvider(subOptions);

await _vcsProvider.PublishRelease(subOptions.RepositoryOwner, subOptions.RepositoryName, subOptions.TagName).ConfigureAwait(false);
return 0;
}

private static async Task<int> ExportReleasesAsync(ExportSubOptions subOptions)
{
Log.Information("Exporting release {TagName}", subOptions.TagName);
_vcsProvider = GetVcsProvider(subOptions);

var releasesMarkdown = await _vcsProvider.ExportReleases(subOptions.RepositoryOwner, subOptions.RepositoryName, subOptions.TagName).ConfigureAwait(false);

using (var sw = new StreamWriter(File.Open(subOptions.FileOutputPath, FileMode.OpenOrCreate)))
{
sw.Write(releasesMarkdown);
}

return 0;
}

private static Task<int> CreateSampleConfigFileAsync(InitSubOptions subOptions)
{
Log.Information("Creating sample configuration file");
var directory = subOptions.TargetDirectory ?? Environment.CurrentDirectory;
ConfigurationProvider.WriteSample(directory, _fileSystem);
return Task.FromResult(0);
}

private static Task<int> ShowConfigAsync(ShowConfigSubOptions subOptions)
{
var configuration = ConfigurationProvider.GetEffectiveConfigAsString(subOptions.TargetDirectory ?? Environment.CurrentDirectory, _fileSystem);

Log.Information("{Configuration}", configuration);
return Task.FromResult(0);
}

private static async Task<int> CreateLabelsAsync(LabelSubOptions subOptions)
{
Log.Information("Creating standard labels");
_vcsProvider = GetVcsProvider(subOptions);

await _vcsProvider.CreateLabels(subOptions.RepositoryOwner, subOptions.RepositoryName).ConfigureAwait(false);
return 0;
}

private static IVcsProvider GetVcsProvider(BaseVcsOptions subOptions)
{
var configuration = ConfigurationProvider.Provide(subOptions.TargetDirectory ?? Environment.CurrentDirectory, _fileSystem);

Log.Information("Using {Provider} as VCS Provider", "GitHub");
return new GitHubProvider(_mapper, configuration, subOptions.UserName, subOptions.Password, subOptions.Token);
var command = _serviceProvider.GetRequiredService<ICommand<TOptions>>();
return command.Execute(options);
}

private static void LogOptions(BaseSubOptions options)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// -----------------------------------------------------------------------
// <copyright file="AddAssetsCommandTests.cs" company="GitTools Contributors">
// Copyright (c) 2015 - Present - GitTools Contributors
// </copyright>
// -----------------------------------------------------------------------

using System.Collections.Generic;
using System.Threading.Tasks;
using GitReleaseManager.Core.Commands;
using GitReleaseManager.Core.Options;
using NSubstitute;
using NUnit.Framework;
using Serilog;
using Shouldly;

namespace GitReleaseManager.Core.Tests.Commands
{
[TestFixture]
public class AddAssetsCommandTests
{
private IVcsService _vcsService;
private ILogger _logger;
private AddAssetsCommand _command;

[SetUp]
public void Setup()
{
_vcsService = Substitute.For<IVcsService>();
_logger = Substitute.For<ILogger>();
_command = new AddAssetsCommand(_vcsService, _logger);
}

[Test]
public async Task Should_Execute_Command()
{
var options = new AddAssetSubOptions
{
RepositoryOwner = "owner",
RepositoryName = "repository",
TagName = "0.1.0",
AssetPaths = new List<string>(),
};

_vcsService.AddAssetsAsync(options.RepositoryOwner, options.RepositoryName, options.TagName, options.AssetPaths).
Returns(Task.CompletedTask);

var result = await _command.Execute(options).ConfigureAwait(false);
result.ShouldBe(0);

await _vcsService.Received(1).AddAssetsAsync(options.RepositoryOwner, options.RepositoryName, options.TagName, options.AssetPaths).ConfigureAwait(false);
_logger.Received(1).Information(Arg.Any<string>());
}
}
}
52 changes: 52 additions & 0 deletions Source/GitReleaseManager.Core.Tests/Commands/CloseCommandTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// -----------------------------------------------------------------------
// <copyright file="CloseCommandTests.cs" company="GitTools Contributors">
// Copyright (c) 2015 - Present - GitTools Contributors
// </copyright>
// -----------------------------------------------------------------------

using System.Threading.Tasks;
using GitReleaseManager.Core.Commands;
using GitReleaseManager.Core.Options;
using NSubstitute;
using NUnit.Framework;
using Serilog;
using Shouldly;

namespace GitReleaseManager.Core.Tests.Commands
{
[TestFixture]
public class CloseCommandTests
{
private IVcsService _vcsService;
private ILogger _logger;
private CloseCommand _command;

[SetUp]
public void Setup()
{
_vcsService = Substitute.For<IVcsService>();
_logger = Substitute.For<ILogger>();
_command = new CloseCommand(_vcsService, _logger);
}

[Test]
public async Task Should_Execute_Command()
{
var options = new CloseSubOptions
{
RepositoryOwner = "owner",
RepositoryName = "repository",
Milestone = "0.1.0",
};

_vcsService.CloseMilestoneAsync(options.RepositoryOwner, options.RepositoryName, options.Milestone)
.Returns(Task.CompletedTask);

var result = await _command.Execute(options).ConfigureAwait(false);
result.ShouldBe(0);

await _vcsService.Received(1).CloseMilestoneAsync(options.RepositoryOwner, options.RepositoryName, options.Milestone).ConfigureAwait(false);
_logger.Received(1).Information(Arg.Any<string>(), options.Milestone);
}
}
}
Loading