Skip to content

Commit 7124ddf

Browse files
authored
Merge pull request #761 from samsmithnz/Updating-For-2022-State-Of-DevOps-Report
Updating For 2022 State Of DevOps Report
2 parents 33eb6fe + ff03097 commit 7124ddf

31 files changed

+261
-115
lines changed

.github/workflows/dotnetcore.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ jobs:
134134
- name: Run Sonarcloud test
135135
uses: samsmithnz/[email protected]
136136
with:
137-
projects: 'src/DevOpsMetrics.Core/DevOpsMetrics.Core.csproj,src/DevOpsMetrics.Function/DevOpsMetrics.Function.csproj,src/DevOpsMetrics.FunctionalTests/DevOpsMetrics.FunctionalTests.csproj,src/DevOpsMetrics.Service/DevOpsMetrics.Service.csproj,src/DevOpsMetrics.Tests/DevOpsMetrics.Tests.csproj,src/DevOpsMetrics.Web/DevOpsMetrics.Web.csproj'
137+
projects: 'src/DevOpsMetrics.Core/DevOpsMetrics.Core.csproj,src/DevOpsMetrics.Function/DevOpsMetrics.Function.csproj,src/DevOpsMetrics.FunctionalTests/DevOpsMetrics.FunctionalTests.csproj,src/DevOpsMetrics.Service/DevOpsMetrics.Service.csproj,src/DevOpsMetrics.Tests/DevOpsMetrics.Tests.csproj,src/DevOpsMetrics.Web/DevOpsMetrics.Web.csproj,src/DevOpsMetrics.Cmd/DevOpsMetrics.Cmd.csproj'
138138
dotnet-version: '7.0.x'
139139
sonarcloud-organization: samsmithnz-github
140140
sonarcloud-project: samsmithnz_DevOpsMetrics
@@ -246,7 +246,6 @@ jobs:
246246
package: functionapp
247247
- name: Deploy function app settings
248248
run: az webapp config appsettings set --name "devops-prod-eu-function" --resource-group "devopsmetrics" --settings "AppSettings:AzureDevOpsPatToken=${{ secrets.AzureDevOpsPATToken }}" "AppSettings:GitHubClientId=${{ secrets.GitHubClientId }}" "AppSettings:GitHubClientSecret=${{ secrets.GitHubClientSecret }}" "AppSettings:AzureStorageAccountConfigurationString=${{ secrets.AzureStorageConnectionString }}" "AppSettings:WebServiceURL=https://devops-prod-eu-service.azurewebsites.net/" "AppSettings:KeyVaultClientId=${{ secrets.KeyVaultClientId }}" "AppSettings:KeyVaultClientSecret=${{ secrets.KeyVaultClientSecret }}" "AppSettings:KeyVaultURL=https://devops-prod-eu-vault.vault.azure.net/" "AzureStorageAccountContainerAzureDevOpsBuilds=DevOpsAzureDevOpsBuilds" "AppSettings:AzureStorageAccountContainerAzureDevOpsPRs=DevOpsAzureDevOpsPRs" "AppSettings:AzureStorageAccountContainerAzureDevOpsPRCommits=DevOpsAzureDevOpsPRCommits" "AppSettings:AzureStorageAccountContainerAzureDevOpsSettings=DevOpsAzureDevOpsSettings" "AppSettings:AzureStorageAccountContainerGitHubRuns=DevOpsGitHubRuns" "AppSettings:AzureStorageAccountContainerGitHubPRs=DevOpsGitHubPRs" "AppSettings:AzureStorageAccountContainerGitHubPRCommits=DevOpsGitHubPRCommits" "AppSettings:AzureStorageAccountContainerGitHubSettings=DevOpsGitHubSettings" "AppSettings:AzureStorageAccountContainerMTTR=DevOpsMTTR" "AppSettings:AzureStorageAccountContainerChangeFailureRate=DevOpsChangeFailureRate" "AppSettings:AzureStorageAccountContainerTableLog=DevOpsLog"
249-
250249
- name: Display GitVersion outputs
251250
run: |
252251
echo "Version: ${{ needs.build.outputs.Version }}"
@@ -261,6 +260,7 @@ jobs:
261260
tag_name: ${{ needs.build.outputs.Version }}
262261
release_name: Release ${{ needs.build.outputs.Version }}
263262

