Skip to content
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

Add CIDeployAppCommand which allow to deploy application in CI environment #19

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
34 changes: 34 additions & 0 deletions src/AppHarbor/AccessTokenHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using RestSharp;
using RestSharp.Contrib;

namespace AppHarbor
{
class AccessTokenHelper
{
/// <summary>
/// Get access token.
/// </summary>
/// <param name="username"></param>
/// <param name="password"></param>
/// <returns></returns>
public static string GetAccessToken(string username, string password)
{
//NOTE: Remove when merged into AppHarbor.NET library
var restClient = new RestClient("https://appharbor-token-client.apphb.com");
var request = new RestRequest("/token", Method.POST);

request.AddParameter("username", username);
request.AddParameter("password", password);

var response = restClient.Execute(request);
var accessToken = HttpUtility.ParseQueryString(response.Content)["access_token"];

if (accessToken == null)
{
throw new CommandException("Couldn't log in. Try again");
}

return accessToken;
}
}
}
5 changes: 5 additions & 0 deletions src/AppHarbor/AppHarbor.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="AccessTokenConfiguration.cs" />
<Compile Include="AccessTokenHelper.cs" />
<Compile Include="AliasMatcher.cs" />
<Compile Include="ApiException.cs" />
<Compile Include="AppHarborCliClient.cs" />
Expand All @@ -85,6 +86,7 @@
<Compile Include="Commands\ApplicationCommand.cs" />
<Compile Include="Commands\BuildCommand.cs" />
<Compile Include="Command.cs" />
<Compile Include="Commands\CIDeployAppCommand.cs" />
<Compile Include="Commands\ConfigCommand.cs" />
<Compile Include="Commands\DeleteAppCommand.cs" />
<Compile Include="Commands\CreateAppCommand.cs" />
Expand All @@ -102,6 +104,7 @@
<Compile Include="Commands\UserCommand.cs" />
<Compile Include="CompressionExtensions.cs" />
<Compile Include="ConsoleProgressBar.cs" />
<Compile Include="ConsoleWindowHelper.cs" />
<Compile Include="DispatchException.cs" />
<Compile Include="FederatedUploadCredentials.cs" />
<Compile Include="ForegroundColor.cs" />
Expand All @@ -121,8 +124,10 @@
<Compile Include="ITypeNameMatcher.cs" />
<Compile Include="MaskedConsoleInput.cs" />
<Compile Include="MegaByteProgressBar.cs" />
<Compile Include="NullProgressBar.cs" />
<Compile Include="PhysicalFileSystem.cs" />
<Compile Include="Program.cs" />
<Compile Include="IProgressBar.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RepositoryConfigurationException.cs" />
<Compile Include="TemporaryFileStream.cs" />
Expand Down
20 changes: 20 additions & 0 deletions src/AppHarbor/AppHarborInstaller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,26 @@ public void Install(IWindsorContainer container, IConfigurationStore store)
container.Register(Component
.For<IMaskedInput>()
.ImplementedBy<MaskedConsoleInput>());

RegisterProgressBar(container);
}

private static void RegisterProgressBar(IWindsorContainer container)
{
if (ConsoleWindowHelper.HasConsoleWindow)
{
container.Register(Component
.For<IProgressBar>()
.ImplementedBy<MegaByteProgressBar>()
.LifeStyle.Transient);
}
else
{
container.Register(Component
.For<IProgressBar>()
.ImplementedBy<NullProgressBar>()
.LifeStyle.Transient);
}
}
}
}
147 changes: 147 additions & 0 deletions src/AppHarbor/Commands/CIDeployAppCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using Amazon.S3;
using Amazon.S3.Transfer;
using RestSharp;

