Skip to content

Commit

Permalink
107 initial article sync (#108)
Browse files Browse the repository at this point in the history
* Create initial DataSync project

* Move GithubPushWebHookPayload model

* Initial app working with hard coded commit

* Getting commits for each file

* Note

* DataSync working

* Adding auth to GitHub api calls

* Adding GitHub PAT env var

* Change order of param

* Underscores rather than :

* Add incremental to deployment mode

* Remove unique name as a param

Build it from RG name

* Remove hyphens from unique name

* Revert location

* Output uniqueName

* hard code name

* Remove hyphen from RG name

* Use params with defaults

* Capitalisation

* Revert to V1

* Passing unique label as param

* Build params file

* Remove at

* toLower and Label rather than name

* Label rather than name

* New RG name and unique label

* Fail on error

* Updated params for both dev and prod

* Adding manual depends

* Remove depends

* new label

* use consistent label for prod
  • Loading branch information
martinkearn authored Oct 7, 2024
1 parent 24792c2 commit e7468f4
Show file tree
Hide file tree
Showing 16 changed files with 279 additions and 29 deletions.
5 changes: 3 additions & 2 deletions .github/workflows/cd-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ jobs:
uses: ./.github/workflows/deployment.yml
with:
rg: 'MartinkMe-Dev'
uniquelabel: 'MartinkMeDev'
unique-label: 'mkmedev'
secrets:
azure-client-id: ${{ secrets.AZURE_CLIENT_ID }}
azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }}
azure-subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
azure-subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
github-pat: ${{ secrets.GH_PAT }}
5 changes: 3 additions & 2 deletions .github/workflows/cd-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ jobs:
uses: ./.github/workflows/deployment.yml
with:
rg: 'MartinkMe-Prod'
uniquelabel: 'MartinkMeProd'
unique-label: 'mkmeprod'
secrets:
azure-client-id: ${{ secrets.AZURE_CLIENT_ID }}
azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }}
azure-subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
azure-subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
github-pat: ${{ secrets.GH_PAT }}
29 changes: 26 additions & 3 deletions .github/workflows/deployment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
rg:
type: string
required: true
uniquelabel:
unique-label:
type: string
required: true
secrets:
Expand All @@ -16,6 +16,8 @@ on:
required: true
azure-subscription-id:
required: true
github-pat:
required: true


permissions:
Expand Down Expand Up @@ -54,14 +56,30 @@ jobs:
- name: bicep-install
run: az bicep upgrade

- name: bicep-parameters
run: |
echo "{
\"$schema\": \"https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#\",
\"contentVersion\": \"1.0.0.0\",
\"parameters\": {
\"githubPat\": {
\"value\": \"${{ secrets.github-pat }}\"
},
\"uniqueLabel\": {
\"value\": \"${{ inputs.unique-label }}\"
}
}
}" > parameters.json
- name: bicep-deploy
uses: azure/arm-deploy@v2
with:
subscriptionId: ${{ secrets.azure-subscription-id }}
resourceGroupName: ${{ inputs.rg }}
template: ./bicep/main.bicep
failOnStdErr: false
parameters: 'uniqueName=${{ inputs.uniquelabel }}'
failOnStdErr: true
parameters: parameters.json
deploymentMode: Incremental

# Web App code
- name: bicep-output-webappname
Expand Down Expand Up @@ -104,3 +122,8 @@ jobs:
with:
app-name: ${{ env.FunctionAppName }}
package: './src/Workflow/output'

# Capture and log the Bicep outputs
- name: output-unique-name
run: |
echo "uniqueName: ${{ steps.bicep-deploy.outputs.uniqueName }}"
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -249,3 +249,4 @@ Src/Workflow/__queuestorage*
src/Workflow/local.settings.json
src/Web/appsettings.Development.json
/src/.idea
src/DataSync/appsettings.development.json
25 changes: 16 additions & 9 deletions bicep/main.bicep
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
@description('The name of the Azure Function app.')
param uniqueName string = toLower(uniqueString('${resourceGroup().id}'))
@description('Github PAT.')
param githubPat string

@description('A unique name for all resources.')
param uniqueLabel string

@description('Location for all resources.')
param location string = resourceGroup().location
param location string = resourceGroup().location // Nothing is being passed so this will use the default

