Skip to content

Commit fd05618

Browse files
Calin LupasCalin Lupas
authored andcommitted
Refactor GitHub integration and update documentation
- Updated `README.md` to remove contributing and trademarks sections. - Modified `main.md` to remove options for automatic issue approval and inventory usage. - Removed feature flags section from documentation. - Added `MergePullRequestAsync` method in `GitHubClientExtensions.cs` for enhanced pull request merging. - Updated `GitHubOptions` to remove required property for repository rulesets. - Updated package references in `DevExcelerateApi.csproj` to newer versions. - Added new properties for auto-approving issues and pull requests in `DevExOrganizationPolicies.cs`. - Removed feature management service registration in `Program.cs`. - Updated `DevExIssuesEventProcessorService.cs` to use policy service for feature flag checks. - Enhanced `IPolicyService.cs` to include method for retrieving organization policies. - Implemented policy retrieval based on webhook events in `PolicyService.cs`. - Updated `RepositoryService.cs` and `TeamService.cs` to utilize the new policy service for custom property names and operations. - Adjusted `appsettings.json` to remove feature management configuration and update GitHub settings structure.
1 parent 639f65f commit fd05618

13 files changed

Lines changed: 136 additions & 99 deletions

README.md

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,3 @@ GitHub DevExcelerate - Platform Engineering. Improve the Developer Experience an
33
---
44

55
Please view our documentation at [docs/main.md](./docs/main.md) for more information.
6-
7-
## Contributing
8-
9-
Lorem ipsum
10-
11-
## Trademarks
12-
13-
Lorem ipsum

docs/main.md

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,6 @@ This application is built using .NET Core. It supports multiple data stores, inc
2727
| `GitHub:RepoOnboarding` | String | Repository for onboarding. |
2828
| `GitHub:RepoInventory` | String | Repository for inventory. |
2929
| `GitHub:RepoPolicy` | String | Repository for policy. |
30-
| `GitHub:IssueAutoApprove` | String | Automatically approve issues, "true" or "false". |
31-
| `GitHub:UseInventoryWithPullRequest` | String | Use inventory with pull requests, "true" or "false". |
32-
| `GitHub:RepoRulesetsCustomPropertyName` | String | Custom property name for repository rulesets. |
3330
| `Azure:KeyVaultName` | String | Name of the Azure Key Vault. |
3431
| `Azure:KeyName` | String | Name of the key in Azure Key Vault. |
3532
| `Azure:AzureMonitor:EnableTelemetry` | String | Whether to enable telemetry or not. |
@@ -41,28 +38,6 @@ This application is built using .NET Core. It supports multiple data stores, inc
4138
| `DataStore:CosmosDb:GitHubWebhooksContainer` | String | Container for GitHub webhooks in Cosmos DB. |
4239
| `DataStore:SqlServer:ConnectionString` | String | Connection string for SQL Server. |
4340