namespace AppHarbor.Commands
{
[CommandHelp("Deploy application in CI environment", alias: "ci-deploy")]
public class CIDeployAppCommand : ApplicationCommand
{
private string _message;
private DirectoryInfo _sourceDirectory;
private string _username;
private string _password;

private readonly IRestClient _restClient;
private readonly TextWriter _writer;
private readonly IProgressBar _progressBar;

private readonly IList<string> _excludedDirectories;

public CIDeployAppCommand(IApplicationConfiguration applicationConfiguration, TextWriter writer, IProgressBar progressBar)
: base(applicationConfiguration)
{
_restClient = new RestClient("https://packageclient.apphb.com/");
_writer = writer;
_progressBar = progressBar;

_sourceDirectory = new DirectoryInfo(Directory.GetCurrentDirectory());
OptionSet.Add("source-directory=", "Set source directory", x => _sourceDirectory = new DirectoryInfo(x));

_excludedDirectories = new List<string> { ".git", ".hg" };
OptionSet.Add("e|excluded-directory=", "Add excluded directory name", x => _excludedDirectories.Add(x));

OptionSet.Add("m|message=", "Specify commit message", x => _message = x);

OptionSet.Add("u|user=", "Optional. Specify the user to use", x => _username = x);
OptionSet.Add("p|password=", "Optional. Specify the password of the user", x => _password = x);
}

protected override void InnerExecute(string[] arguments)
{
_writer.WriteLine("Ensure login credentials...");
string accessToken = GetAccessToken();
_writer.WriteLine();

_writer.WriteLine("Getting upload credentials... ");
_writer.WriteLine();

var uploadCredentials = GetCredentials();

var temporaryFileName = Path.GetTempFileName();
try
{
using (var packageStream = new FileStream(temporaryFileName, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite))
using (var gzipStream = new GZipStream(packageStream, CompressionMode.Compress, true))
{
_sourceDirectory.ToTar(gzipStream, excludedDirectoryNames: _excludedDirectories.ToArray(), progressBar: _progressBar);
}

using (var s3Client = new AmazonS3Client(uploadCredentials.GetSessionCredentials()))
using (var transferUtility = new TransferUtility(s3Client))
{
var request = new TransferUtilityUploadRequest
{
FilePath = temporaryFileName,
BucketName = uploadCredentials.Bucket,
Key = uploadCredentials.ObjectKey,
Timeout = (int)TimeSpan.FromHours(2).TotalMilliseconds,
};

request.UploadProgressEvent += (object x, UploadProgressArgs y) => _progressBar
.Update("Uploading package", y.TransferredBytes, y.TotalBytes);

transferUtility.Upload(request);

Console.CursorTop++;
_writer.WriteLine();
}
}
finally
{
File.Delete(temporaryFileName);
}

TriggerAppHarborBuild(uploadCredentials, accessToken);
}

private FederatedUploadCredentials GetCredentials()
{
var urlRequest = new RestRequest("applications/{slug}/uploadCredentials", Method.POST);
urlRequest.AddUrlSegment("slug", ApplicationId);

var federatedCredentials = _restClient.Execute<FederatedUploadCredentials>(urlRequest);
return federatedCredentials.Data;
}

private void TriggerAppHarborBuild(FederatedUploadCredentials credentials, string accessToken)
{
_writer.WriteLine("The package will be deployed to application \"{0}\".", ApplicationId);

if (string.IsNullOrEmpty(_message))
{
_message = string.Format("CI Deployment at {0}", DateTime.Now);
}

var request = new RestRequest("applications/{slug}/buildnotifications", Method.POST)
{
RequestFormat = DataFormat.Json
}
.AddUrlSegment("slug", ApplicationId)
.AddHeader("Authorization", string.Format("BEARER {0}", accessToken))
.AddBody(new
{
Bucket = credentials.Bucket,
ObjectKey = credentials.ObjectKey,
CommitMessage = string.IsNullOrEmpty(_message) ? "Deployed from CLI" : _message,
});

_writer.WriteLine("Notifying AppHarbor.");

var response = _restClient.Execute(request);

if (response.StatusCode == HttpStatusCode.OK)
{
using (new ForegroundColor(ConsoleColor.Green))
{
_writer.WriteLine("Deploying... Open application overview with `appharbor open`.");
}
}
}

private string GetAccessToken()
{
// Request new access token using the specific
string accessToken = AccessTokenHelper.GetAccessToken(_username, _password);
_writer.WriteLine("Logged in with the username " + _username);

return accessToken;
}
}
}
9 changes: 5 additions & 4 deletions src/AppHarbor/Commands/DeployAppCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,18 @@ public class DeployAppCommand : ApplicationCommand
private readonly IRestClient _restClient;
private readonly TextReader _reader;
private readonly TextWriter _writer;
private readonly IProgressBar _progressBar;

private readonly IList<string> _excludedDirectories;