263+
264264
swapDeploymentSlots:
265265
runs-on: ubuntu-latest # Note, Azure CLI requires a Linux runner...
266266
needs: [build, deployToStagingSlots]

GitVersion.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
next-version: 1.5.0
1+
next-version: 1.6.0

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ This project is focused on helping you collect and analyze four key high perform
1515
- **Mean time to restore (MTTR): How quickly restoration of production occurs in an outage or degradation.** When there is a degradation, how quickly can the system auto-heal itself, scale to handle increased load, and/or This one is contraversal, as it's challenging to compare different events that cause degradation.
1616
- **Change failure rate: After a production deployment, was it successful? Or was a fix or rollback required after the fact?** How often is a change we made 'successful'? This ties in well with deployment frequency and lead time for changes, but is challenging to measure - as it requires a signoff off of success. Not just that the code deployed correctly, but that there weren't adverse effects or degradation of the deployment to the system
1717

18-
![High performing metrics](https://user-images.githubusercontent.com/8389039/140629477-27849eb7-0550-4ccd-ae43-dee43e48f3ec.png)
19-
(Chart from [page 9 of state of DevOps 2021 report](https://services.google.com/fh/files/misc/state-of-devops-2021.pdf))
18+
![High performing metrics](https://user-images.githubusercontent.com/8389039/212061370-6984b2c3-bc13-4d92-8afc-0068be4cdde1.png)
19+
(Chart from [page 11 of state of DevOps 2022 report](https://cloud.google.com/devops/state-of-devops))
2020
A [demo website displaying the metrics can be viewed here](https://devops-prod-eu-web.azurewebsites.net/).
2121
More information about high performing DevOps metrics can be found in a [blog post here](https://samlearnsazure.blog/2020/04/30/high-performing-devops-metrics/)
2222

@@ -79,7 +79,7 @@ Dependabot runs daily to check for dependency upgrades.
7979

8080
## Badges
8181
The API can generate a URL for static badges, but more work is needed. Some current samples are shown below:
82-
[![Build](https://img.shields.io/badge/Deployment%20frequency-Elite-brightgreen)](https://img.shields.io/badge/Deployment%20frequency-Elite-brightgreen) [![Build](https://img.shields.io/badge/Lead%20time%20for%20changes-High-green)](https://img.shields.io/badge/Lead%20time%20for%20changes-High-green) [![Build](https://img.shields.io/badge/Time%20to%20restore%20service-Medium-orange)](https://img.shields.io/badge/Time%20to%20restore%20service-Medium-orange) [![Build](https://img.shields.io/badge/Change%20failure%20rate-Low-red)](https://img.shields.io/badge/Change%20failure%20rate-Low-red)
82+
[![Build](https://img.shields.io/badge/Lead%20time%20for%20changes-High-green)](https://img.shields.io/badge/Lead%20time%20for%20changes-High-green) [![Build](https://img.shields.io/badge/Time%20to%20restore%20service-Medium-orange)](https://img.shields.io/badge/Time%20to%20restore%20service-Medium-orange) [![Build](https://img.shields.io/badge/Change%20failure%20rate-Low-red)](https://img.shields.io/badge/Change%20failure%20rate-Low-red)
8383

8484
# Setup
8585

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net7.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
<UserSecretsId>dc96f8ff-2de5-4ed3-b124-89a134688b66</UserSecretsId>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<None Remove="appsettings.json" />
13+
</ItemGroup>
14+
15+
<ItemGroup>
16+
<Content Include="appsettings.json">
17+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
18+
</Content>
19+
</ItemGroup>
20+
21+
<ItemGroup>
22+
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="7.0.0" />
23+
</ItemGroup>
24+
25+
<ItemGroup>
26+
<ProjectReference Include="..\DevOpsMetrics.Core\DevOpsMetrics.Core.csproj" />
27+
<ProjectReference Include="..\DevOpsMetrics.Function\DevOpsMetrics.Function.csproj" />
28+
<ProjectReference Include="..\DevOpsMetrics.Service\DevOpsMetrics.Service.csproj" />
29+
</ItemGroup>
30+
31+
</Project>

src/DevOpsMetrics.Cmd/Program.cs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
using System.Reflection;
2+
using DevOpsMetrics.Core.DataAccess.TableStorage;
3+
using DevOpsMetrics.Core.Models.AzureDevOps;
4+
using DevOpsMetrics.Core.Models.Common;
5+
using DevOpsMetrics.Core.Models.GitHub;
6+
using DevOpsMetrics.Function;
7+
using DevOpsMetrics.Service;
8+
using DevOpsMetrics.Service.Controllers;
9+
using Microsoft.Azure.KeyVault;
10+
using Microsoft.Azure.Services.AppAuthentication;
11+
using Microsoft.Extensions.Configuration;
12+
13+
namespace DevOpsMetrics.Cmd
14+
{
15+
internal class Program
16+
{
17+
static async Task Main(string[] args)
18+
{
19+
Console.WriteLine($"C# Timer trigger function UpdateStorageTables started at: {DateTime.Now}");
20+
21+
//Load settings
22+
IConfigurationBuilder? builder = new ConfigurationBuilder()
23+
.SetBasePath(Directory.GetCurrentDirectory())
24+
.AddJsonFile("appsettings.json", optional: false)
25+
.AddUserSecrets<Program>(true);
26+
IConfigurationRoot Configuration = builder.Build();
27+
28+
string keyVaultURL = Configuration["AppSettings:KeyVaultURL"];
29+
string keyVaultId = Configuration["AppSettings:KeyVaultClientId"];
30+
string keyVaultSecret = Configuration["AppSettings:KeyVaultClientSecret"];
31+
AzureServiceTokenProvider azureServiceTokenProvider = new();
32+
KeyVaultClient keyVaultClient = new(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
33+
builder.AddAzureKeyVault(keyVaultURL, keyVaultId, keyVaultSecret);
34+
Configuration = builder.Build();
35+
36+
//Get settings
37+
string clientId = Configuration["AppSettings:GitHubClientId"];
38+
string clientSecret = Configuration["AppSettings:GitHubClientSecret"];
39+
AzureTableStorageDA azureTableStorageDA = new();
40+
BuildsController buildsController = new(Configuration, azureTableStorageDA);
41+
PullRequestsController pullRequestsController = new(Configuration, azureTableStorageDA);
42+
SettingsController settingsController = new(Configuration, azureTableStorageDA);
43+
List<AzureDevOpsSettings> azSettings = settingsController.GetAzureDevOpsSettings();
44+
List<GitHubSettings> ghSettings = settingsController.GetGitHubSettings();
45+
TableStorageConfiguration tableStorageConfig = Common.GenerateTableStorageConfiguration(Configuration);
46+
47+
//Loop through each setting to update the runs, pull requests and pull request commits
48+
int numberOfDays = 30;
49+
int maxNumberOfItems = 20;
50+
int totalResults = 0;
51+
foreach (AzureDevOpsSettings item in azSettings)
52+
{
53+
// (int, string) buildsUpdated = (0, null);
54+
// (int, string) prsUpdated = (0, null);
55+
// try
56+
// {
57+
Console.WriteLine($"Processing Azure DevOps organization {item.Organization}, project {item.Project}");
58+
// buildsUpdated = await api.UpdateAzureDevOpsBuilds(item.Organization, item.Project, item.Repository, item.Branch, item.BuildName, item.BuildId, numberOfDays, maxNumberOfItems);
59+
// prsUpdated = await api.UpdateAzureDevOpsPullRequests(item.Organization, item.Project, item.Repository, numberOfDays, maxNumberOfItems);
60+
// log.LogInformation($"Processed Azure DevOps organization {item.Organization}, project {item.Project}. {buildsUpdated.Item1} builds and {prsUpdated.Item1} prs/commits updated");
61+
// totalResults += buildsUpdated.Item1 + prsUpdated.Item1;
62+
// await api.UpdateAzureDevOpsProjectLog(item.Organization, item.Project, item.Repository, buildsUpdated.Item1, prsUpdated.Item1, buildsUpdated.Item2, prsUpdated.Item2, null, null);
63+
// }
64+
// catch (Exception ex)
65+
// {
66+
// string error = $"Exception while processing Azure DevOps organization {item.Organization}, project {item.Project}. {buildsUpdated.Item1} builds and {prsUpdated.Item1} prs/commits updated";
67+
// log.LogInformation(error);
68+
// await api.UpdateAzureDevOpsProjectLog(item.Organization, item.Project, item.Repository, buildsUpdated.Item1, prsUpdated.Item1, buildsUpdated.Item2, prsUpdated.Item2, ex.Message, error);
69+
// }
70+
}
71+
72+
foreach (GitHubSettings ghSetting in ghSettings)
73+
{
74+
ProcessingResult ghResult = await Processing.ProcessGitHubItem(ghSetting,
75+
clientId, clientSecret, tableStorageConfig,
76+
numberOfDays, maxNumberOfItems,
77+
buildsController, pullRequestsController, settingsController,
78+
null, totalResults);
79+
totalResults = ghResult.TotalResults;
80+
}
81+
Console.WriteLine($"C# Timer trigger function complete at: {DateTime.Now} after updating {totalResults} records");
82+
83+
}
84+
}
85+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"AppSettings": {
3+
"GitHubClientId": "", //Set in key vault
4+
"GitHubClientSecret": "", //Set in key vault
5+
"AzureDevOpsPatToken": "", //Set in key vault
6+
"AzureStorageAccountConfigurationString": "",
7+
"AzureStorageAccountContainerAzureDevOpsBuilds": "DevOpsAzureDevOpsBuilds",
8+
"AzureStorageAccountContainerAzureDevOpsPRs": "DevOpsAzureDevOpsPRs",
9+
"AzureStorageAccountContainerAzureDevOpsPRCommits": "DevOpsAzureDevOpsPRCommits",
10+
"AzureStorageAccountContainerAzureDevOpsSettings": "DevOpsAzureDevOpsSettings",
11+
"AzureStorageAccountContainerGitHubRuns": "DevOpsGitHubRuns",
12+
"AzureStorageAccountContainerGitHubPRs": "DevOpsGitHubPRs",
13+
"AzureStorageAccountContainerGitHubPRCommits": "DevOpsGitHubPRCommits",
14+
"AzureStorageAccountContainerGitHubSettings": "DevOpsGitHubSettings",
15+
"AzureStorageAccountContainerMTTR": "DevOpsMTTR",
16+
"AzureStorageAccountContainerChangeFailureRate": "DevOpsChangeFailureRate",
17+
"AzureStorageAccountDORASummaryItem": "DevOpsDORASummaryItem",
18+
"AzureStorageAccountContainerTableLog": "DevOpsLog",
19+
"WebServiceURL": "https://devops-prod-eu-service.azurewebsites.net/",
20+
"KeyVaultURL": "https://devops-prod-eu-vault.vault.azure.net/",
21+
"KeyVaultClientId": "",
22+
"KeyVaultClientSecret": ""
23+
},
24+
"ApplicationInsights": {
25+
"InstrumentationKey": "dc3fb590-3612-4f09-8c95-e9378cba8654"
26+
}
27+
}

src/DevOpsMetrics.Core/ChangeFailureRate.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,21 +80,21 @@ public static string GetChangeFailureRateRating(float changeFailureRate)
8080
rating = "None";
8181
}
8282
else if (changeFailureRate <= 0.15f) //0-15%
83-
{
84-
rating = "Elite";
85-
}
86-
else if (changeFailureRate <= 0.30f) //16-30% (not a typo, overriding table to <= 30% to create a range)
8783
{
8884
rating = "High";
8985
}
90-
else if (changeFailureRate < 0.46f) //16-30% (not a typo, overriding table to < 46% to create a range)
86+
else if (changeFailureRate <= 0.30f) //16-30%
9187
{
9288
rating = "Medium";
9389
}
94-
else if (changeFailureRate >= 0.46f)// 16-30% (not a typo, overriding table to > 46% to create a range)
90+
else if (changeFailureRate <= 0.60f) //46-60% (not a typo, overriding table from 46-60 to < 60% to create a range)
9591
{
9692
rating = "Low";
9793
}
94+
else //no rating
95+
{
96+
rating = "None";
97+
}
9898
return rating;
9999
}
100100
}

src/DevOpsMetrics.Core/DataAccess/DeploymentFrequencyDA.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ public static async Task<DeploymentFrequencyModel> GetAzureDevOpsDeploymentFrequ
107107
DeploymentName = buildName,
108108
BuildList = builds,
109109
DeploymentsPerDayMetric = 10f,
110-
DeploymentsPerDayMetricDescription = "Elite",
110+
DeploymentsPerDayMetricDescription = "High",
111111
NumberOfDays = numberOfDays,
112112
MaxNumberOfItems = builds.Count,
113113
TotalItems = builds.Count
@@ -212,7 +212,7 @@ public static async Task<DeploymentFrequencyModel> GetGitHubDeploymentFrequency(
212212
DeploymentName = workflowName,
213213
BuildList = builds,
214214
DeploymentsPerDayMetric = 10f,
215-
DeploymentsPerDayMetricDescription = "Elite",
215+
DeploymentsPerDayMetricDescription = "High",
216216
NumberOfDays = numberOfDays,
217217
MaxNumberOfItems = builds.Count,
218218
TotalItems = builds.Count

src/DevOpsMetrics.Core/DataAccess/LeadTimeForChangesDA.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ public static async Task<LeadTimeForChangesModel> GetAzureDevOpsLeadTimesForChan
166166
AverageBuildHours = 1f,
167167
AveragePullRequestHours = 12f,
168168
LeadTimeForChangesMetric = 12f + 1f,
169-
LeadTimeForChangesMetricDescription = "Elite",
169+
LeadTimeForChangesMetricDescription = "High",
170170
PullRequests = samplePullRequests,
171171
NumberOfDays = numberOfDays,
172172
MaxNumberOfItems = samplePullRequests.Count,
@@ -346,7 +346,7 @@ public static async Task<LeadTimeForChangesModel> GetGitHubLeadTimesForChanges(b
346346
AverageBuildHours = 1f,
347347
AveragePullRequestHours = 20.33f,
348348
LeadTimeForChangesMetric = 20.33f + 1f,
349-
LeadTimeForChangesMetricDescription = "Elite",
349+
LeadTimeForChangesMetricDescription = "High",
350350
PullRequests = samplePullRequests,
351351
NumberOfDays = numberOfDays,
352352
MaxNumberOfItems = samplePullRequests.Count,

src/DevOpsMetrics.Core/DeploymentFrequency.cs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -70,26 +70,22 @@ public static string GetDeploymentFrequencyRating(float deploymentsPerDay)
7070
float dailyDeployment = 1f;
7171
//float weeklyDeployment = 1f / 7f;
7272
float monthlyDeployment = 1f / 30f;
73-
float everySixMonthsDeployment = 1f / (6f * 30f); //Every 6 months
73+
//float everySixMonthsDeployment = 1f / (6f * 30f); //Every 6 months
7474

7575
string rating = "";
7676
if (deploymentsPerDay <= 0f)
7777
{
7878
rating = "None";
7979
}
80-
else if (deploymentsPerDay >= dailyDeployment) //NOTE: Does not capture on-demand deployments
81-
{
82-
rating = "Elite";
83-
}
84-
else if (deploymentsPerDay < dailyDeployment && deploymentsPerDay >= monthlyDeployment)
80+
else if (deploymentsPerDay >= dailyDeployment) //NOTE: Assumes on-demand deployments are <1 day
8581
{
8682
rating = "High";
8783
}
88-
else if (deploymentsPerDay < monthlyDeployment && deploymentsPerDay >= everySixMonthsDeployment)
84+
else if (deploymentsPerDay < dailyDeployment && deploymentsPerDay >= monthlyDeployment) //Captures days to months
8985
{
9086
rating = "Medium";
9187
}
92-
else if (deploymentsPerDay < everySixMonthsDeployment)
88+
else if (deploymentsPerDay < monthlyDeployment)
9389
{
9490
rating = "Low";
9591
}

0 commit comments

Comments
 (0)