Example #1
0
        private async Task <IMergePolicyRunner> CreateNew(MergePolicyRunnerFactoryContext context)
        {
            var policyConfiguration = await context.AzDoClient !.GetMergePoliciesAsync(context.Organization, context.RepositoryId);

            if (policyConfiguration.Value is null || policyConfiguration.Value.Count == 0)
            {
                return(NoopMergePolicyRunner.Instance);
            }

            var policies = new List <MergePolicy>();

            foreach (var policyConfig in policyConfiguration.Value.OrderBy(n => n.CreateDate))
            {
                var policyType = policyConfig.Strategy switch
                {
                    ReleaseBranchCascadingPolicy.PolicyName => typeof(ReleaseBranchCascadingPolicy),
                    SpecificSourceAndTargetPolicy.PolicyName => typeof(SpecificSourceAndTargetPolicy),
                    _ => null
                };

                if (policyType != null)
                {
                    var policy = (MergePolicy)_serviceProvider.GetService(policyType);
                    try
                    {
                        policy.Configure(policyConfig);
                    }
                    catch (Exception ex)
                    {
                        _logger.LogDebug(new EventId(1, "ConfigurationFailed"), ex, "Failed to configure {Policy} with {@Config}", policyType.Name, policyConfig);
                        return(NoopMergePolicyRunner.Instance);
                    }
                    policies.Add(policy);
                }
            }

            return(new MergePolicyRunner(
                       policies.ToArray(),
                       _serviceProvider.GetService <ILogger <MergePolicyRunner> >()
                       ));
        }
    }
Example #2
0
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(
            IApplicationBuilder app,
            IMergePolicyRunnerFactory runnerFactory,
            IAzureDevOpsClientFactory azDoClientFactory,
            ILogger <Startup> logger)
        {
            app.UseSerilogRequestLogging();
            app.Use(ExceptionHandler);
            app.UseRouting();
            app.UseCors();
            app.UseAuthentication();
            app.UseAuthorization();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapGet("/", Home);
                endpoints.MapPost("/jwt", JwtRoute);
                endpoints.MapPost("/webhook", Webhook).RequireAuthorization();
                endpoints.MapDelete("/policies", ClearPolicies);
            });

            async Task Home(HttpContext context)
            {
                await context.Response.WriteAsync("Merge-a-Bot");
            }

            async Task JwtRoute(HttpContext context)
            {
                using var sr = new StreamReader(context.Request.Body);
                var pat = await sr.ReadToEndAsync();

                if (string.IsNullOrEmpty(pat))
                {
                    context.Response.StatusCode = StatusCodes.Status400BadRequest;
                    await context.Response.WriteAsync("Body did not contain Personal Access Token");

                    return;
                }

                var claims = new List <Claim> {
                    new Claim(JwtRegisteredClaimNames.Sub, pat),
                };

                var token = new JwtSecurityToken(
                    issuer: JwtIssuer,
                    audience: null,
                    claims: claims,
                    expires: DateTime.UtcNow.Date.AddYears(1),
                    signingCredentials: new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(JwtSigningKey)), SecurityAlgorithms.HmacSha256)
                    );

                var tokenValue = new JwtSecurityTokenHandler().WriteToken(token);
                await context.Response.WriteAsync(tokenValue);
            }

            async Task Webhook(HttpContext context)
            {
                var request = context.Request;

                if (!HttpMethods.IsPost(request.Method))
                {
                    return;
                }

                if (request.ContentType == null || !request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
                {
                    return;
                }

                var payload = await WebhookDeserializer.DeserializeAsync(request.Body);

                var pat = context.User.FindFirstValue(ClaimTypes.NameIdentifier);

                if (payload != null)
                {
                    var azDoClient     = azDoClientFactory.Create(pat);
                    var organization   = payload.Resource.Repository.GetOrganization();
                    var baseUrl        = payload.Resource.Repository.GetBaseUrl();
                    var factoryContext = new MergePolicyRunnerFactoryContext(azDoClient, payload.Resource.Repository.Id, organization, baseUrl);
                    var runner         = await runnerFactory.CreateAsync(factoryContext);

                    await runner.RunAsync(azDoClient, payload);
                }
            }

            async Task ExceptionHandler(HttpContext context, Func <Task> next)
            {
                try
                {
                    await next();
                }
                catch (Exception ex)
                {
                    logger.LogError(new EventId(1, "UnhandledError"), ex, "Unhandled error");
                    context.Response.StatusCode = 200;
                }
            }

            Task ClearPolicies(HttpContext context)
            {
                var org    = context.Request.Query["organization"];
                var repoId = context.Request.Query["repositoryId"];

                if (string.IsNullOrEmpty(org) || string.IsNullOrEmpty(repoId))
                {
                    context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
                    return(Task.CompletedTask);
                }
                runnerFactory.Clear(new MergePolicyRunnerFactoryContext(repoId, org));
                return(Task.CompletedTask);
            }
        }
Example #3
0
 public void Clear(MergePolicyRunnerFactoryContext context)
 {
     _cache.Remove(context, out _);
 }
Example #4
0
 public Task <IMergePolicyRunner> CreateAsync(MergePolicyRunnerFactoryContext context)
 {
     return(_cache.GetOrAdd(context, _valueFactory));
 }
 public Task <IMergePolicyRunner> CreateAsync(MergePolicyRunnerFactoryContext context)
 {
     return(_valueFactory(context));
     // Caching by the context will not reflect changes that are done in DevOps for the Merge-Policies.
     //return _cache.GetOrAdd(context, _valueFactory);
 }