public DeployAppCommand(IApplicationConfiguration applicationConfiguration, IAccessTokenConfiguration accessTokenConfiguration, TextReader reader, TextWriter writer)
public DeployAppCommand(IApplicationConfiguration applicationConfiguration, IAccessTokenConfiguration accessTokenConfiguration, TextReader reader, TextWriter writer, IProgressBar progressBar)
: base(applicationConfiguration)
{
_accessToken = accessTokenConfiguration.GetAccessToken();
_restClient = new RestClient("https://packageclient.apphb.com/");
_reader = reader;
_writer = writer;
_progressBar = progressBar;

_sourceDirectory = new DirectoryInfo(Directory.GetCurrentDirectory());
OptionSet.Add("source-directory=", "Set source directory", x => _sourceDirectory = new DirectoryInfo(x));
Expand All @@ -53,7 +55,7 @@ protected override void InnerExecute(string[] arguments)
using (var packageStream = new FileStream(temporaryFileName, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite))
using (var gzipStream = new GZipStream(packageStream, CompressionMode.Compress, true))
{
_sourceDirectory.ToTar(gzipStream, excludedDirectoryNames: _excludedDirectories.ToArray());
_sourceDirectory.ToTar(gzipStream, excludedDirectoryNames: _excludedDirectories.ToArray(), progressBar: _progressBar);
}

using (var s3Client = new AmazonS3Client(uploadCredentials.GetSessionCredentials()))
Expand All @@ -67,8 +69,7 @@ protected override void InnerExecute(string[] arguments)
Timeout = (int)TimeSpan.FromHours(2).TotalMilliseconds,
};

var progressBar = new MegaByteProgressBar();
request.UploadProgressEvent += (object x, UploadProgressArgs y) => progressBar
request.UploadProgressEvent += (object x, UploadProgressArgs y) => _progressBar
.Update("Uploading package", y.TransferredBytes, y.TotalBytes);

transferUtility.Upload(request);
Expand Down
17 changes: 1 addition & 16 deletions src/AppHarbor/Commands/LoginUserCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,22 +41,7 @@ protected override void InnerExecute(string[] arguments)

public virtual string GetAccessToken(string username, string password)
{
//NOTE: Remove when merged into AppHarbor.NET library
var restClient = new RestClient("https://appharbor-token-client.apphb.com");
var request = new RestRequest("/token", Method.POST);

request.AddParameter("username", username);
request.AddParameter("password", password);

var response = restClient.Execute(request);
var accessToken = HttpUtility.ParseQueryString(response.Content)["access_token"];

if (accessToken == null)
{
throw new CommandException("Couldn't log in. Try again");
}

return accessToken;
return AccessTokenHelper.GetAccessToken(username, password);
}
}
}
3 changes: 1 addition & 2 deletions src/AppHarbor/CompressionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace AppHarbor
{
public static class CompressionExtensions
{
public static void ToTar(this DirectoryInfo sourceDirectory, Stream output, string[] excludedDirectoryNames)
public static void ToTar(this DirectoryInfo sourceDirectory, Stream output, string[] excludedDirectoryNames, IProgressBar progressBar)
{
var archive = TarArchive.CreateOutputTarArchive(output);

Expand All @@ -19,7 +19,6 @@ public static void ToTar(this DirectoryInfo sourceDirectory, Stream output, stri

var entriesCount = entries.Count();

var progressBar = new MegaByteProgressBar();
for (var i = 0; i < entriesCount; i++)
{
archive.WriteEntry(entries[i], true);
Expand Down
2 changes: 1 addition & 1 deletion src/AppHarbor/ConsoleProgressBar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace AppHarbor
{
public abstract class ConsoleProgressBar
public abstract class ConsoleProgressBar : IProgressBar
{
private const char ProgressBarCharacter = '\u2592';

Expand Down
32 changes: 32 additions & 0 deletions src/AppHarbor/ConsoleWindowHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;

namespace AppHarbor
{
/// <summary>
/// Helper class for console window information.
/// </summary>
static class ConsoleWindowHelper
{
/// <summary>
/// Get indication if a console window is available or we run
/// without a window (in CI environment).
/// </summary>
public static bool HasConsoleWindow
{
get
{
bool hasConsoleWindow;
try
{
int w = Console.BufferWidth;
hasConsoleWindow = true;
}
catch (Exception)
{
hasConsoleWindow = false;
}
return hasConsoleWindow;
}
}
}
}
16 changes: 16 additions & 0 deletions src/AppHarbor/IProgressBar.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace AppHarbor
{
/// <summary>
/// Represent a progress bar that show status updates.
/// </summary>
public interface IProgressBar
{
/// <summary>
/// Show update on the progress.
/// </summary>
/// <param name="message"></param>
/// <param name="processedItems"></param>
/// <param name="totalItems"></param>
void Update(string message, long processedItems, long totalItems);
}
}
13 changes: 13 additions & 0 deletions src/AppHarbor/NullProgressBar.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

namespace AppHarbor
{
/// <summary>
/// Reperesent a null progress bar which does nothing.
/// </summary>
public class NullProgressBar : IProgressBar
{
public void Update(string message, long processedItems, long totalItems)
{
}
}
}