Permalink
Browse files

dropbox support and functional test

  • Loading branch information...
1 parent 710bc89 commit 513bed08cbb8530f40b447e16e923854ba6011bb @suwatch suwatch committed Nov 20, 2012
Showing with 951 additions and 93 deletions.
  1. +19 −0 Kudu.Contracts/Dropbox/DropboxDeltaInfo.cs
  2. +26 −0 Kudu.Contracts/Dropbox/DropboxDeployInfo.cs
  3. +2 −0 Kudu.Contracts/Kudu.Contracts.csproj
  4. +3 −0 Kudu.Contracts/SourceControl/IServerRepository.cs
  5. +10 −2 Kudu.Contracts/Tracing/TraceExtensions.cs
  6. +4 −0 Kudu.Core/Deployment/DeploymentManager.cs
  7. +2 −2 Kudu.Core/SourceControl/Git/GitExeRepository.cs
  8. +18 −0 Kudu.Core/SourceControl/Git/GitExeServer.cs
  9. +347 −0 Kudu.FunctionalTests/DropboxTests.cs
  10. +2 −0 Kudu.FunctionalTests/Kudu.FunctionalTests.csproj
  11. 0 Kudu.FunctionalTests/dropbox/oauth.json
  12. +3 −2 Kudu.Services.Web/App_Start/NinjectServices.cs
  13. +263 −0 Kudu.Services/Dropbox/DropboxHelper.cs
  14. +14 −36 Kudu.Services/GitServer/FetchHandler.cs
  15. +1 −18 Kudu.Services/GitServer/GitServerHttpHandler.cs
  16. +14 −0 Kudu.Services/GitServer/Helpers.cs
  17. +1 −1 Kudu.Services/GitServer/ReceivePackHandler.cs
  18. +7 −0 Kudu.Services/GitServer/ServiceHookHandlers/BitbucketHandler.cs
  19. +51 −0 Kudu.Services/GitServer/ServiceHookHandlers/CodePlexHandler.cs
  20. +6 −0 Kudu.Services/GitServer/ServiceHookHandlers/CodebaseHqHandler.cs
  21. +93 −0 Kudu.Services/GitServer/ServiceHookHandlers/DropboxHandler.cs
  22. +0 −25 Kudu.Services/GitServer/ServiceHookHandlers/FallbackHandler.cs
  23. +7 −1 Kudu.Services/GitServer/ServiceHookHandlers/GithubHandler.cs
  24. +6 −0 Kudu.Services/GitServer/ServiceHookHandlers/GitlabHqHandler.cs
  25. +2 −0 Kudu.Services/GitServer/ServiceHookHandlers/IServiceHookHandler.cs
  26. +18 −2 Kudu.Services/GitServer/ServiceHookHandlers/JsonServiceHookHandler.cs
  27. +1 −0 Kudu.Services/GitServer/ServiceHookHandlers/RepositoryInfo.cs
  28. +1 −1 Kudu.Services/GitServer/UploadPackHandler.cs
  29. +5 −2 Kudu.Services/Kudu.Services.csproj
  30. +19 −1 Kudu.Services/Resources.Designer.cs
  31. +6 −0 Kudu.Services/Resources.resx