44-
### Feature flags
45-
46-
Follows the .NET FeatureManagement [schema](https://learn.microsoft.com/en-us/azure/azure-app-configuration/feature-management-dotnet-reference#net-feature-management-schema)
47-
48-
```json
49-
"feature_management": {
50-
"feature_flags": [
51-
{
52-
"id": "IssueAutoApprove",
53-
"display_name": "Auto Approve Issues",
54-
"description": "Whether to auto approve issues or let someone approve them",
55-
"enabled": true
56-
},
57-
{
58-
"id": "UseInventoryWithPullRequest",
59-
"display_name": "Use Pull Request for Inventory Metadata",
60-
"description": "Determines whether to use a pull request to populate the inventory metadata or not",
61-
"enabled": true
62-
}
63-
]
64-
}
65-
```
6641

6742
### Example
6843

@@ -85,9 +60,6 @@ Follows the .NET FeatureManagement [schema](https://learn.microsoft.com/en-us/az
8560
"RepoOnboarding": "",
8661
"RepoInventory": "",
8762
"RepoPolicy": "",
88-
"IssueAutoApprove": "true",
89-
"UseInventoryWithPullRequest": "true",
90-
"RepoRulesetsCustomPropertyName": ""
9163
},
9264
"Azure": {
9365
"KeyVaultName": "",

src/DevExcelerateApi/Core/Extensions/GitHubClientExtensions.cs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,59 @@ public static async Task<PullRequest> CreatePullRequestAsync(this IGitHubClient
192192
return await gitHubClient.PullRequest.Create(owner, repo, newPullRequest);
193193
}
194194

195+
public static async Task<PullRequestMerge> MergePullRequestAsync(this IGitHubClient gitHubClient, string owner, string repo, int pullRequestNumber, string? commitTitle = null, string? commitMessage = null)
196+
{
197+
if (gitHubClient == null)
198+
{
199+
throw new InvalidOperationException("GitHub client is not configured.");
200+
}
201+
202+
try
203+
{
204+
// First, check if the pull request exists and is mergeable
205+
var pullRequest = await gitHubClient.PullRequest.Get(owner, repo, pullRequestNumber);
206+
207+
if (pullRequest.State == ItemState.Closed)
208+
{
209+
throw new InvalidOperationException($"Pull request #{pullRequestNumber} is already closed.");
210+
}
211+
212+
if (pullRequest.Merged == true)
213+
{
214+
throw new InvalidOperationException($"Pull request #{pullRequestNumber} is already merged.");
215+
}
216+
217+
if (pullRequest.Mergeable == false)
218+
{
219+
throw new InvalidOperationException($"Pull request #{pullRequestNumber} has conflicts and cannot be merged.");
220+
}
221+
222+
// Create the merge request
223+
var mergePullRequest = new MergePullRequest
224+
{
225+
CommitTitle = commitTitle ?? pullRequest.Title,
226+
CommitMessage = commitMessage ?? pullRequest.Body
227+
};
228+
229+
// Perform the merge
230+
var mergeResult = await gitHubClient.PullRequest.Merge(owner, repo, pullRequestNumber, mergePullRequest);
231+
232+
return mergeResult;
233+
}
234+
catch (NotFoundException)
235+
{
236+
throw new InvalidOperationException($"Pull request #{pullRequestNumber} not found in repository '{owner}/{repo}'.");
237+
}
238+
catch (ApiException ex) when (ex.StatusCode == System.Net.HttpStatusCode.MethodNotAllowed)
239+
{
240+
throw new InvalidOperationException($"Pull request #{pullRequestNumber} cannot be merged. It may have conflicts, required status checks may be failing, or branch protection rules may be preventing the merge.");
241+
}
242+
catch (Exception ex)
243+
{
244+
throw new InvalidOperationException($"Failed to merge pull request #{pullRequestNumber} in repository '{owner}/{repo}': {ex.Message}", ex);
245+
}
246+
}
247+
195248
public static async Task<IReadOnlyList<GitHubCommitFile>> GetCommitFilesAsync(this IGitHubClient gitHubClient, string owner, string repo, string commitSha)
196249
{
197250
if (gitHubClient == null)

src/DevExcelerateApi/Core/Options/GitHubOptions.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,6 @@ public class GitHubOptions
4646
[Required]
4747
public required string RepoPolicy { get; set; }
4848

49-
/// <summary>
50-
/// The custom property name for the repository rulesets.
51-
/// </summary>
52-
[Required]
53-
public required string RepoRulesetsCustomPropertyName { get; set; }
54-
5549
/// <summary>
5650
/// Gets or sets the local path to the RSA private key used for the signing credentials.
5751
/// </summary>

src/DevExcelerateApi/DevExcelerateApi.csproj

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,12 @@
1111
<PackageReference Include="Azure.Identity" Version="1.14.0" />
1212
<PackageReference Include="Azure.Monitor.OpenTelemetry.AspNetCore" Version="1.3.0" />
1313
<PackageReference Include="Azure.Security.KeyVault.Keys" Version="4.7.0" />
14-
<PackageReference Include="Microsoft.Azure.Cosmos" Version="3.51.0" />
15-
<PackageReference Include="Microsoft.FeatureManagement" Version="4.0.0" />
16-
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.10.0" />
14+
<PackageReference Include="Microsoft.Azure.Cosmos" Version="3.52.0" />
15+
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.12.1" />
1716
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
1817
<PackageReference Include="Octokit" Version="14.0.0" />
1918
<PackageReference Include="Octokit.Webhooks.AspNetCore" Version="2.4.1" />
20-
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.10.0" />
19+
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.12.1" />
2120
<PackageReference Include="YamlDotNet" Version="16.3.0" />
2221
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.0.2" />
2322
<PackageReference Include="Dapper" Version="2.1.66" />

src/DevExcelerateApi/Models/DevExOrganizationPolicies.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,30 @@ public class DevExOrganizationPolicies
2727
[YamlMember(Alias = "team_naming_convention", Order = 5)]
2828
public string? TeamNamingConvention { get; set; }
2929

30+
/// <summary>
31+
/// Whether to auto approve issues or let someone approve them
32+
/// </summary>
33+
[YamlMember(Alias = "issue_auto_approve", Order = 6)]
34+
public bool? IssueAutoApprove { get; set; } = true;
35+
36+
/// <summary>
37+
/// Whether to auto approve pull requests or let someone approve them
38+
/// </summary>
39+
[YamlMember(Alias = "pull_request_auto_approve", Order = 7)]
40+
public bool? PullRequestAutoApprove { get; set; } = true;
41+
42+
/// <summary>
43+
/// Determines whether to use a pull request to populate the inventory metadata or not
44+
/// </summary>
45+
[YamlMember(Alias = "use_inventory_with_pull_request", Order = 8)]
46+
public bool? UseInventoryWithPullRequest { get; set; } = true;
47+
48+
/// <summary>
49+
/// The custom property name for the repository rulesets.
50+
/// </summary>
51+
[YamlMember(Alias = "repo_rulesets_custom_property_name", Order = 9)]
52+
public string? RepoRulesetsCustomPropertyName { get; set; } = "DevExcelerate-Rulesets";
53+
3054
public static DevExOrganizationPolicies DeserializeFromYaml(string yamlContent)
3155
{
3256
ArgumentNullException.ThrowIfNull(yamlContent);

src/DevExcelerateApi/Program.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
using System.Text.Json;
99
using Azure.Core;
1010
using Azure.Identity;
11-
using Microsoft.FeatureManagement;
1211

1312
// see https://learn.microsoft.com/en-us/aspnet/core/fundamentals/localization/provide-resources?view=aspnetcore-9.0#culture-fallback-behavior
1413
[assembly: NeutralResourcesLanguage("en")]
@@ -71,8 +70,6 @@ public static async Task Main(string[] args)
7170
});
7271
}
7372

74-
builder.Services.AddFeatureManagement();
75-
7673
// Application Build & Run
7774

7875
// Configure middleware and endpoints

src/DevExcelerateApi/Services/DevExIssuesEventProcessorService.cs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
using DevExcelerateApi.Core.Extensions;
22
using DevExcelerateApi.Data;
33
using DevExcelerateApi.Models;
4-
using Microsoft.Azure.Cosmos.Linq;
5-
using Microsoft.FeatureManagement;
64
using Octokit.Webhooks;
75
using Octokit.Webhooks.Events;
86
using Octokit.Webhooks.Events.IssueComment;
@@ -20,7 +18,7 @@ public class DevExIssuesEventProcessorService(
2018
GitHubWebhookRepository gitHubWebhookRepository,
2119
IssueRequestRepository issueRequestRepository,
2220
ILocalizationService localizationService,
23-
IVariantFeatureManager featureManager)
21+
IPolicyService policyService)
2422
: IDevExIssuesEventProcessorService
2523
{
2624
private readonly ILogger<DevExIssuesEventProcessorService> _logger = logger;
@@ -30,8 +28,8 @@ public class DevExIssuesEventProcessorService(
3028
private readonly GitHubWebhookRepository _gitHubWebhookRepository = gitHubWebhookRepository;
3129
private readonly IssueRequestRepository _issueRequestRepository = issueRequestRepository;
3230
private readonly ILocalizationService _localizationService = localizationService;
33-
private readonly IVariantFeatureManager _featureManager = featureManager;
34-
31+
private readonly IPolicyService _policyService = policyService;
32+
3533
public async Task SaveAndProcessIssueEventAsync(WebhookHeaders headers, IssuesEvent issuesEvent, IssuesAction action)
3634
{
3735
_logger.LogInformation("Starting the process of the Issues event for Issue# {number} with action - {action}...", issuesEvent?.Issue.Number, issuesEvent?.Action);
@@ -95,7 +93,7 @@ private async Task ValidateIssueApproval(IssuesEvent issuesEvent)
9593
// Opened
9694
if ((issuesEvent?.Issue?.Labels?.Any(i => i.Name.IsIssueOpened())).GetValueOrDefault())
9795
{
98-
if (await _featureManager.IsEnabledAsync("IssueAutoApprove"))
96+
if ((await _policyService.GetDevExOrganizationPolicies(issuesEvent!))?.IssueAutoApprove == true)
9997
{
10098
// TODO: add more conditions for auto-approval
10199

@@ -148,7 +146,7 @@ private async Task ValidateIssue(IssuesEvent issuesEvent)
148146
}
149147
else if ((issuesEvent?.Issue?.Labels?.Any(i => i.Name.IsIssueValidated())).GetValueOrDefault())
150148
{
151-
if (await _featureManager.IsEnabledAsync("UseInventoryWithPullRequest"))
149+
if ((await _policyService.GetDevExOrganizationPolicies(issuesEvent!))?.UseInventoryWithPullRequest == true)
152150
{
153151
// IF validated, create a pull request
154152
await _repositoryService.ValidateRepository(issuesEvent!, createPullRequest: true);
@@ -174,7 +172,7 @@ private async Task ValidateIssue(IssuesEvent issuesEvent)
174172
}
175173
else if ((issuesEvent?.Issue?.Labels?.Any(i => i.Name.IsIssueValidated())).GetValueOrDefault())
176174
{
177-
if (await _featureManager.IsEnabledAsync("UseInventoryWithPullRequest"))
175+
if ((await _policyService.GetDevExOrganizationPolicies(issuesEvent!))?.UseInventoryWithPullRequest == true)
178176
{
179177
// IF validated, create a pull request
180178
await _teamService.ValidateTeam(issuesEvent!, createPullRequest: true);
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using Octokit.Webhooks;
1+
using DevExcelerateApi.Models;
2+
using Octokit.Webhooks;
23
using System.ComponentModel.DataAnnotations;
34

45
namespace DevExcelerateApi.Services
@@ -7,5 +8,6 @@ public interface IPolicyService
78
{
89
Task<ValidationResult> ValidateRepositoryNameAsync(string name, WebhookEvent webhookEvent);
910
Task<ValidationResult> ValidateTeamNameAsync(string name, WebhookEvent webhookEvent);
11+
Task<DevExOrganizationPolicies> GetDevExOrganizationPolicies(WebhookEvent webhookEvent);
1012
}
1113
}

src/DevExcelerateApi/Services/PolicyService.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ public async Task<ValidationResult> ValidateTeamNameAsync(string name, WebhookEv
4343
"Team");
4444
}
4545

46+
public async Task<DevExOrganizationPolicies> GetDevExOrganizationPolicies(WebhookEvent webhookEvent)
47+
{
48+
return await GetDevExOrganizationPolicies(webhookEvent?.Installation?.Id, webhookEvent?.Organization?.Login!);
49+
}
50+
4651
private async Task<ValidationResult> ValidateNameAgainstPolicyAsync(
4752
string name,
4853
WebhookEvent webhookEvent,

0 commit comments

Comments
 (0)