public async Task Fetch(IRepository repository, DeploymentInfoBase deploymentInfo, string targetBranch, ILogger logger, ITracer tracer) { var oneDriveInfo = (OneDriveInfo)deploymentInfo; _oneDriveHelper.Logger = logger; oneDriveInfo.TargetChangeset = await _oneDriveHelper.Sync(oneDriveInfo, repository, tracer); }
public virtual async Task Fetch(IRepository repository, DeploymentInfoBase deploymentInfo, string targetBranch, ILogger logger, ITracer tracer) { // (A)sync with dropbox var dropboxInfo = (DropboxInfo)deploymentInfo; _dropBoxHelper.Logger = logger; deploymentInfo.TargetChangeset = await _dropBoxHelper.Sync(dropboxInfo, targetBranch, repository, tracer); }
public void TryParseDeploymentInfoShouldReturnUnknownPayload() { var oneDriveHandler = new OneDriveHandler(Mock.Of <ITracer>(), Mock.Of <IDeploymentStatusManager>(), Mock.Of <IDeploymentSettingsManager>(), Mock.Of <IEnvironment>(), Mock.Of <IRepositoryFactory>()); JObject payload = JObject.FromObject(new { }); DeploymentInfoBase deploymentInfo = null; DeployAction result = oneDriveHandler.TryParseDeploymentInfo(null, payload, null, out deploymentInfo); Assert.Equal(DeployAction.UnknownPayload, result); }
public static void SetTargetSubDirectoyAndFileNameFromPath(DeploymentInfoBase deploymentInfo, string relativeFilePath) { // Extract directory path and file name from relativeFilePath // Example: path=a/b/c.jar => TargetDirectoryName=a/b and TargetFileName=c.jar // Example: path=c.jar => TargetDirectoryName=null and TargetFileName=c.jar // Example: path=/c.jar => TargetDirectoryName="" and TargetFileName=c.jar // Example: path=null => TargetDirectoryName=null and TargetFileName=null deploymentInfo.TargetFileName = Path.GetFileName(relativeFilePath); deploymentInfo.TargetSubDirectoryRelativePath = Path.GetDirectoryName(relativeFilePath); }
// Extract directory path and file name from relativeFilePath // Example: path=a/b/c.jar => TargetSubDirectoryRelativePath=a/b and TargetFileName=c.jar // Example: path=c.jar => TargetSubDirectoryRelativePath=null and TargetFileName=c.jar // Example: path=/c.jar => TargetSubDirectoryRelativePath="" and TargetFileName=c.jar // Example: path=null => TargetSubDirectoryRelativePath=null and TargetFileName=null public static void SetTargetSubDirectoyAndFileNameFromRelativePath(DeploymentInfoBase deploymentInfo, string relativeFilePath) { if (relativeFilePath != null) { relativeFilePath = relativeFilePath.TrimStart('/'); } deploymentInfo.TargetFileName = Path.GetFileName(relativeFilePath); deploymentInfo.TargetSubDirectoryRelativePath = Path.GetDirectoryName(relativeFilePath); }
public void TryParseDeploymentInfoShouldReturnProcessDeployment() { var oneDriveHandler = new OneDriveHandler(Mock.Of <ITracer>(), Mock.Of <IDeploymentStatusManager>(), Mock.Of <IDeploymentSettingsManager>(), Mock.Of <IEnvironment>(), Mock.Of <IRepositoryFactory>()); JObject payload = JObject.FromObject(new { RepositoryUrl = "https://api.onedrive.com", AccessToken = "one-drive-access-token" }); DeploymentInfoBase deploymentInfo = null; DeployAction result = oneDriveHandler.TryParseDeploymentInfo(null, payload, null, out deploymentInfo); Assert.Equal(DeployAction.ProcessDeployment, result); }
// OneDeploy Fetch handler for non-zip artifacts. // For zip files, OneDeploy uses the LocalZipHandler Fetch handler // NOTE: Do not access the request stream as it may have been closed during asynchronous scenarios private async Task OneDeployFetch(IRepository repository, DeploymentInfoBase deploymentInfo, string targetBranch, ILogger logger, ITracer tracer) { var artifactDeploymentInfo = (ArtifactDeploymentInfo)deploymentInfo; // This is the path where the artifact being deployed is staged, before it is copied to the final target location var artifactDirectoryStagingPath = repository.RepositoryPath; var targetInfo = FileSystemHelpers.DirectoryInfoFromDirectoryName(artifactDirectoryStagingPath); if (targetInfo.Exists) { // If tempDirPath already exists, rename it so we can delete it later var moveTarget = Path.Combine(targetInfo.Parent.FullName, Path.GetRandomFileName()); using (tracer.Step(string.Format("Renaming ({0}) to ({1})", targetInfo.FullName, moveTarget))) { targetInfo.MoveTo(moveTarget); } } // Create artifact staging directory before later use Directory.CreateDirectory(artifactDirectoryStagingPath); var artifactFileStagingPath = Path.Combine(artifactDirectoryStagingPath, deploymentInfo.TargetFileName); // If RemoteUrl is non-null, it means the content needs to be downloaded from the Url source to the staging location // Else, it had been downloaded already so we just move the downloaded file to the staging location if (!string.IsNullOrWhiteSpace(artifactDeploymentInfo.RemoteURL)) { using (tracer.Step("Saving request content to {0}", artifactFileStagingPath)) { var content = await DeploymentHelper.GetArtifactContentFromURL(artifactDeploymentInfo, tracer); var copyTask = content.CopyToAsync(artifactFileStagingPath, tracer); // Deletes all files and directories except for artifactFileStagingPath and artifactDirectoryStagingPath var cleanTask = Task.Run(() => DeleteFilesAndDirsExcept(artifactFileStagingPath, artifactDirectoryStagingPath, tracer)); // Lets the copy and cleanup tasks to run in parallel and wait for them to finish await Task.WhenAll(copyTask, cleanTask); } } else { var srcInfo = FileSystemHelpers.DirectoryInfoFromDirectoryName(deploymentInfo.RepositoryUrl); using (tracer.Step(string.Format("Moving {0} to {1}", targetInfo.FullName, artifactFileStagingPath))) { srcInfo.MoveTo(artifactFileStagingPath); } // Deletes all files and directories except for artifactFileStagingPath and artifactDirectoryStagingPath DeleteFilesAndDirsExcept(artifactFileStagingPath, artifactDirectoryStagingPath, tracer); } // The deployment flow expects at least 1 commit in the IRepository commit, refer to CommitRepo() for more info CommitRepo(repository, artifactDeploymentInfo); }
private async Task LocalZipFetch(IRepository repository, DeploymentInfoBase deploymentInfo, string targetBranch, ILogger logger, ITracer tracer) { var zipDeploymentInfo = (ZipDeploymentInfo)deploymentInfo; // If this was a request with a Zip URL in the JSON, we need to deploy the zip locally and get the path // Otherwise, for this kind of deployment, RepositoryUrl is a local path. var sourceZipFile = !string.IsNullOrEmpty(zipDeploymentInfo.ZipURL) ? await DeployZipLocally(zipDeploymentInfo, tracer) : zipDeploymentInfo.RepositoryUrl; var extractTargetDirectory = repository.RepositoryPath; var info = FileSystemHelpers.FileInfoFromFileName(sourceZipFile); var sizeInMb = (info.Length / (1024f * 1024f)).ToString("0.00", CultureInfo.InvariantCulture); var message = String.Format( CultureInfo.InvariantCulture, "Cleaning up temp folders from previous zip deployments and extracting pushed zip file {0} ({1} MB) to {2}", info.FullName, sizeInMb, extractTargetDirectory); logger.Log(message); using (tracer.Step(message)) { // If extractTargetDirectory already exists, rename it so we can delete it concurrently with // the unzip (along with any other junk in the folder) var targetInfo = FileSystemHelpers.DirectoryInfoFromDirectoryName(extractTargetDirectory); if (targetInfo.Exists) { var moveTarget = Path.Combine(targetInfo.Parent.FullName, Path.GetRandomFileName()); using (tracer.Step(string.Format("Renaming extractTargetDirectory({0}) to tempDirectory({1})", targetInfo.FullName, moveTarget))) { targetInfo.MoveTo(moveTarget); } } var cleanTask = Task.Run(() => DeleteFilesAndDirsExcept(sourceZipFile, extractTargetDirectory, tracer)); var extractTask = Task.Run(() => { FileSystemHelpers.CreateDirectory(extractTargetDirectory); using (var file = info.OpenRead()) using (var zip = new ZipArchive(file, ZipArchiveMode.Read)) { zip.Extract(extractTargetDirectory, tracer, _settings.GetZipDeployDoNotPreserveFileTime()); } }); await Task.WhenAll(cleanTask, extractTask); } CommitRepo(repository, zipDeploymentInfo); }
private async Task LocalZipHandler(IRepository repository, DeploymentInfoBase deploymentInfo, string targetBranch, ILogger logger, ITracer tracer) { if (_settings.RunFromLocalZip() && deploymentInfo is ZipDeploymentInfo) { // If this is a Run-From-Zip deployment, then we need to extract function.json // from the zip file into path zipDeploymentInfo.SyncFunctionsTrigersPath ExtractTriggers(repository, deploymentInfo as ZipDeploymentInfo); } else { await LocalZipFetch(repository, deploymentInfo, targetBranch, logger, tracer); } }
private Task LocalZipFetch(IRepository repository, DeploymentInfoBase deploymentInfo, string targetBranch, ILogger logger, ITracer tracer) { var zipDeploymentInfo = (ZipDeploymentInfo)deploymentInfo; // For this kind of deployment, RepositoryUrl is a local path. var sourceZipFile = zipDeploymentInfo.RepositoryUrl; var extractTargetDirectory = repository.RepositoryPath; var info = FileSystemHelpers.FileInfoFromFileName(sourceZipFile); var sizeInMb = (info.Length / (1024f * 1024f)).ToString("0.00", CultureInfo.InvariantCulture); var message = String.Format( CultureInfo.InvariantCulture, "Cleaning up temp folders from previous zip deployments and extracting pushed zip file {0} ({1} MB) to {2}", info.FullName, sizeInMb, extractTargetDirectory); logger.Log(message); using (tracer.Step(message)) { // If extractTargetDirectory already exists, rename it so we can delete it concurrently with // the unzip (along with any other junk in the folder) var targetInfo = FileSystemHelpers.DirectoryInfoFromDirectoryName(extractTargetDirectory); if (targetInfo.Exists) { var moveTarget = Path.Combine(targetInfo.Parent.FullName, Path.GetRandomFileName()); targetInfo.MoveTo(moveTarget); } DeleteFilesAndDirsExcept(sourceZipFile, extractTargetDirectory, tracer); FileSystemHelpers.CreateDirectory(extractTargetDirectory); using (var file = info.OpenRead()) using (var zip = new ZipArchive(file, ZipArchiveMode.Read)) { deploymentInfo.repositorySymlinks = zip.Extract(extractTargetDirectory, preserveSymlinks: ShouldPreserveSymlinks()); CreateZipSymlinks(deploymentInfo.repositorySymlinks, extractTargetDirectory); PermissionHelper.ChmodRecursive("777", extractTargetDirectory, tracer, TimeSpan.FromMinutes(1)); } } CommitRepo(repository, zipDeploymentInfo); return(Task.CompletedTask); }
private async Task LocalZipFetch(IRepository repository, DeploymentInfoBase deploymentInfo, string targetBranch, ILogger logger, ITracer tracer) { var zipDeploymentInfo = (ZipDeploymentInfo)deploymentInfo; // For this kind of deployment, RepositoryUrl is a local path. var sourceZipFile = zipDeploymentInfo.RepositoryUrl; var extractTargetDirectory = repository.RepositoryPath; var info = FileSystemHelpers.FileInfoFromFileName(sourceZipFile); var sizeInMb = (info.Length / (1024f * 1024f)).ToString("0.00", CultureInfo.InvariantCulture); var message = String.Format( CultureInfo.InvariantCulture, "Cleaning up temp folders from previous zip deployments and extracting pushed zip file {0} ({1} MB) to {2}", info.FullName, sizeInMb, extractTargetDirectory); logger.Log(message); using (tracer.Step(message)) { // If extractTargetDirectory already exists, rename it so we can delete it concurrently with // the unzip (along with any other junk in the folder) var targetInfo = FileSystemHelpers.DirectoryInfoFromDirectoryName(extractTargetDirectory); if (targetInfo.Exists) { var moveTarget = Path.Combine(targetInfo.Parent.FullName, Path.GetRandomFileName()); targetInfo.MoveTo(moveTarget); } var cleanTask = Task.Run(() => DeleteFilesAndDirsExcept(sourceZipFile, extractTargetDirectory, tracer)); var extractTask = Task.Run(() => { FileSystemHelpers.CreateDirectory(extractTargetDirectory); using (var file = info.OpenRead()) using (var zip = new ZipArchive(file, ZipArchiveMode.Read)) { zip.Extract(extractTargetDirectory); } }); await Task.WhenAll(cleanTask, extractTask); } // Needed in order for repository.GetChangeSet() to work. // Similar to what OneDriveHelper and DropBoxHelper do. repository.Commit(zipDeploymentInfo.Message, zipDeploymentInfo.Author, zipDeploymentInfo.AuthorEmail); }
private void SetTargetFromPath(DeploymentInfoBase deploymentInfo, string path) { // Extract directory path and file name from 'path' // Example: path=a/b/c.jar => TargetDirectoryName=a/b and TargetFileName=c.jar deploymentInfo.TargetFileName = Path.GetFileName(path); var relativeDirectoryPath = Path.GetDirectoryName(path); // Translate /foo/bar to foo/bar // Translate \foo\bar to foo\bar // That way, we can combine it with %HOME% to get the absolute path relativeDirectoryPath = relativeDirectoryPath.TrimStart('/', '\\'); var absoluteDirectoryPath = Path.Combine(_environment.RootPath, relativeDirectoryPath); deploymentInfo.TargetDirectoryPath = absoluteDirectoryPath; }
public static void SetRepositoryUrl(DeploymentInfoBase deploymentInfo, string url) { string commitId = null; int index = url.LastIndexOf('#'); if (index >= 0) { if (index + 1 < url.Length) { commitId = url.Substring(index + 1); } url = url.Substring(0, index); } deploymentInfo.RepositoryUrl = url; deploymentInfo.CommitId = commitId; }
private async Task LocalZipHandler(IRepository repository, DeploymentInfoBase deploymentInfo, string targetBranch, ILogger logger, ITracer tracer) { if (_settings.RunFromLocalZip() && deploymentInfo is ZipDeploymentInfo) { ZipDeploymentInfo zipDeploymentInfo = (ZipDeploymentInfo)deploymentInfo; // If this was a request with a Zip URL in the JSON, we first need to get the zip content and write it to the site. if (!string.IsNullOrEmpty(zipDeploymentInfo.ZipURL)) { await WriteSitePackageZip(zipDeploymentInfo, tracer, await DeploymentHelper.GetZipContentFromURL(zipDeploymentInfo, tracer)); } // If this is a Run-From-Zip deployment, then we need to extract function.json // from the zip file into path zipDeploymentInfo.SyncFunctionsTrigersPath ExtractTriggers(repository, zipDeploymentInfo); } else { await LocalZipFetch(repository, deploymentInfo, targetBranch, logger, tracer); } }
private DeployAction GetRepositoryInfo( HttpRequest request, JObject payload, string targetBranch, IEnumerable <IServiceHookHandler> serviceHookHandlers, ITracer tracer, out DeploymentInfoBase info) { foreach (var handler in serviceHookHandlers) { DeployAction result = handler.TryParseDeploymentInfo(request, payload, targetBranch, out info); if (result == DeployAction.UnknownPayload) { continue; } if (tracer.TraceLevel >= TraceLevel.Verbose) { var attribs = new Dictionary <string, string> { { "type", handler.GetType().FullName } }; tracer.Trace("handler", attribs); } if (result == DeployAction.ProcessDeployment) { // Although a payload may be intended for a handler, it might not need to fetch. // For instance, if a different branch was pushed than the one the repository is deploying, // we can no-op it. Debug.Assert(info != null); info.Fetch = handler.Fetch; } return(result); } throw new FormatException(Resources.Error_UnsupportedFormat); }
public static Task FakeFetch(IRepository repository, DeploymentInfoBase deploymentInfo, string targetBranch, ILogger logger, ITracer tracer) { return(Task.FromResult(0)); }
public Task DeployAsync(IRepository repository, ChangeSet changeSet, string deployer, bool clean, DeploymentInfoBase deploymentInfo = null, bool needFileUpdate = true, bool fullBuildByDefault = true) { ++DeployCount; return(Task.FromResult(1)); }
private DeployAction GetRepositoryInfo(HttpRequestBase request, JObject payload, string targetBranch, out DeploymentInfoBase info) { foreach (var handler in _serviceHookHandlers) { DeployAction result = handler.TryParseDeploymentInfo(request, payload, targetBranch, out info); if (result != DeployAction.UnknownPayload) { if (_tracer.TraceLevel >= TraceLevel.Verbose) { TraceHandler(handler); } if (result == DeployAction.ProcessDeployment) { // Although a payload may be intended for a handler, it might not need to fetch. // For instance, if a different branch was pushed than the one the repository is deploying, we can no-op it. Debug.Assert(info != null); info.Fetch = handler.Fetch; } return(result); } } throw new FormatException(Resources.Error_UnsupportedFormat); }
public override DeployAction TryParseDeploymentInfo(HttpRequestBase request, JObject payload, string targetBranch, out DeploymentInfoBase deploymentInfo) { deploymentInfo = null; if (request.UserAgent != null && request.UserAgent.StartsWith("Bitbucket-Webhooks/2.0", StringComparison.OrdinalIgnoreCase)) { deploymentInfo = GetDeploymentInfo(payload, targetBranch); return(deploymentInfo == null ? DeployAction.NoOp : DeployAction.ProcessDeployment); } return(DeployAction.UnknownPayload); }
public override async Task ProcessRequestAsync(HttpContext context) { using (_tracer.Step("FetchHandler")) { // Redirect GET /deploy requests to the Kudu root for convenience when using URL from Azure portal if (String.Equals(context.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) { context.Response.Redirect("~/"); context.ApplicationInstance.CompleteRequest(); return; } if (!String.Equals(context.Request.HttpMethod, "POST", StringComparison.OrdinalIgnoreCase)) { context.Response.StatusCode = (int)HttpStatusCode.NotFound; context.ApplicationInstance.CompleteRequest(); return; } context.Response.TrySkipIisCustomErrors = true; DeploymentInfoBase deployInfo = null; // We are going to assume that the branch details are already set by the time it gets here. This is particularly important in the mercurial case, // since Settings hardcodes the default value for Branch to be "master". Consequently, Kudu will NoOp requests for Mercurial commits. string targetBranch = _settings.GetBranch(); try { var request = new HttpRequestWrapper(context.Request); JObject payload = GetPayload(request); DeployAction action = GetRepositoryInfo(request, payload, targetBranch, out deployInfo); if (action == DeployAction.NoOp) { _tracer.Trace("No-op for deployment."); return; } } catch (FormatException ex) { _tracer.TraceError(ex); context.Response.StatusCode = 400; context.Response.Write(ex.Message); context.ApplicationInstance.CompleteRequest(); return; } bool asyncRequested = String.Equals(context.Request.QueryString["isAsync"], "true", StringComparison.OrdinalIgnoreCase); var response = await _manager.FetchDeploy(deployInfo, asyncRequested, UriHelper.GetRequestUri(context.Request), targetBranch); switch (response) { case FetchDeploymentRequestResult.RunningAynschronously: // to avoid regression, only set location header if isAsync if (asyncRequested) { // latest deployment keyword reserved to poll till deployment done context.Response.Headers["Location"] = new Uri(UriHelper.GetRequestUri(context.Request), String.Format("/api/deployments/{0}?deployer={1}&time={2}", Constants.LatestDeployment, deployInfo.Deployer, DateTime.UtcNow.ToString("yyy-MM-dd_HH-mm-ssZ"))).ToString(); } context.Response.StatusCode = (int)HttpStatusCode.Accepted; context.ApplicationInstance.CompleteRequest(); return; case FetchDeploymentRequestResult.ForbiddenScmDisabled: context.Response.StatusCode = (int)HttpStatusCode.Forbidden; context.ApplicationInstance.CompleteRequest(); _tracer.Trace("Scm is not enabled, reject all requests."); return; case FetchDeploymentRequestResult.ConflictAutoSwapOngoing: context.Response.StatusCode = (int)HttpStatusCode.Conflict; context.Response.Write(Resources.Error_AutoSwapDeploymentOngoing); context.ApplicationInstance.CompleteRequest(); return; case FetchDeploymentRequestResult.Pending: // Return a http 202: the request has been accepted for processing, but the processing has not been completed. context.Response.StatusCode = (int)HttpStatusCode.Accepted; context.ApplicationInstance.CompleteRequest(); return; case FetchDeploymentRequestResult.ConflictDeploymentInProgress: context.Response.StatusCode = (int)HttpStatusCode.Conflict; context.Response.Write(Resources.Error_DeploymentInProgress); context.ApplicationInstance.CompleteRequest(); break; case FetchDeploymentRequestResult.RanSynchronously: default: break; } } }
public DeployAction TryParseDeploymentInfo(HttpRequest request, JObject payload, string targetBranch, out DeploymentInfoBase deploymentInfo) { DropboxInfo dropboxInfo = null; string message = null; if (!String.IsNullOrEmpty(payload.Value <string>("NewCursor"))) { dropboxInfo = DropboxInfo.CreateV1Info(payload, GetRepositoryType(), _repositoryFactory); message = String.Format(CultureInfo.CurrentUICulture, Resources.Dropbox_SynchronizingNChanges, dropboxInfo.DeployInfo.Deltas.Count); } else if (String.Equals(payload.Value <string>(DropboxVersionKey), "2", StringComparison.OrdinalIgnoreCase)) { string oauthToken = GetValue(payload, DropboxTokenKey), path = GetValue(payload, DropboxPathKey), userName = payload.Value <string>("dropbox_username") ?? "Dropbox", email = payload.Value <string>("dropbox_email"); dropboxInfo = DropboxInfo.CreateV2Info(path, oauthToken, GetRepositoryType(), _repositoryFactory); dropboxInfo.DeployInfo.UserName = userName; dropboxInfo.DeployInfo.Email = email; message = String.Format(CultureInfo.CurrentUICulture, Resources.Dropbox_Synchronizing); } if (dropboxInfo != null) { deploymentInfo = dropboxInfo; // Temporary deployment deploymentInfo.TargetChangeset = DeploymentManager.CreateTemporaryChangeSet( authorName: dropboxInfo.DeployInfo.UserName, authorEmail: dropboxInfo.DeployInfo.Email, message: message ); deploymentInfo.AllowDeploymentWhileScmDisabled = true; return(DeployAction.ProcessDeployment); } deploymentInfo = null; return(DeployAction.UnknownPayload); }
public DeployAction TryParseDeploymentInfo(HttpRequest request, JObject payload, string targetBranch, out DeploymentInfoBase deploymentInfo) { deploymentInfo = null; string url = payload.Value <string>("RepositoryUrl"); if (string.IsNullOrWhiteSpace(url) || !url.ToLowerInvariant().Contains("api.onedrive.com")) { return(DeployAction.UnknownPayload); } /* * Expecting payload to be: * { * "RepositoryUrl": "xxx", * "AccessToken": "xxx" * } */ string accessToken = payload.Value <string>("AccessToken"); // keep email and name, so that can be re-used in later commit OneDriveInfo oneDriveInfo = new OneDriveInfo(_repositoryFactory) { Deployer = "OneDrive", RepositoryUrl = url, AccessToken = accessToken, AuthorName = _settings.GetValue("authorName"), AuthorEmail = _settings.GetValue("authorEmail") }; deploymentInfo = oneDriveInfo; deploymentInfo.TargetChangeset = DeploymentManager.CreateTemporaryChangeSet( authorName: oneDriveInfo.AuthorName, authorEmail: oneDriveInfo.AuthorEmail, message: String.Format(CultureInfo.CurrentUICulture, Resources.OneDrive_Synchronizing) ); return(DeployAction.ProcessDeployment); }
public override DeployAction TryParseDeploymentInfo(HttpRequest request, JObject payload, string targetBranch, out DeploymentInfoBase deploymentInfo) { deploymentInfo = null; if (request.Headers["User-Agent"].ToString().StartsWith("Bitbucket.org", StringComparison.OrdinalIgnoreCase)) { deploymentInfo = GetDeploymentInfo(payload, targetBranch); return(deploymentInfo == null ? DeployAction.NoOp : DeployAction.ProcessDeployment); } return(DeployAction.UnknownPayload); }
public override DeployAction TryParseDeploymentInfo(HttpRequestBase request, JObject payload, string targetBranch, out DeploymentInfoBase deploymentInfo) { deploymentInfo = null; var publisherId = payload.Value <string>("publisherId"); if (String.Equals(publisherId, "tfs", StringComparison.OrdinalIgnoreCase)) { deploymentInfo = GetDeploymentInfo(request, payload, targetBranch); return(DeployAction.ProcessDeployment); } return(DeployAction.UnknownPayload); }
public async Task Invoke( HttpContext context, ITracer tracer, IDeploymentSettingsManager settings, IFetchDeploymentManager manager, IEnumerable <IServiceHookHandler> serviceHookHandlers) { using (tracer.Step("FetchHandler")) { // Redirect GET /deploy requests to the Kudu root for convenience when using URL from Azure portal if (string.Equals(context.Request.Method, "GET", StringComparison.OrdinalIgnoreCase)) { context.Response.Redirect("/"); return; } if (!string.Equals(context.Request.Method, "POST", StringComparison.OrdinalIgnoreCase)) { context.Response.StatusCode = StatusCodes.Status404NotFound; return; } // CORE TODO Need to set up UseDeveloperExceptionPage, UseExceptionHandler or the like in startup //context.Response.TrySkipIisCustomErrors = true; DeploymentInfoBase deployInfo = null; // We are going to assume that the branch details are already set by the time it gets here. This is particularly important in the mercurial case, // since Settings hardcodes the default value for Branch to be "master". Consequently, Kudu will NoOp requests for Mercurial commits. var targetBranch = settings.GetBranch(); try { JObject payload = GetPayload(context.Request, tracer); DeployAction action = GetRepositoryInfo(context.Request, payload, targetBranch, serviceHookHandlers, tracer, out deployInfo); if (action == DeployAction.NoOp) { tracer.Trace("No-op for deployment."); return; } } catch (FormatException ex) { tracer.TraceError(ex); context.Response.StatusCode = StatusCodes.Status400BadRequest; await context.Response.WriteAsync(ex.Message); return; } // CORE TODO make sure .Query has the same semantics as the old .QueryString (null, empty, etc.) bool asyncRequested = String.Equals(context.Request.Query["isAsync"], "true", StringComparison.OrdinalIgnoreCase); var response = await manager.FetchDeploy(deployInfo, asyncRequested, UriHelper.GetRequestUri(context.Request), targetBranch); switch (response) { case FetchDeploymentRequestResult.RunningAynschronously: // to avoid regression, only set location header if isAsync if (asyncRequested) { // latest deployment keyword reserved to poll till deployment done context.Response.Headers["Location"] = new Uri(UriHelper.GetRequestUri(context.Request), String.Format("/api/deployments/{0}?deployer={1}&time={2}", Constants.LatestDeployment, deployInfo.Deployer, DateTime.UtcNow.ToString("yyy-MM-dd_HH-mm-ssZ"))).ToString(); } context.Response.StatusCode = StatusCodes.Status202Accepted; return; case FetchDeploymentRequestResult.ForbiddenScmDisabled: context.Response.StatusCode = StatusCodes.Status403Forbidden; tracer.Trace("Scm is not enabled, reject all requests."); return; case FetchDeploymentRequestResult.ConflictAutoSwapOngoing: context.Response.StatusCode = StatusCodes.Status409Conflict; await context.Response.WriteAsync(Resources.Error_AutoSwapDeploymentOngoing); return; case FetchDeploymentRequestResult.Pending: // Return a http 202: the request has been accepted for processing, but the processing has not been completed. context.Response.StatusCode = StatusCodes.Status202Accepted; return; case FetchDeploymentRequestResult.ConflictDeploymentInProgress: context.Response.StatusCode = StatusCodes.Status409Conflict; await context.Response.WriteAsync(Resources.Error_DeploymentInProgress); return; case FetchDeploymentRequestResult.ConflictRunFromRemoteZipConfigured: context.Response.StatusCode = StatusCodes.Status409Conflict; await context.Response.WriteAsync(Resources.Error_RunFromRemoteZipConfigured); return; case FetchDeploymentRequestResult.RanSynchronously: default: context.Response.StatusCode = StatusCodes.Status200OK; break; } } }
public ISiteBuilder CreateBuilder(ITracer tracer, ILogger logger, IDeploymentSettingsManager settings, IRepository repository, DeploymentInfoBase deploymentInfo) { string repositoryRoot = repository.RepositoryPath; // Use the cached vs projects file finder for: a. better performance, b. ignoring solutions/projects under node_modules var fileFinder = new CachedVsProjectsFileFinder(repository); // If there's a custom deployment file then let that take over. var command = settings.GetValue(SettingsKeys.Command); if (!String.IsNullOrEmpty(command)) { return(new CustomBuilder(_environment, settings, _propertyProvider, repositoryRoot, command)); } // If the user provided specific generator arguments, that overrides any detection logic string scriptGeneratorArgs = settings.GetValue(SettingsKeys.ScriptGeneratorArgs); if (!String.IsNullOrEmpty(scriptGeneratorArgs)) { return(new CustomGeneratorCommandSiteBuilder(_environment, settings, _propertyProvider, repositoryRoot, scriptGeneratorArgs)); } // If the repository has an explicit pointer to a project path to be deployed // then use it. string targetProjectPath = settings.GetValue(SettingsKeys.Project); if (!String.IsNullOrEmpty(targetProjectPath)) { tracer.Trace("Specific project was specified: " + targetProjectPath); targetProjectPath = Path.GetFullPath(Path.Combine(repositoryRoot, targetProjectPath.TrimStart('/', '\\'))); } if (deploymentInfo != null && deploymentInfo.Deployer == Constants.OneDeploy) { var projectPath = !String.IsNullOrEmpty(targetProjectPath) ? targetProjectPath : repositoryRoot; return(new OneDeployBuilder(_environment, settings, _propertyProvider, repositoryRoot, projectPath, deploymentInfo)); } if (settings.RunFromLocalZip()) { return(new RunFromZipSiteBuilder()); } if (!settings.DoBuildDuringDeployment()) { var projectPath = !String.IsNullOrEmpty(targetProjectPath) ? targetProjectPath : repositoryRoot; if (DeploymentHelper.IsDeploymentV2Request()) { return(new DeploymentV2Builder(_environment, settings, _propertyProvider, repositoryRoot)); } else { return(new BasicBuilder(_environment, settings, _propertyProvider, repositoryRoot, projectPath)); } } // Check if we really need a builder for this // If not, return the NoOpBuilder string appFramework = System.Environment.GetEnvironmentVariable("FRAMEWORK"); if (!string.IsNullOrEmpty(appFramework) && string.Equals(appFramework, "STATICSITE", StringComparison.OrdinalIgnoreCase)) { var projectPath = !String.IsNullOrEmpty(targetProjectPath) ? targetProjectPath : repositoryRoot; return(new NoOpBuilder(_environment, settings, _propertyProvider, repositoryRoot, projectPath)); } // If ENABLE_ORYX_BUILD is not set, for function app, we assume it on by default string enableOryxBuild = System.Environment.GetEnvironmentVariable("ENABLE_ORYX_BUILD"); if (!string.IsNullOrEmpty(enableOryxBuild)) { if (StringUtils.IsTrueLike(enableOryxBuild)) { return(new OryxBuilder(_environment, settings, _propertyProvider, repositoryRoot)); } } else if (FunctionAppHelper.LooksLikeFunctionApp()) { return(new OryxBuilder(_environment, settings, _propertyProvider, repositoryRoot)); } string framework = System.Environment.GetEnvironmentVariable("FRAMEWORK"); if (framework.Equals("ruby", StringComparison.OrdinalIgnoreCase)) { return(new RubySiteBuilder(_environment, settings, _propertyProvider, repositoryRoot, targetProjectPath)); } if (!String.IsNullOrEmpty(targetProjectPath)) { // Try to resolve the project return(ResolveProject(repositoryRoot, targetProjectPath, settings, fileFinder, tryWebSiteProject: true, searchOption: SearchOption.TopDirectoryOnly)); } // Get all solutions in the current repository path var solutions = VsHelper.GetSolutions(repositoryRoot, fileFinder).ToList(); if (!solutions.Any()) { return(ResolveProject(repositoryRoot, settings, fileFinder, searchOption: SearchOption.AllDirectories)); } // More than one solution is ambiguous if (solutions.Count > 1) { // TODO: Show relative paths in error messages ThrowAmbiguousSolutionsError(solutions); } // We have a solution VsSolution solution = solutions[0]; // We need to determine what project to deploy so get a list of all web projects and // figure out with some heuristic, which one to deploy. // TODO: Pick only 1 and throw if there's more than one // shunTODO need to implement this VsSolutionProject project = solution.Projects.Where(p => p.IsWap || p.IsWebSite || p.IsAspNetCore || p.IsFunctionApp).FirstOrDefault(); if (project == null) { // Try executable type project project = solution.Projects.Where(p => p.IsExecutable).FirstOrDefault(); if (project != null) { return(new DotNetConsoleBuilder(_environment, settings, _propertyProvider, repositoryRoot, project.AbsolutePath, solution.Path)); } logger.Log(Resources.Log_NoDeployableProjects, solution.Path); // we have a solution file, but no deployable project // shunTODO how often do we run into this return(ResolveNonAspProject(repositoryRoot, null, settings)); } if (project.IsWap) { return(new WapBuilder(_environment, settings, _propertyProvider, repositoryRoot, project.AbsolutePath, solution.Path)); } if (project.IsAspNetCore) { return(new AspNetCoreBuilder(_environment, settings, _propertyProvider, repositoryRoot, project.AbsolutePath, solution.Path)); } if (project.IsWebSite) { return(new WebSiteBuilder(_environment, settings, _propertyProvider, repositoryRoot, project.AbsolutePath, solution.Path)); } return(new FunctionMsbuildBuilder(_environment, settings, _propertyProvider, repositoryRoot, project.AbsolutePath, solution.Path)); }
public abstract DeployAction TryParseDeploymentInfo(System.Web.HttpRequestBase request, Newtonsoft.Json.Linq.JObject payload, string targetBranch, out DeploymentInfoBase deploymentInfo);
// { // 'format':'basic' // 'url':'http://host/repository', // 'is_hg':true // optional // } public override DeployAction TryParseDeploymentInfo(HttpRequest request, JObject payload, string targetBranch, out DeploymentInfoBase deploymentInfo) { deploymentInfo = null; if (!String.Equals(payload.Value <string>("format"), "basic", StringComparison.OrdinalIgnoreCase)) { return(DeployAction.UnknownPayload); } string url = payload.Value <string>("url"); if (String.IsNullOrEmpty(url)) { return(DeployAction.UnknownPayload); } string scm = payload.Value <string>("scm"); bool is_hg; if (String.IsNullOrEmpty(scm)) { // SSH hg@... vs git@... is_hg = url.StartsWith("hg@", StringComparison.OrdinalIgnoreCase); } else { is_hg = String.Equals(scm, "hg", StringComparison.OrdinalIgnoreCase); } deploymentInfo = new DeploymentInfo(RepositoryFactory); SetRepositoryUrl(deploymentInfo, url); deploymentInfo.RepositoryType = is_hg ? RepositoryType.Mercurial : RepositoryType.Git; deploymentInfo.Deployer = GetDeployerFromUrl(url); deploymentInfo.TargetChangeset = DeploymentManager.CreateTemporaryChangeSet(message: "Fetch from " + url); deploymentInfo.AllowDeploymentWhileScmDisabled = true; return(DeployAction.ProcessDeployment); }
public ISiteBuilder CreateBuilder(ITracer tracer, ILogger logger, IDeploymentSettingsManager settings, IRepository repository, DeploymentInfoBase deploymentInfo) { string repositoryRoot = repository.RepositoryPath; // Use the cached vs projects file finder for: a. better performance, b. ignoring solutions/projects under node_modules var fileFinder = new CachedVsProjectsFileFinder(repository); // If there's a custom deployment file then let that take over. var command = settings.GetValue(SettingsKeys.Command); if (!String.IsNullOrEmpty(command)) { return(new CustomBuilder(_environment, settings, _propertyProvider, repositoryRoot, command)); } // If the user provided specific generator arguments, that overrides any detection logic string scriptGeneratorArgs = settings.GetValue(SettingsKeys.ScriptGeneratorArgs); if (!String.IsNullOrEmpty(scriptGeneratorArgs)) { return(new CustomGeneratorCommandSiteBuilder(_environment, settings, _propertyProvider, repositoryRoot, scriptGeneratorArgs)); } // If the repository has an explicit pointer to a project path to be deployed // then use it. string targetProjectPath = settings.GetValue(SettingsKeys.Project); if (!String.IsNullOrEmpty(targetProjectPath)) { tracer.Trace("Specific project was specified: " + targetProjectPath); targetProjectPath = Path.GetFullPath(Path.Combine(repositoryRoot, targetProjectPath.TrimStart('/', '\\'))); } if (deploymentInfo != null && deploymentInfo.Deployer == Constants.OneDeploy) { var projectPath = !String.IsNullOrEmpty(targetProjectPath) ? targetProjectPath : repositoryRoot; return(new OneDeployBuilder(_environment, settings, _propertyProvider, repositoryRoot, projectPath, deploymentInfo)); } if (settings.RunFromLocalZip()) { return(new RunFromZipSiteBuilder()); } if (!settings.DoBuildDuringDeployment()) { var projectPath = !String.IsNullOrEmpty(targetProjectPath) ? targetProjectPath : repositoryRoot; return(new BasicBuilder(_environment, settings, _propertyProvider, repositoryRoot, projectPath)); } string msbuild16Log = String.Format("UseMSBuild16: {0}", VsHelper.UseMSBuild16().ToString()); tracer.Trace(msbuild16Log); KuduEventSource.Log.GenericEvent( ServerConfiguration.GetRuntimeSiteName(), msbuild16Log, string.Empty, string.Empty, string.Empty, string.Empty ); if (!String.IsNullOrEmpty(targetProjectPath)) { // Try to resolve the project return(ResolveProject(repositoryRoot, targetProjectPath, settings, fileFinder, tryWebSiteProject: true, searchOption: SearchOption.TopDirectoryOnly)); } // Get all solutions in the current repository path var solutions = VsHelper.GetSolutions(repositoryRoot, fileFinder).ToList(); if (!solutions.Any()) { return(ResolveProject(repositoryRoot, settings, fileFinder, searchOption: SearchOption.AllDirectories)); } // More than one solution is ambiguous if (solutions.Count > 1) { // TODO: Show relative paths in error messages ThrowAmbiguousSolutionsError(solutions); } // We have a solution VsSolution solution = solutions[0]; return(DetermineProjectFromSolution(logger, settings, repository, solution)); }
public Task Fetch(IRepository repository, DeploymentInfoBase deploymentInfo, string targetBranch, ILogger logger, ITracer tracer) { repository.FetchWithoutConflict(deploymentInfo.RepositoryUrl, targetBranch); return(_completed); }