//STORAGE ACCOUNT
var storageAccountName = toLower('storage${uniqueName}')
var storageAccountName = toLower('storage${uniqueLabel}')
resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' = {
name: storageAccountName
location: location
Expand Down Expand Up @@ -54,7 +57,7 @@ resource storageAccountTableServiceShortcutsTable 'Microsoft.Storage/storageAcco
}

//APP INSIGHTS
var appInsightsName = toLower('appinisghts-${uniqueName}')
var appInsightsName = toLower('appinisghts-${uniqueLabel}')
resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = {
name: appInsightsName
location: location
Expand All @@ -67,15 +70,15 @@ resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = {

//APP SERVICE PLAN for FUNCTION APP
resource functionAppServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = {
name: toLower('functionapp-service-${uniqueName}')
name: toLower('functionapp-service-${uniqueLabel}')
location: location
sku: { tier: 'Dynamic', name: 'Y1', family: 'Y', capacity: 1 }
properties: { reserved: true }
}

//FUNCTION APP
resource functionApp 'Microsoft.Web/sites@2022-03-01' = {
name: toLower('functionapp-${uniqueName}')
name: toLower('functionapp-${uniqueLabel}')
location: location
kind: 'functionapp,linux'
properties: {
Expand Down Expand Up @@ -125,6 +128,10 @@ resource functionApp 'Microsoft.Web/sites@2022-03-01' = {
name: 'StorageConfiguration__ConnectionString'
value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccountName};EndpointSuffix=${environment().suffixes.storage};AccountKey=${listKeys(storageAccount.id, '2019-06-01').keys[0].value}'
}
{
name: 'GithubConfiguration__Pat'
value: githubPat
}
]
}
}
Expand All @@ -133,7 +140,7 @@ output functionAppName string = functionApp.name