View
19 Kudu.Contracts/Dropbox/DropboxDeltaInfo.cs
@@ -0,0 +1,19 @@
+using System;
+
+namespace Kudu.Contracts.Dropbox
+{
+ public class DropboxDeltaInfo
+ {
+ public string Path { get; set; }
+
+ public string Nonce { get; set; }
+
+ public string Signature { get; set; }
+
+ public bool IsDirectory { get; set; }
+
+ public bool IsDeleted { get; set; }
+
+ public string Modified { get; set; }
+ }
+}
View
26 Kudu.Contracts/Dropbox/DropboxDeployInfo.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+
+namespace Kudu.Contracts.Dropbox
+{
+ public class DropboxDeployInfo
+ {
+ public string ConsumerKey { get; set; }
+
+ public string SignatureMethod { get; set; }
+
+ public string TimeStamp { get; set; }
+
+ public string OAuthVersion { get; set; }
+
+ public string Token { get; set; }
+
+ public string OldCursor { get; set; }
+
+ public string NewCursor { get; set; }
+
+ public string Path { get; set; }
+
+ public IEnumerable<DropboxDeltaInfo> Deltas { get; set; }
+ }
+}
View
2 Kudu.Contracts/Kudu.Contracts.csproj
@@ -57,6 +57,8 @@
<Compile Include="Deployment\IDeploymentManagerFactory.cs" />
<Compile Include="Deployment\LogEntry.cs" />
<Compile Include="Deployment\LogEntryType.cs" />
+ <Compile Include="Dropbox\DropboxDeltaInfo.cs" />
+ <Compile Include="Dropbox\DropboxDeployInfo.cs" />
<Compile Include="Editor\IProjectSystem.cs" />
<Compile Include="Editor\Project.cs" />
<Compile Include="Editor\VfsStatEntry.cs" />
View
3 Kudu.Contracts/SourceControl/IServerRepository.cs
@@ -5,11 +5,14 @@ namespace Kudu.Core.SourceControl
public interface IServerRepository
{
bool Exists { get; }
+ string CurrentId { get; }
bool Initialize(RepositoryConfiguration configuration);
ChangeSet Initialize(RepositoryConfiguration configuration, string path);
RepositoryType GetRepositoryType();
void Clean();
void FetchWithoutConflict(string remoteUrl, string remoteAlias, string branchName);
void SetReceiveInfo(string oldRef, string newRef, string branchName);
+ ChangeSet Commit(string message, string authorName);
+ void Update(string id);
}
}
View
12 Kudu.Contracts/Tracing/TraceExtensions.cs
@@ -19,12 +19,20 @@ public static void Trace(this ITracer tracer, string message, params object[] ar
public static void TraceError(this ITracer tracer, Exception ex)
{
- tracer.Trace("Error occured", new Dictionary<string, string>
+ var attribs = new Dictionary<string, string>
{
{ "type", "error" },
{ "text", ex.Message },
{ "stackTrace", ex.StackTrace ?? String.Empty }
- });
+ };
+
+ if (ex.InnerException != null)
+ {
+ attribs["innerText"] = ex.InnerException.Message;
+ attribs["innerStackTrace"] = ex.InnerException.StackTrace ?? String.Empty;
+ }
+
+ tracer.Trace("Error occured", attribs);
}
public static void TraceError(this ITracer tracer, string message)
View
4 Kudu.Core/Deployment/DeploymentManager.cs
@@ -343,6 +343,10 @@ public IDisposable CreateTemporaryDeployment(string statusText)
using (tracer.Step("Creating temporary deployment"))
{
DeploymentStatusFile statusFile = CreateStatusFile(id);
+ statusFile.Message = "N/A";
+ statusFile.Author = "N/A";
+ statusFile.Deployer = "N/A";
+ statusFile.AuthorEmail = "N/A";
statusFile.Status = DeployStatus.Pending;
statusFile.StatusText = statusText;
statusFile.Save(_fileSystem);
View
4 Kudu.Core/SourceControl/Git/GitExeRepository.cs
@@ -137,11 +137,11 @@ public ChangeSet Commit(string message, string authorName = null)
string output;
if (authorName == null)
{
- output = _gitExe.Execute(tracer, "commit -m\"{0}\"", message).Item1;
+ output = _gitExe.Execute(tracer, "commit -m \"{0}\"", message).Item1;
}
else
{
- output = _gitExe.Execute("commit -m\"{0}\" --author=\"{1}\"", message, authorName).Item1;
+ output = _gitExe.Execute(tracer, "commit -m \"{0}\" --author=\"{1}\"", message, authorName).Item1;
}
// No pending changes
View
18 Kudu.Core/SourceControl/Git/GitExeServer.cs
@@ -38,6 +38,14 @@ public GitExeServer(string path, string homePath, IOperationLock initLock, strin
_gitExe.EnvironmentVariables[KnownEnviornment.DEPLOYER] = "";
}
+ public string CurrentId
+ {
+ get
+ {
+ return _repository.CurrentId;
+ }
+ }
+
private string PostReceiveHookPath
{
get
@@ -217,6 +225,16 @@ public void SetReceiveInfo(string oldRef, string newRef, string branchName)
File.WriteAllText(PushInfoPath, oldRef + " " + newRef + " " + branchName);
}
+ public ChangeSet Commit(string message, string authorName)
+ {
+ return _repository.Commit(message, authorName);
+ }
+
+ public void Update(string id)
+ {
+ _repository.Update(id);
+ }
+
/// <summary>
/// Environment variables used for the post receive hook
/// </summary>
View
347 Kudu.FunctionalTests/DropboxTests.cs
@@ -0,0 +1,347 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Reflection;
+using System.Security.Cryptography;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Web;
+using Kudu.Client.Infrastructure;
+using Kudu.Contracts.Dropbox;
+using Kudu.FunctionalTests.Infrastructure;
+using Kudu.TestHarness;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using Xunit;
+
+namespace Kudu.FunctionalTests
+{
+ public class DropboxTests
+ {
+ static Random _random = new Random(unchecked((int)DateTime.Now.Ticks));
+
+ [Fact]
+ public void TestDropboxBasic()
+ {
+ OAuthInfo oauth = GetOAuthInfo();
+ if (oauth == null)
+ {
+ // only run in private kudu
+ return;
+ }
+
+ AccountInfo account = GetAccountInfo(oauth);
+ DropboxDeployInfo deploy = GetDeployInfo(oauth);
+
+ string appName = KuduUtils.GetRandomWebsiteName("DropboxTest");
+ ApplicationManager.Run(appName, appManager =>
+ {
+ appManager.SettingsManager.SetValues(
+ new KeyValuePair<string, string>("dropbox_username", account.display_name),
+ new KeyValuePair<string, string>("dropbox_email", account.email)
+ );
+
+ HttpClient client = new HttpClient();
+ client.BaseAddress = new Uri(appManager.ServiceUrl);
+ client.DefaultRequestHeaders.Add("user-agent", "dropbox");
+ if (appManager.DeploymentManager.Credentials != null)
+ {
+ client.SetClientCredentials(appManager.DeploymentManager.Credentials);
+ }
+ client.PostAsJsonAsync("deploy", deploy).Result.EnsureSuccessStatusCode();
+
+ KuduAssert.VerifyUrl(appManager.SiteUrl + "/default.html", "Hello Default!");
+ KuduAssert.VerifyUrl(appManager.SiteUrl + "/temp/temp.html", "Hello Temp!");
+ });
+ }
+
+ private OAuthInfo GetOAuthInfo()
+ {
+ Assembly assembly = Assembly.GetExecutingAssembly();
+ using (var reader = new JsonTextReader(new StreamReader(assembly.GetManifestResourceStream("Kudu.FunctionalTests.dropbox.oauth.json"))))
+ {
+ JsonSerializer serializer = new JsonSerializer();
+ return serializer.Deserialize<OAuthInfo>(reader);
+ }
+ }
+
+ private AccountInfo GetAccountInfo(OAuthInfo oauth)
+ {
+ var uri = new Uri("https://api.dropbox.com/1/account/info");
+ var client = GetDropboxClient(HttpMethod.Get, uri, oauth);
+ var response = client.GetAsync(uri.PathAndQuery).Result.EnsureSuccessStatusCode();
+ using (var reader = new JsonTextReader(new StreamReader(response.Content.ReadAsStreamAsync().Result)))
+ {
+ JsonSerializer serializer = new JsonSerializer();
+ return serializer.Deserialize<AccountInfo>(reader);
+ }
+ }
+
+ private DeltaInfo GetDeltaInfo(OAuthInfo oauth, string cursor = null)
+ {
+ Uri uri;
+ HttpClient client;
+ if (!String.IsNullOrEmpty(cursor))
+ {
+ uri = new Uri("https://api.dropbox.com/1/delta/?cursor=" + cursor);
+ client = GetDropboxClient(HttpMethod.Post, uri, oauth, new KeyValuePair<string, string>("cursor", cursor));
+ }
+ else
+ {
+ uri = new Uri("https://api.dropbox.com/1/delta");
+ client = GetDropboxClient(HttpMethod.Post, uri, oauth);
+ }
+
+ var response = client.PostAsync(uri.PathAndQuery, null).Result.EnsureSuccessStatusCode();
+ using (var reader = new JsonTextReader(new StreamReader(response.Content.ReadAsStreamAsync().Result)))
+ {
+ JsonSerializer serializer = new JsonSerializer();
+ return new DeltaInfo(serializer.Deserialize<JObject>(reader));
+ }
+ }
+
+ private DropboxDeployInfo GetDeployInfo(OAuthInfo oauth, string cursor = null)
+ {
+ List<DropboxDeltaInfo> deltas = new List<DropboxDeltaInfo>();
+ string timeStamp = GetUtcTimeStamp();
+ string oldCursor = cursor;
+ string newCursor = "";
+ while (true)
+ {
+ DeltaInfo delta = GetDeltaInfo(oauth, cursor);
+ newCursor = delta.cursor;
+ if (newCursor == oldCursor)
+ {
+ break;
+ }
+
+ foreach (EntryInfo info in delta.entries)
+ {
+ DropboxDeltaInfo item = new DropboxDeltaInfo { Path = info.path };
+ if (info.metadata == null || info.metadata.is_deleted || string.IsNullOrEmpty(info.metadata.path))
+ {
+ item.IsDeleted = true;
+ }
+ else
+ {
+ item.IsDirectory = info.metadata.is_dir;
+ if (!item.IsDirectory)
+ {
+ item.Modified = info.metadata.modified;
+ item.Nonce = GetNonce();
+ item.Signature = GetSignature(oauth, info.path, timeStamp, item.Nonce);
+ }
+ }
+
+ deltas.Add(item);
+ }
+
+ if (!delta.has_more)
+ {
+ break;
+ }
+ }
+
+ if (deltas.Count == 0)
+ {
+ throw new InvalidOperationException("the repo is up-to-date.");
+ }
+
+ return new DropboxDeployInfo
+ {
+ TimeStamp = timeStamp,
+ Token = oauth.Token,
+ ConsumerKey = oauth.ConsumerKey,
+ OAuthVersion = "1.0",
+ SignatureMethod = "HMAC-SHA1",
+ OldCursor = oldCursor,
+ NewCursor = newCursor,
+ Path = "/",
+ Deltas = deltas
+ };
+ }
+
+ private HttpClient GetDropboxClient(HttpMethod method, Uri uri, OAuthInfo oauth, params KeyValuePair<string, string>[] query)
+ {
+ var parameters = new Dictionary<string, string>
+ {
+ { "oauth_consumer_key", oauth.ConsumerKey },
+ { "oauth_signature_method", "HMAC-SHA1" },
+ { "oauth_timestamp", GetUtcTimeStamp() },
+ { "oauth_nonce", GetNonce() },
+ { "oauth_version", "1.0" },
+ };
+
+ if (!string.IsNullOrEmpty(oauth.Token))
+ {
+ parameters["oauth_token"] = oauth.Token;
+ }
+
+ var pp = new Dictionary<string, string>(parameters);
+ foreach (KeyValuePair<string, string> pair in query)
+ {
+ pp.Add(pair.Key, pair.Value);
+ }
+
+ var strb = new StringBuilder();
+ foreach (var pair in pp.OrderBy(pair => pair.Key, StringComparer.OrdinalIgnoreCase))
+ {
+ if (strb.Length != 0)
+ {
+ strb.Append('&');
+ }
+
+ strb.AppendFormat("{0}={1}", pair.Key, pair.Value);
+ }
+
+ string data = string.Format(
+ "{0}&{1}&{2}",
+ method.ToString().ToUpperInvariant(),
+ UrlEncode(uri.AbsoluteUri.Split('?')[0]),
+ UrlEncode(strb.ToString()));
+
+ string key = string.Format(
+ "{0}&{1}",
+ UrlEncode(oauth.ConsumerSecret),
+ string.IsNullOrEmpty(oauth.TokenSecret) ? string.Empty : UrlEncode(oauth.TokenSecret));
+
+ HMACSHA1 hmacSha1 = new HMACSHA1();
+ hmacSha1.Key = Encoding.ASCII.GetBytes(key);
+ byte[] hashBytes = hmacSha1.ComputeHash(Encoding.ASCII.GetBytes(data));
+
+ parameters.Add("oauth_signature", Convert.ToBase64String(hashBytes));
+
+ strb = new StringBuilder();
+ foreach (KeyValuePair<string, string> pair in parameters)
+ {
+ if (strb.Length != 0)
+ {
+ strb.Append(',');
+ }
+
+ strb.AppendFormat("{0}=\"{1}\"", pair.Key, pair.Value);
+ }
+
+ var client = new HttpClient();
+ client.BaseAddress = new Uri(string.Format("{0}://{1}", uri.Scheme, uri.Host));
+ client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("OAuth", strb.ToString());
+ return client;
+ }
+
+ private string GetSignature(OAuthInfo oauth, string path, string timeStamp, string nonce)
+ {
+ var strb = new StringBuilder();
+ strb.AppendFormat("{0}={1}", "oauth_consumer_key", oauth.ConsumerKey);
+ strb.AppendFormat("&{0}={1}", "oauth_nonce", nonce);
+ strb.AppendFormat("&{0}={1}", "oauth_signature_method", "HMAC-SHA1");
+ strb.AppendFormat("&{0}={1}", "oauth_timestamp", timeStamp);
+ strb.AppendFormat("&{0}={1}", "oauth_token", oauth.Token);
+ strb.AppendFormat("&{0}={1}", "oauth_version", "1.0");
+
+ string data = String.Format("{0}&{1}&{2}",
+ "GET",
+ UrlEncode("https://api-content.dropbox.com/1/files/sandbox" + path),
+ UrlEncode(strb.ToString()));
+
+ var key = String.Format("{0}&{1}",
+ UrlEncode(oauth.ConsumerSecret),
+ UrlEncode(oauth.TokenSecret));
+
+ HMACSHA1 hmacSha1 = new HMACSHA1();
+ hmacSha1.Key = Encoding.ASCII.GetBytes(key);
+ byte[] hashBytes = hmacSha1.ComputeHash(Encoding.ASCII.GetBytes(data));
+ return Convert.ToBase64String(hashBytes);
+ }
+
+ private string UrlEncode(string str)
+ {
+ Regex reg = new Regex("%[a-f0-9]{2}");
+ return reg.Replace(HttpUtility.UrlEncode(str), m => m.Value.ToUpperInvariant());
+ }
+
+ private string GetUtcTimeStamp()
+ {
+ TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
+ return Convert.ToInt64(ts.TotalSeconds).ToString();
+ }
+
+ private string GetNonce()
+ {
+ const string unreserved = "-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz";
+
+ var chars = new char[8];
+ for (int i = 0; i < 8; ++i)
+ {
+ chars[i] = unreserved[_random.Next(unreserved.Length)];
+ }
+ return new String(chars);
+ }
+
+ public class OAuthInfo
+ {
+ public string ConsumerKey { get; set; }
+ public string ConsumerSecret { get; set; }
+ public string Token { get; set; }
+ public string TokenSecret { get; set; }
+ }
+
+ public class AccountInfo
+ {
+ public string display_name { get; set; }
+ public string email { get; set; }
+ }
+
+ public class DeltaInfo
+ {
+ public DeltaInfo(JObject json)
+ {
+ cursor = (string)json["cursor"];
+ has_more = (bool)json["has_more"];
+ entries = new List<EntryInfo>();
+ foreach (JArray entry in json["entries"])
+ {
+ entries.Add(new EntryInfo(entry));
+ }
+ }
+
+ public string cursor { get; set; }
+ public bool has_more { get; set; }
+ public List<EntryInfo> entries { get; set; }
+ }
+
+ public class EntryInfo
+ {
+ public EntryInfo(JArray json)
+ {
+ path = (string)json[0];
+ metadata = json[1] is JObject ? new Metadata((JObject)json[1]) : null;
+ }
+
+ public string path { get; set; }
+ public Metadata metadata { get; set; }
+ }
+
+ public class Metadata
+ {
+ public Metadata(JObject json)
+ {
+ path = (string)json["path"];
+ is_dir = json["is_dir"] == null ? false : (bool)json["is_dir"];
+ is_deleted = json["is_deleted"] == null ? false : (bool)json["is_deleted"];
+ if (!is_dir && !is_deleted)
+ {
+ modified = (string)json["modified"];
+ }
+ }
+
+ public string path { get; set; }
+ public bool is_dir { get; set; }
+ public bool is_deleted { get; set; }
+ public string modified { get; set; }
+ }
+ }
+}
+
View
2 Kudu.FunctionalTests/Kudu.FunctionalTests.csproj
@@ -84,6 +84,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="DeploymentManagerTests.cs" />
+ <Compile Include="DropboxTests.cs" />
<Compile Include="GitRepositoryManagementTests.cs" />
<Compile Include="GitStabilityTests.cs" />
<Compile Include="Infrastructure\KuduAssert.cs" />
@@ -100,6 +101,7 @@
<EmbeddedResource Include=".ssh\config" />
<EmbeddedResource Include=".ssh\id_rsa" />
<None Include="App.config" />
+ <EmbeddedResource Include="dropbox\oauth.json" />
<None Include="packages.config">
<SubType>Designer</SubType>
</None>
View
0 Kudu.FunctionalTests/dropbox/oauth.json
No changes.
View
5 Kudu.Services.Web/App_Start/NinjectServices.cs
@@ -185,12 +185,13 @@ private static void RegisterServices(IKernel kernel)
.InRequestScope();
// Git Servicehook parsers
+ kernel.Bind<IServiceHookHandler>().To<GitHubHandler>();
kernel.Bind<IServiceHookHandler>().To<BitbucketHandler>();
+ kernel.Bind<IServiceHookHandler>().To<DropboxHandler>();
kernel.Bind<IServiceHookHandler>().To<CodebaseHqHandler>();
kernel.Bind<IServiceHookHandler>().To<GitlabHqHandler>();
- kernel.Bind<IServiceHookHandler>().To<GithubHandler>();
kernel.Bind<IServiceHookHandler>().To<JsonServiceHookHandler>();
- kernel.Bind<IServiceHookHandler>().To<FallbackHandler>();
+ kernel.Bind<IServiceHookHandler>().To<CodePlexHandler>();
// Editor
kernel.Bind<IProjectSystem>().ToMethod(context => GetEditorProjectSystem(environment, context))
View
263 Kudu.Services/Dropbox/DropboxHelper.cs
@@ -0,0 +1,263 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Kudu.Contracts.Dropbox;
+using Kudu.Contracts.Settings;
+using Kudu.Contracts.Tracing;
+using Kudu.Core;
+using Kudu.Core.SourceControl;
+
+namespace Kudu.Services.Dropbox
+{
+ public class DropboxHelper
+ {
+ public const string Dropbox = "dropbox";
+ public const string CursorKey = "dropbox_cursor";
+ public const string UserNameKey = "dropbox_username";
+ public const string EmailKey = "dropbox_email";
+
+ private const string DropboxApiContentUri = "https://api-content.dropbox.com/";
+ private const string SandboxFilePath = "1/files/sandbox";
+ private const int MaxConcurrentRequests = 5;
+
+ private readonly ITracer _tracer;
+ private readonly IServerRepository _repository;
+ private readonly IDeploymentSettingsManager _settings;
+ private readonly IEnvironment _environment;
+
+ public DropboxHelper(ITracer tracer,
+ IServerRepository repository,
+ IDeploymentSettingsManager settings,
+ IEnvironment environment)
+ {
+ _tracer = tracer;
+ _repository = repository;
+ _settings = settings;
+ _environment = environment;
+ }
+
+ public void Sync(DropboxDeployInfo info, string branch)
+ {
+ if (_settings.GetValue(CursorKey) != info.OldCursor)
+ {
+ throw new InvalidOperationException(Resources.Error_MismatchDropboxCursor);
+ }
+
+ if (!IsEmptyRepo())
+ {
+ // git checkout --force <branch>
+ _repository.Update(branch);
+ }
+
+ string prefix = "Partially";
+ try
+ {
+ using (_tracer.Step("Synch with Dropbox"))
+ {
+ // Sync dropbox => repository directory
+ ApplyChanges(info);
+ }
+
+ prefix = "Successfully";
+ }
+ finally
+ {
+ // Commit anyway even partial change
+ _repository.Commit(prefix + " sync with dropbox at " + DateTime.UtcNow.ToString("g"), GetAuthor());
+ }
+
+ // Save new dropboc cursor
+ _tracer.Trace("Update dropbox cursor");
+ _settings.SetValue(CursorKey, info.NewCursor);
+ }
+
+ private bool IsEmptyRepo()
+ {
+ try
+ {
+ return string.IsNullOrEmpty(_repository.CurrentId);
+ }
+ catch (Exception)
+ {
+ return true;
+ }
+ }
+
+ private string GetAuthor()
+ {
+ string userName = _settings.GetValue(UserNameKey);
+ string email = _settings.GetValue(EmailKey);
+ if (!String.IsNullOrEmpty(userName) && !String.IsNullOrEmpty(email))
+ {
+ return String.Format("{0} <{1}>", userName, email);
+ }
+
+ return null;
+ }
+
+ private void ApplyChanges(DropboxDeployInfo info)
+ {
+ Semaphore sem = new Semaphore(MaxConcurrentRequests, MaxConcurrentRequests);
+ List<Task> tasks = new List<Task>();
+ string parent = info.Path.TrimEnd('/') + '/';
+
+ foreach (DropboxDeltaInfo delta in info.Deltas)
+ {
+ if (!delta.Path.StartsWith(parent, StringComparison.OrdinalIgnoreCase))
+ {
+ continue;
+ }
+
+ var path = delta.Path;
+ if (delta.IsDeleted)
+ {
+ SafeDelete(parent, path);
+ }
+ else if (delta.IsDirectory)
+ {
+ SafeCreateDir(parent, path);
+ }
+ else
+ {
+ DateTime modified = DateTime.Parse(delta.Modified).ToUniversalTime();
+ if (!IsFileChanged(parent, path, modified))
+ {
+ _tracer.Trace("file unchanged {0}", path);
+ continue;
+ }
+
+ // throttle concurrent get file dropbox
+ sem.WaitOne();
+ Task task;
+ try
+ {
+ task = GetFileAsync(info, delta).Then(stream =>
+ {
+ using (stream)
+ {
+ SafeWriteFile(parent, path, stream, modified);
+ }
+ }).Catch(catchInfo =>
+ {
+ _tracer.TraceError(catchInfo.Exception);
+ return catchInfo.Throw();
+ })
+ .Finally(() =>
+ {
+ sem.Release();
+ });
+ }
+ catch (Exception ex)
+ {
+ sem.Release();
+ _tracer.TraceError(ex);
+ Task.WaitAll(tasks.ToArray());
+ throw;
+ }
+
+ tasks.Add(task);
+ }
+ }
+
+ Task.WaitAll(tasks.ToArray());
+ }
+
+ private bool IsFileChanged(string parent, string path, DateTime modified)
+ {
+ var file = new System.IO.FileInfo(GetRepositoryPath(parent, path));
+ return !file.Exists || file.LastWriteTimeUtc != modified;
+ }
+
+ private void SafeDelete(string parent, string path)
+ {
+ var fullPath = GetRepositoryPath(parent, path);
+ if (File.Exists(fullPath))
+ {
+ File.Delete(fullPath);
+ _tracer.Trace("del " + path);
+ }
+ else if (Directory.Exists(fullPath))
+ {
+ Directory.Delete(fullPath, recursive: true);
+ _tracer.Trace("rmdir /s " + path);
+ }
+ }
+
+ private void SafeCreateDir(string parent, string path)
+ {
+ var fullPath = GetRepositoryPath(parent, path);
+ if (File.Exists(fullPath))
+ {
+ File.Delete(fullPath);
+ _tracer.Trace("del " + path);
+ }
+
+ if (!Directory.Exists(fullPath))
+ {
+ Directory.CreateDirectory(fullPath);
+ _tracer.Trace("mkdir " + path);
+ }
+ }
+
+ private void SafeWriteFile(string parent, string path, Stream stream, DateTime modified)
+ {
+ var fullPath = GetRepositoryPath(parent, path);
+ if (Directory.Exists(fullPath))
+ {
+ Directory.Delete(fullPath, true);
+ _tracer.Trace("rmdir /s " + path);
+ }
+
+ using (FileStream fs = new FileStream(fullPath, FileMode.Create, FileAccess.Write))
+ {
+ stream.CopyTo(fs);
+ _tracer.Trace("write {0,6} bytes to {1}", fs.Length, path);
+ }
+
+ File.SetLastWriteTimeUtc(fullPath, modified);
+ }
+
+ private string GetRepositoryPath(string parent, string path)
+ {
+ string relativePath = path.Substring(parent.Length).Replace('/', '\\');
+ return Path.Combine(_environment.RepositoryPath, relativePath);
+ }
+
+ private Task<Stream> GetFileAsync(DropboxDeployInfo info, DropboxDeltaInfo delta)
+ {
+ var parameters = new Dictionary<string, string>
+ {
+ { "oauth_consumer_key", info.ConsumerKey },
+ { "oauth_signature_method", info.SignatureMethod },
+ { "oauth_timestamp", info.TimeStamp },
+ { "oauth_nonce", delta.Nonce },
+ { "oauth_version", info.OAuthVersion },
+ { "oauth_token", info.Token },
+ { "oauth_signature", delta.Signature }
+ };
+
+ var sb = new StringBuilder();
+ foreach (var key in parameters.Keys)
+ {
+ if (sb.Length != 0)
+ {
+ sb.Append(',');
+ }
+ sb.AppendFormat("{0}=\"{1}\"", key, parameters[key]);
+ }
+
+ var client = new HttpClient();
+ client.BaseAddress = new Uri(DropboxApiContentUri);
+ client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("OAuth", sb.ToString());
+ return client.GetAsync(SandboxFilePath + delta.Path).Then(response =>
+ {
+ return response.EnsureSuccessStatusCode().Content.ReadAsStreamAsync();
+ }).Finally(() => client.Dispose());
+ }
+ }
+}
View
50 Kudu.Services/GitServer/FetchHandler.cs
@@ -1,8 +1,7 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.IO;
-using System.Linq;
-using System.Text;
using System.Web;
using Kudu.Contracts.Infrastructure;
using Kudu.Contracts.Settings;
@@ -13,16 +12,11 @@
using Kudu.Core.Infrastructure;
using Kudu.Core.SourceControl.Git;
using Kudu.Services.GitServer.ServiceHookHandlers;
-using Newtonsoft.Json.Linq;
-using System.Diagnostics;
namespace Kudu.Services.GitServer
{
public class FetchHandler : GitServerHttpHandler
{
- private const string PrivateKeyFile = "id_rsa";
- private const string PublicKeyFile = "id_rsa.pub";
-
private readonly IDeploymentSettingsManager _settings;
private readonly RepositoryConfiguration _configuration;
private readonly IEnvironment _environment;
@@ -58,14 +52,7 @@ public override void ProcessRequest(HttpContext context)
{
context.Response.TrySkipIisCustomErrors = true;
- if (_tracer.TraceLevel >= TraceLevel.Verbose)
- {
- var body = new StreamReader(context.Request.InputStream).ReadToEnd();
- TracePayload(body);
- }
-
RepositoryInfo repositoryInfo = null;
-
try
{
repositoryInfo = GetRepositoryInfo(context.Request);
@@ -138,7 +125,7 @@ private void PerformDeployment(RepositoryInfo repositoryInfo, string targetBranc
_gitServer.SetReceiveInfo(repositoryInfo.OldRef, repositoryInfo.NewRef, targetBranch);
// Fetch from url
- _gitServer.FetchWithoutConflict(repositoryInfo.RepositoryUrl, "external", targetBranch);
+ repositoryInfo.Handler.Fetch(repositoryInfo, targetBranch);
// Perform the actual deployment
_deploymentManager.Deploy(repositoryInfo.Deployer);
@@ -189,32 +176,23 @@ private RepositoryInfo GetRepositoryInfo(HttpRequest request)
RepositoryInfo info = null;
foreach (var handler in _serviceHookHandlers)
{
- try
+ if (handler.TryGetRepositoryInfo(request, out info))
{
- if (!handler.TryGetRepositoryInfo(request, out info)) continue;
-
- // don't trust parser, validate repository
- if (info != null
- && !String.IsNullOrEmpty(info.RepositoryUrl)
- && !String.IsNullOrEmpty(info.OldRef)
- && !String.IsNullOrEmpty(info.NewRef)
- && !String.IsNullOrEmpty(info.Deployer))
+ Debug.Assert(info != null, "info must not be null");
+ Debug.Assert(!String.IsNullOrEmpty(info.OldRef), "OldRef must not be null");
+ Debug.Assert(!String.IsNullOrEmpty(info.NewRef), "NewRef must not be null");
+ Debug.Assert(!String.IsNullOrEmpty(info.Deployer), "Deployer must not be null");
+
+ if (_tracer.TraceLevel >= TraceLevel.Verbose)
{
- if (_tracer.TraceLevel >= TraceLevel.Verbose)
- {
- TraceHandler(handler);
- }
- return info;
+ TraceHandler(handler);
}
- }
- catch (Exception)
- {
- // TODO: review
- // ignore exceptions from parsing, just continue to the next
- _tracer.TraceWarning("Exception occured in ServiceHookParser");
- // _tracer.TraceError(ex);
+
+ info.Handler = handler;
+ return info;
}
}
+
throw new FormatException(Resources.Error_UnsupportedFormat);
}
}
View
19 Kudu.Services/GitServer/GitServerHttpHandler.cs
@@ -20,13 +20,11 @@
#endregion
-using System.IO;
-using System.IO.Compression;
using System.Web;
using Kudu.Contracts.Infrastructure;
using Kudu.Contracts.Tracing;
-using Kudu.Core.SourceControl.Git;
using Kudu.Core.Deployment;
+using Kudu.Core.SourceControl.Git;
namespace Kudu.Services.GitServer
{
@@ -64,20 +62,5 @@ protected void UpdateNoCacheForResponse(HttpResponse response)
response.AddHeader("Pragma", "no-cache");
response.AddHeader("Cache-Control", "no-cache, max-age=0, must-revalidate");
}
-
- protected Stream GetInputStream(HttpRequest request)
- {
- using (_tracer.Step("RpcService.GetInputStream"))
- {
- var contentEncoding = request.Headers["Content-Encoding"];
-
- if (contentEncoding != null && contentEncoding.Contains("gzip"))
- {
- return new GZipStream(request.InputStream, CompressionMode.Decompress);
- }
-
- return request.InputStream;
- }
- }
}
}
View
14 Kudu.Services/GitServer/Helpers.cs
@@ -24,9 +24,11 @@ namespace Kudu.Services.GitServer
{
using System;
using System.IO;
+ using System.IO.Compression;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
+ using System.Web;
public static class Helpers
{
@@ -59,5 +61,17 @@ public static void PktFlush(this Stream response)
var toWrite = "0000";
response.Write(Encoding.UTF8.GetBytes(toWrite), 0, Encoding.UTF8.GetByteCount(toWrite));
}
+
+ public static Stream GetInputStream(this HttpRequest request)
+ {
+ var contentEncoding = request.Headers["Content-Encoding"];
+
+ if (contentEncoding != null && contentEncoding.Contains("gzip"))
+ {
+ return new GZipStream(request.InputStream, CompressionMode.Decompress);
+ }
+
+ return request.InputStream;
+ }
}
}
View
2 Kudu.Services/GitServer/ReceivePackHandler.cs
@@ -57,7 +57,7 @@ public override void ProcessRequest(HttpContext context)
using (_deploymentManager.CreateTemporaryDeployment(Resources.ReceivingChanges))
{
- _gitServer.Receive(GetInputStream(context.Request), context.Response.OutputStream);
+ _gitServer.Receive(context.Request.GetInputStream(), context.Response.OutputStream);
}
},
() =>
View
7 Kudu.Services/GitServer/ServiceHookHandlers/BitbucketHandler.cs
@@ -1,11 +1,17 @@
using System;
using System.Web;
+using Kudu.Core.SourceControl.Git;
using Newtonsoft.Json.Linq;
namespace Kudu.Services.GitServer.ServiceHookHandlers
{
public class BitbucketHandler : JsonServiceHookHandler
{
+ public BitbucketHandler(IGitServer gitServer)
+ : base(gitServer)
+ {
+ }
+
public override bool TryGetRepositoryInfo(HttpRequest request, out RepositoryInfo repositoryInfo)
{
repositoryInfo = null;
@@ -19,6 +25,7 @@ public override bool TryGetRepositoryInfo(HttpRequest request, out RepositoryInf
protected override RepositoryInfo GetRepositoryInfo(HttpRequest request, JObject payload)
{
+ // TODO, suwatch: this needs to throw, not returning null.
// bitbucket format
// { repository: { absolute_url: "/a/b", is_private: true }, canon_url: "https//..." }
var repository = payload.Value<JObject>("repository");
View
51 Kudu.Services/GitServer/ServiceHookHandlers/CodePlexHandler.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Web;
+using Kudu.Core.SourceControl.Git;
+using Newtonsoft.Json.Linq;
+
+namespace Kudu.Services.GitServer.ServiceHookHandlers
+{
+ public class CodePlexHandler : IServiceHookHandler
+ {
+ protected readonly IGitServer _gitServer;
+
+ public CodePlexHandler(IGitServer gitServer)
+ {
+ _gitServer = gitServer;
+ }
+
+ public bool TryGetRepositoryInfo(HttpRequest request, out RepositoryInfo repositoryInfo)
+ {
+ repositoryInfo = null;
+
+ string json = request.Form["payload"];
+ if (String.IsNullOrEmpty(json))
+ {
+ return false;
+ }
+
+ JObject payload = JObject.Parse(json);
+
+ // Look for the generic format
+ // { url: "", branch: "", deployer: "", oldRef: "", newRef: "" }
+ repositoryInfo = new RepositoryInfo
+ {
+ RepositoryUrl = payload.Value<string>("url"),
+ Deployer = payload.Value<string>("deployer"),
+ OldRef = payload.Value<string>("oldRef"),
+ NewRef = payload.Value<string>("newRef")
+ };
+
+ return !String.IsNullOrEmpty(repositoryInfo.RepositoryUrl) &&
+ !String.IsNullOrEmpty(repositoryInfo.Deployer) &&
+ !String.IsNullOrEmpty(repositoryInfo.OldRef) &&
+ !String.IsNullOrEmpty(repositoryInfo.NewRef);
+ }
+
+ public virtual void Fetch(RepositoryInfo repositoryInfo, string targetBranch)
+ {
+ // Fetch from url
+ _gitServer.FetchWithoutConflict(repositoryInfo.RepositoryUrl, "external", targetBranch);
+ }
+ }
+}
View
6 Kudu.Services/GitServer/ServiceHookHandlers/CodebaseHqHandler.cs
@@ -1,11 +1,17 @@
using System;
using System.Web;
+using Kudu.Core.SourceControl.Git;
using Newtonsoft.Json.Linq;
namespace Kudu.Services.GitServer.ServiceHookHandlers
{
public class CodebaseHqHandler : JsonServiceHookHandler
{
+ public CodebaseHqHandler(IGitServer gitServer)
+ : base(gitServer)
+ {
+ }
+
public override bool TryGetRepositoryInfo(HttpRequest request, out RepositoryInfo repositoryInfo)
{
repositoryInfo = null;
View
93 Kudu.Services/GitServer/ServiceHookHandlers/DropboxHandler.cs
@@ -0,0 +1,93 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Web;
+using Kudu.Contracts.Dropbox;
+using Kudu.Contracts.Settings;
+using Kudu.Contracts.Tracing;
+using Kudu.Core;
+using Kudu.Core.SourceControl;
+using Kudu.Services.Dropbox;
+using Newtonsoft.Json;
+
+namespace Kudu.Services.GitServer.ServiceHookHandlers
+{
+ public class DropboxHandler : IServiceHookHandler
+ {
+ protected readonly ITracer _tracer;
+ protected readonly DropboxHelper _helper;
+
+ public DropboxHandler(ITracer tracer,
+ IServerRepository repository,
+ IDeploymentSettingsManager settings,
+ IEnvironment environment)
+ {
+ _tracer = tracer;
+ _helper = new DropboxHelper(tracer, repository, settings, environment);
+ }
+
+ public bool TryGetRepositoryInfo(HttpRequest request, out RepositoryInfo repositoryInfo)
+ {
+ repositoryInfo = null;
+ if (request.UserAgent != null &&
+ request.UserAgent.StartsWith(DropboxHelper.Dropbox , StringComparison.OrdinalIgnoreCase))
+ {
+ DropboxInfo info = DropboxInfo.Parse(request.GetInputStream());
+
+ _tracer.Trace("Dropbox payload", new Dictionary<string, string>
+ {
+ { "changes", info.DeployInfo.Deltas.Count().ToString() },
+ { "oldcursor", info.DeployInfo.OldCursor },
+ { "newcursor", info.DeployInfo.NewCursor }
+ });
+
+ repositoryInfo = info;
+ }
+
+ return repositoryInfo != null;
+ }
+
+ public virtual void Fetch(RepositoryInfo repositoryInfo, string targetBranch)
+ {
+ // Sync with dropbox
+ DropboxInfo info = (DropboxInfo)repositoryInfo;
+ _helper.Sync(info.DeployInfo, targetBranch);
+ }
+
+ private class DropboxInfo : RepositoryInfo
+ {
+ public DropboxDeployInfo DeployInfo { get; set; }
+
+ public static DropboxInfo Parse(Stream stream)
+ {
+ DropboxDeployInfo deployInfo = null;
+ using (JsonTextReader reader = new JsonTextReader(new StreamReader(stream)))
+ {
+ JsonSerializer serializer = new JsonSerializer();
+ try
+ {
+ deployInfo = serializer.Deserialize<DropboxDeployInfo>(reader);
+ }
+ catch (Exception ex)
+ {
+ throw new FormatException(Resources.Error_UnsupportedFormat, ex);
+ }
+
+ if (deployInfo == null)
+ {
+ throw new FormatException(Resources.Error_EmptyPayload);
+ }
+ }
+
+ return new DropboxInfo
+ {
+ Deployer = DropboxHelper.Dropbox,
+ NewRef = "dummy",
+ OldRef = "dummy",
+ DeployInfo = deployInfo
+ };
+ }
+ }
+ }
+}
View
25 Kudu.Services/GitServer/ServiceHookHandlers/FallbackHandler.cs
@@ -1,25 +0,0 @@
-using System.Web;
-using Newtonsoft.Json.Linq;
-
-namespace Kudu.Services.GitServer.ServiceHookHandlers
-{
- public class FallbackHandler : IServiceHookHandler
- {
- public bool TryGetRepositoryInfo(HttpRequest request, out RepositoryInfo repositoryInfo)
- {
- string json = request.Form["payload"];
- JObject payload = JObject.Parse(json);
-
- // Look for the generic format
- // { url: "", branch: "", deployer: "", oldRef: "", newRef: "" }
- repositoryInfo = new RepositoryInfo
- {
- RepositoryUrl = payload.Value<string>("url"),
- Deployer = payload.Value<string>("deployer"),
- OldRef = payload.Value<string>("oldRef"),
- NewRef = payload.Value<string>("newRef")
- };
- return true;
- }
- }
-}
View
8 Kudu.Services/GitServer/ServiceHookHandlers/GithubHandler.cs
@@ -1,9 +1,15 @@
using System.Web;
+using Kudu.Core.SourceControl.Git;
namespace Kudu.Services.GitServer.ServiceHookHandlers
{
- public class GithubHandler : JsonServiceHookHandler
+ public class GitHubHandler : JsonServiceHookHandler
{
+ public GitHubHandler(IGitServer gitServer)
+ : base(gitServer)
+ {
+ }
+
public override bool TryGetRepositoryInfo(HttpRequest request, out RepositoryInfo repositoryInfo)
{
repositoryInfo = null;
View
6 Kudu.Services/GitServer/ServiceHookHandlers/GitlabHqHandler.cs
@@ -1,11 +1,17 @@
using System;
using System.Web;
+using Kudu.Core.SourceControl.Git;
using Newtonsoft.Json.Linq;
namespace Kudu.Services.GitServer.ServiceHookHandlers
{
public class GitlabHqHandler : JsonServiceHookHandler
{
+ public GitlabHqHandler(IGitServer gitServer)
+ : base(gitServer)
+ {
+ }
+
protected override RepositoryInfo GetRepositoryInfo(HttpRequest request, JObject payload)
{
var repository = payload.Value<JObject>("repository");
View
2 Kudu.Services/GitServer/ServiceHookHandlers/IServiceHookHandler.cs
@@ -5,5 +5,7 @@ namespace Kudu.Services.GitServer.ServiceHookHandlers
public interface IServiceHookHandler
{
bool TryGetRepositoryInfo(HttpRequest request, out RepositoryInfo repositoryInfo);
+
+ void Fetch(RepositoryInfo repositoryInfo, string targetBranch);
}
}
View
20 Kudu.Services/GitServer/ServiceHookHandlers/JsonServiceHookHandler.cs
@@ -1,6 +1,7 @@
using System;
using System.IO;
using System.Web;
+using Kudu.Core.SourceControl.Git;
using Newtonsoft.Json.Linq;
namespace Kudu.Services.GitServer.ServiceHookHandlers
@@ -10,6 +11,13 @@ namespace Kudu.Services.GitServer.ServiceHookHandlers
/// </summary>
public class JsonServiceHookHandler : IServiceHookHandler
{
+ protected readonly IGitServer _gitServer;
+
+ public JsonServiceHookHandler(IGitServer gitServer)
+ {
+ _gitServer = gitServer;
+ }
+
public virtual bool TryGetRepositoryInfo(HttpRequest request, out RepositoryInfo repositoryInfo)
{
string json = String.Empty;
@@ -25,15 +33,23 @@ public virtual bool TryGetRepositoryInfo(HttpRequest request, out RepositoryInfo
{
// assume raw json
request.InputStream.Seek(0, SeekOrigin.Begin);
- json = new StreamReader(request.InputStream).ReadToEnd();
+
+ // TODO, suwatch: this is problematic as multiple handler may not collaborate.
+ // input stream can only be read once.
+ json = new StreamReader(request.GetInputStream()).ReadToEnd();
}
JObject payload = JObject.Parse(json);
-
repositoryInfo = GetRepositoryInfo(request, payload);
return repositoryInfo != null;
}
+ public virtual void Fetch(RepositoryInfo repositoryInfo, string targetBranch)
+ {
+ // Fetch from url
+ _gitServer.FetchWithoutConflict(repositoryInfo.RepositoryUrl, "external", targetBranch);
+ }
+
protected virtual RepositoryInfo GetRepositoryInfo(HttpRequest request, JObject payload)
{
JObject repository = payload.Value<JObject>("repository");
View
1 Kudu.Services/GitServer/ServiceHookHandlers/RepositoryInfo.cs
@@ -9,5 +9,6 @@ public class RepositoryInfo
public string OldRef { get; set; }
public string NewRef { get; set; }
public string Deployer { get; set; }
+ public IServiceHookHandler Handler { get; set; }
}
}
View
2 Kudu.Services/GitServer/UploadPackHandler.cs
@@ -46,7 +46,7 @@ public override void ProcessRequest(HttpContext context)
context.Response.ContentType = "application/x-git-{0}-result".With("upload-pack");
- _gitServer.Upload(GetInputStream(context.Request), context.Response.OutputStream);
+ _gitServer.Upload(context.Request.GetInputStream(), context.Response.OutputStream);
}
}
}
View
7 Kudu.Services/Kudu.Services.csproj
@@ -140,15 +140,17 @@
<Compile Include="Diagnostics\LogStreamHandler.cs" />
<Compile Include="Diagnostics\LogStreamManager.cs" />
<Compile Include="Editor\VfsController.cs" />
+ <Compile Include="Dropbox\DropboxHelper.cs" />
<Compile Include="GitServer\GitServerHttpHandler.cs" />
<Compile Include="GitServer\ServiceHookHandlers\CodebaseHqHandler.cs" />
- <Compile Include="GitServer\ServiceHookHandlers\FallbackHandler.cs" />
+ <Compile Include="GitServer\ServiceHookHandlers\CodePlexHandler.cs" />
+ <Compile Include="GitServer\ServiceHookHandlers\DropboxHandler.cs" />
<Compile Include="GitServer\ServiceHookHandlers\GitlabHqHandler.cs" />
<Compile Include="GitServer\ServiceHookHandlers\JsonServiceHookHandler.cs" />
<Compile Include="GitServer\ServiceHookHandlers\RepositoryInfo.cs" />
<Compile Include="GitServer\ServiceHookHandlers\IServiceHookHandler.cs" />
<Compile Include="GitServer\ServiceHookHandlers\BitbucketHandler.cs" />
- <Compile Include="GitServer\ServiceHookHandlers\GithubHandler.cs" />
+ <Compile Include="GitServer\ServiceHookHandlers\GitHubHandler.cs" />
<Compile Include="GitServer\UploadPackHandler.cs" />
<Compile Include="Infrastructure\ShutdownDetector.cs" />
<Compile Include="Infrastructure\DelegatingStream.cs" />
@@ -195,6 +197,7 @@
<EmbeddedResource Include="Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
+ <SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
View
20 Kudu.Services/Resources.Designer.cs
@@ -1,7 +1,7 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
-// Runtime Version:4.0.30319.18010
+// Runtime Version:4.0.30319.586
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -169,6 +169,24 @@ internal class Resources {
}
/// <summary>
+ /// Looks up a localized string similar to The json payload is empty..
+ /// </summary>
+ internal static string Error_EmptyPayload {
+ get {
+ return ResourceManager.GetString("Error_EmptyPayload", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Mismatch dropbox origin cursor..
+ /// </summary>
+ internal static string Error_MismatchDropboxCursor {
+ get {
+ return ResourceManager.GetString("Error_MismatchDropboxCursor", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to Missing repository url..
/// </summary>
internal static string Error_MissingRepositoryUrl {
View
6 Kudu.Services/Resources.resx
@@ -153,6 +153,12 @@
<data name="Error_DumbProtocolNotSupported" xml:space="preserve">
<value>The dumb protocol is not supported.</value>
</data>
+ <data name="Error_EmptyPayload" xml:space="preserve">
+ <value>The json payload is empty.</value>
+ </data>
+ <data name="Error_MismatchDropboxCursor" xml:space="preserve">
+ <value>Mismatch dropbox origin cursor.</value>
+ </data>
<data name="Error_MissingRepositoryUrl" xml:space="preserve">
<value>Missing repository url.</value>
</data>

0 comments on commit 513bed0

Please sign in to comment.