//APP SERVICE PLAN for WEB APP
resource webAppServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = {
name: 'webapp-service-${uniqueName}'
name: toLower('webapp-service-${uniqueLabel}')
location: location
sku: {
name: 'B1'
Expand All @@ -144,7 +151,7 @@ resource webAppServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = {

//WEB APP
resource webApp 'Microsoft.Web/sites@2022-03-01' = {
name: toLower('webapp-${uniqueName}')
name: toLower('webapp-${uniqueLabel}')
location: location
kind: 'app,linux'
properties: {
Expand Down
29 changes: 29 additions & 0 deletions src/DataSync/DataSync.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="AutoFixture" Version="4.18.1" />
<PackageReference Include="AutoFixture.AutoMoq" Version="4.18.1" />
<ProjectReference Include="..\Domain\Domain.csproj" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
</ItemGroup>

<ItemGroup>
<None Update="appsettings.development.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
141 changes: 141 additions & 0 deletions src/DataSync/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
using System.Net.Http.Headers;
using System.Runtime.CompilerServices;
using Domain.Models;
using System.Text;
using System.Text.Json;
using AutoFixture;
using AutoFixture.AutoMoq;
using Microsoft.Extensions.Configuration;

namespace DataSync;

class Program
{
static async Task Main(string[] args)
{
// Setup configuration
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory()) // Set the base path
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) // Load default appsettings.json
.AddJsonFile("appsettings.development.json", optional: true, reloadOnChange: true) // Load development-specific settings
.AddEnvironmentVariables(); // Optionally add environment variables
IConfiguration configuration = builder.Build();
var functionUrl = configuration["FunctionUrl"]!;
var gitHubPat = configuration["GitHubPAT"]!;

// Get GH blogs
var files = await GetGithubFiles("martinkearn", "Content", "Blogs", gitHubPat); // These values ARE case senitive
Console.WriteLine($"Got {files.Count} files from GitHub");
foreach (var file in files)
{
Console.WriteLine($"Processing File: {file.Path}");

// Get Commit
var commit = await GetGithubLastCommit("martinkearn", "Content", file.Path, gitHubPat);

// Create Fixture
var fixture = CreateFixture($"Updated {file.Path}", commit.Url, file.Path);

// Send to Function
if (functionUrl != null) await CallFunction(functionUrl, fixture);

await Task.Delay(10000); // Pause for 10 seconds

Console.WriteLine($"Processed File: {file.Path}");

//Console.WriteLine("Press any key to continue...");
//Console.ReadKey(); // Waits for the user to press any key

Console.WriteLine("");

}

Console.WriteLine("COMPLETED");
}


private static GithubPushWebhookPayload CreateFixture(string message, string commitUrl, string modifiedPath)
{
var fixture = new Fixture().Customize(new AutoMoqCustomization());
var ghWh = fixture.Create<GithubPushWebhookPayload>();
ghWh.Repository.Name = "Content";
ghWh.HeadCommit.Message = message;
ghWh.HeadCommit.Url = commitUrl;
ghWh.HeadCommit.Timestamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:sszzz");
ghWh.HeadCommit.Author = new Author()
{
Name = "Martin Kearn",
Email = "martin.kearn@microsoft.com",
Username = "martinkearn"
};
ghWh.HeadCommit.Added = [];
ghWh.HeadCommit.Removed = [];
ghWh.HeadCommit.Modified = [modifiedPath];
var commit = new Commit()
{
Id = ghWh.HeadCommit.Id,
TreeId = ghWh.HeadCommit.TreeId,
Distinct = ghWh.HeadCommit.Distinct,
Message = ghWh.HeadCommit.Message,
Timestamp = Convert.ToDateTime(ghWh.HeadCommit.Timestamp),
Url = ghWh.HeadCommit.Url,
Author = ghWh.HeadCommit.Author,
Committer = ghWh.HeadCommit.Committer,
Added = [],
Removed = [],
Modified = ghWh.HeadCommit.Modified
};
ghWh.Commits =
[
commit
];

return ghWh;
}

private static async Task<List<GithubFile>> GetGithubFiles(string repoOwner, string repoName, string folderPath, string pat)
{
var url = $"https://api.github.com/repos/{repoOwner}/{repoName}/contents/{folderPath}";
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", pat);
client.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (compatible; dotnet)"); //(GitHub requires this)
var response = await client.GetAsync(url);
response.EnsureSuccessStatusCode();
var responseBody = await response.Content.ReadAsStringAsync();
var files = JsonSerializer.Deserialize<GithubFile[]>(responseBody);

return files!.ToList();
}

private static async Task<Commit> GetGithubLastCommit(string repoOwner, string repoName, string filePath, string pat)
{
var url = $"https://api.github.com/repos/{repoOwner}/{repoName}/commits?path={filePath}&sha=master";
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", pat);
client.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (compatible; dotnet)"); //(GitHub requires this)
var response = await client.GetAsync(url);
response.EnsureSuccessStatusCode();
var responseBody = await response.Content.ReadAsStringAsync();
var commits = JsonSerializer.Deserialize<Commit[]>(responseBody);

return commits.FirstOrDefault();
}

private static async Task CallFunction(string functionUrl, GithubPushWebhookPayload data)
{
using var client = new HttpClient();
var jsonData = JsonSerializer.Serialize(data);
var content = new StringContent(jsonData, Encoding.UTF8, "application/json");
var response = await client.PostAsync(functionUrl, content);
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadAsStringAsync();
Console.WriteLine("Response received successfully:");
Console.WriteLine(result);
}
else
{
Console.WriteLine($"Failed to send POST request. Status Code: {response.StatusCode}");
}
}
}
4 changes: 4 additions & 0 deletions src/DataSync/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"FunctionUrl": "",
"GitHubPAT": ""
}
1 change: 0 additions & 1 deletion src/Domain/Domain.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,5 @@
</ItemGroup>
<ItemGroup>
<Folder Include="Interfaces\" />
<Folder Include="Models\" />
</ItemGroup>
</Project>
13 changes: 13 additions & 0 deletions src/Domain/Models/GithubConfiguration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace Domain.Models
{
/// <summary>
/// Used to strongly type the "GithubConfiguration" appsettings section
/// </summary>
public class GithubConfiguration
{
/// <summary>
/// PAT for acessing API.
/// </summary>
public string Pat { get; set; }
}
}
18 changes: 18 additions & 0 deletions src/Domain/Models/GithubFile.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System.Text.Json.Serialization;

namespace Domain.Models;

public class GithubFile
{
[JsonPropertyName("name")]
public string Name { get; set; }

[JsonPropertyName("path")]
public string Path { get; set; }

[JsonPropertyName("type")]
public string Type { get; set; }

[JsonPropertyName("download_url")]
public string DownloadUrl { get; set; }
}
Loading

0 comments on commit e7468f4

Please sign in to comment.