From 0bc45e203e7fcea589f05f37b6502c4e52a138f4 Mon Sep 17 00:00:00 2001 From: Christo du Toit Date: Thu, 19 Dec 2024 15:35:12 +0000 Subject: [PATCH 1/6] MINOR FOUNDATIONS: Require Issue Or Task --- .github/workflows/build.yml | 51 ++++++- .../ADotNet.Infrastructure.Build.csproj | 2 +- ADotNet.Infrastructure.Build/Program.cs | 12 +- .../DotNets/RequireIssueOrTaskJob.cs | 131 ++++++++++++++++++ 4 files changed, 193 insertions(+), 3 deletions(-) create mode 100644 ADotNet/Models/Pipelines/GithubPipelines/DotNets/RequireIssueOrTaskJob.cs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 258b06c..3e57570 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,6 +28,7 @@ env: }} jobs: label: + name: Label runs-on: ubuntu-latest steps: - name: Apply Label @@ -101,6 +102,54 @@ jobs: permissions: contents: read pull-requests: write + requireIssueOrTask: + name: Require Issue Or Task Association + runs-on: ubuntu-latest + steps: + - name: Check out + uses: actions/checkout@v3 + - name: Get PR Information + id: get_pr_info + uses: actions/github-script@v6 + with: + script: >2- + const pr = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.payload.pull_request.number + }); + + const prOwner = pr.data.user.login || ""; + const prBody = pr.data.body || ""; + core.setOutput("prOwner", prOwner); + core.setOutput("description", prBody); + console.log(`PR Owner: ${prOwner}`); + console.log(`PR Body: ${prBody}`); + - name: Check For Associated Issues Or Tasks + id: check_for_issues_or_tasks + if: ${{ steps.get-pr.outputs.prOwner != 'dependabot[bot]' }} + run: >2- + PR_BODY="${{ steps.get-pr.outputs.description }}" + echo "::notice::Raw PR Body: $PR_BODY" + + if [[ -z "$PR_BODY" ]]; then + echo "Error: PR description does not contain any links to issue(s)/task(s) (e.g., 'closes #123' / 'closes AB#123' / 'fixes #123' / 'fixes AB#123')." + exit 1 + fi + + PR_BODY=$(echo "$PR_BODY" | tr -s '\r\n' ' ' | tr '\n' ' ' | xargs) + echo "::notice::Normalized PR Body: $PR_BODY" + + if echo "$PR_BODY" | grep -Piq "((close|closes|closed|fix|fixes|fixed|resolve|resolves|resolved)\s*(\[#\d+\]|\#\d+)|(?:close|closes|closed|fix|fixes|fixed|resolve|resolves|resolved)\s*(\[AB#\d+\]|AB#\d+))"; then + echo "Valid PR description." + else + echo "Error: PR description does not contain any links to issue(s)/task(s) (e.g., 'closes #123' / 'closes AB#123' / 'fixes #123' / 'fixes AB#123')." + exit 1 + fi + shell: bash + permissions: + contents: read + pull-requests: read build: runs-on: ubuntu-latest steps: @@ -109,7 +158,7 @@ jobs: - name: Setup .Net uses: actions/setup-dotnet@v3 with: - dotnet-version: 7.0.201 + dotnet-version: 9.0.100 - name: Restore run: dotnet restore - name: Build diff --git a/ADotNet.Infrastructure.Build/ADotNet.Infrastructure.Build.csproj b/ADotNet.Infrastructure.Build/ADotNet.Infrastructure.Build.csproj index 8a6f39b..af7ae87 100644 --- a/ADotNet.Infrastructure.Build/ADotNet.Infrastructure.Build.csproj +++ b/ADotNet.Infrastructure.Build/ADotNet.Infrastructure.Build.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net9.0 false diff --git a/ADotNet.Infrastructure.Build/Program.cs b/ADotNet.Infrastructure.Build/Program.cs index b800cf7..0fd59c0 100644 --- a/ADotNet.Infrastructure.Build/Program.cs +++ b/ADotNet.Infrastructure.Build/Program.cs @@ -48,6 +48,16 @@ static void Main(string[] args) { "label", new LabelJobV2(runsOn: BuildMachines.UbuntuLatest) + { + Name = "Label", + } + }, + { + "requireIssueOrTask", + new RequireIssueOrTaskJob() + { + Name = "Require Issue Or Task Association", + } }, { "build", @@ -68,7 +78,7 @@ static void Main(string[] args) With = new TargetDotNetVersionV3 { - DotNetVersion = "7.0.201" + DotNetVersion = "9.0.100" } }, diff --git a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/RequireIssueOrTaskJob.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/RequireIssueOrTaskJob.cs new file mode 100644 index 0000000..62e46b4 --- /dev/null +++ b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/RequireIssueOrTaskJob.cs @@ -0,0 +1,131 @@ +// --------------------------------------------------------------------------- +// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// Licensed under the MIT License. +// See License.txt in the project root for license information. +// --------------------------------------------------------------------------- + +using System.Collections.Generic; +using System.ComponentModel; +using ADotNet.Models.Pipelines.GithubPipelines.DotNets.Tasks; +using YamlDotNet.Serialization; + +namespace ADotNet.Models.Pipelines.GithubPipelines.DotNets +{ + public sealed class RequireIssueOrTaskJob : Job + { + public RequireIssueOrTaskJob() + { + RunsOn = "ubuntu-latest"; + + Permissions = new Dictionary + { + { "contents", "read" }, + { "pull-requests", "read" } + }; + + Steps = new List + { + new CheckoutTaskV3 + { + Name = "Check out" + }, + + new GithubTask() + { + Name = "Get PR Information", + Id = "get_pr_info", + Uses = "actions/github-script@v6", + With = new Dictionary + { + { + "script", + """ + const pr = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.payload.pull_request.number + }); + + const prOwner = pr.data.user.login || ""; + const prBody = pr.data.body || ""; + core.setOutput("prOwner", prOwner); + core.setOutput("description", prBody); + console.log(`PR Owner: ${prOwner}`); + console.log(`PR Body: ${prBody}`); + """ + } + } + }, + + new GithubTask() + { + Name = "Check For Associated Issues Or Tasks", + If = "${{ steps.get-pr.outputs.prOwner != 'dependabot[bot]' }}", + Id = "check_for_issues_or_tasks", + Shell = "bash", + Run = + """ + PR_BODY="${{ steps.get-pr.outputs.description }}" + echo "::notice::Raw PR Body: $PR_BODY" + + if [[ -z "$PR_BODY" ]]; then + echo "Error: PR description does not contain any links to issue(s)/task(s) (e.g., 'closes #123' / 'closes AB#123' / 'fixes #123' / 'fixes AB#123')." + exit 1 + fi + + PR_BODY=$(echo "$PR_BODY" | tr -s '\r\n' ' ' | tr '\n' ' ' | xargs) + echo "::notice::Normalized PR Body: $PR_BODY" + + if echo "$PR_BODY" | grep -Piq "((close|closes|closed|fix|fixes|fixed|resolve|resolves|resolved)\s*(\[#\d+\]|\#\d+)|(?:close|closes|closed|fix|fixes|fixed|resolve|resolves|resolved)\s*(\[AB#\d+\]|AB#\d+))"; then + echo "Valid PR description." + else + echo "Error: PR description does not contain any links to issue(s)/task(s) (e.g., 'closes #123' / 'closes AB#123' / 'fixes #123' / 'fixes AB#123')." + exit 1 + fi + """, + }, + }; + } + + [YamlMember(Order = 0, DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public new string Name { get; set; } + + [YamlMember(Order = 1, Alias = "runs-on")] + public new string RunsOn { get; set; } + + [YamlMember(Order = 2, Alias = "needs", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public new string[] Needs { get; set; } + + [YamlMember(Order = 3, Alias = "if", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public new string If { get; set; } + + [YamlMember(Order = 4, DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public new string Environment { get; set; } + + [YamlMember(Order = 5, DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public new DefaultValues Defaults { get; set; } + + [YamlMember(Order = 6)] + public new List Steps { get; set; } + + [DefaultValue(0)] + [YamlMember(Order = 7, Alias = "timeout-minutes", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public new int TimeoutInMinutes { get; set; } + + [YamlMember(Order = 8, DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public new Strategy Strategy { get; set; } + + [YamlMember(Order = 9, Alias = "env", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public new Dictionary EnvironmentVariables { get; set; } + + [YamlMember(Order = 10, DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public new Dictionary Outputs { get; set; } + + [DefaultValue(false)] + [YamlMember(Order = 11, Alias = "continue-on-error", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public new bool ContinueOnError { get; set; } + + [YamlMember(Order = 12, Alias = "permissions", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public new Dictionary Permissions { get; set; } + } +} From 85458191ea66324bdea3624766a1c8f8565aeb7c Mon Sep 17 00:00:00 2001 From: Christo du Toit Date: Thu, 19 Dec 2024 15:39:36 +0000 Subject: [PATCH 2/6] CODE RUB: Updated Target Version to .net 9.0 --- AdoNet.Tests.Console/ADotNet.Tests.Console.csproj | 2 +- AdoNet.Tests.Unit/ADotNet.Tests.Unit.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AdoNet.Tests.Console/ADotNet.Tests.Console.csproj b/AdoNet.Tests.Console/ADotNet.Tests.Console.csproj index 3b8babe..2880879 100644 --- a/AdoNet.Tests.Console/ADotNet.Tests.Console.csproj +++ b/AdoNet.Tests.Console/ADotNet.Tests.Console.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net9.0 false diff --git a/AdoNet.Tests.Unit/ADotNet.Tests.Unit.csproj b/AdoNet.Tests.Unit/ADotNet.Tests.Unit.csproj index d6a86a4..9d704dd 100644 --- a/AdoNet.Tests.Unit/ADotNet.Tests.Unit.csproj +++ b/AdoNet.Tests.Unit/ADotNet.Tests.Unit.csproj @@ -1,7 +1,7 @@ - net7.0 + net9.0 false From a7ee82d627e0884c1a6fac286c0b3f0ddedcd3a4 Mon Sep 17 00:00:00 2001 From: Christo du Toit Date: Thu, 19 Dec 2024 18:22:39 +0000 Subject: [PATCH 3/6] CODE RUB: Updated build script to include edit --- .github/workflows/build.yml | 1 + ADotNet.Infrastructure.Build/Program.cs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3e57570..514d979 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,6 +6,7 @@ on: pull_request: types: - opened + - edited - synchronize - reopened - closed diff --git a/ADotNet.Infrastructure.Build/Program.cs b/ADotNet.Infrastructure.Build/Program.cs index 0fd59c0..1182d7c 100644 --- a/ADotNet.Infrastructure.Build/Program.cs +++ b/ADotNet.Infrastructure.Build/Program.cs @@ -33,7 +33,7 @@ static void Main(string[] args) PullRequest = new PullRequestEvent { - Types = new string[] { "opened", "synchronize", "reopened", "closed" }, + Types = new string[] { "opened", "edited", "synchronize", "reopened", "closed" }, Branches = new string[] { branchName } } }, @@ -52,7 +52,7 @@ static void Main(string[] args) Name = "Label", } }, - { + { "requireIssueOrTask", new RequireIssueOrTaskJob() { From c02582b98c0cd53b70bed406753e92f135c84922 Mon Sep 17 00:00:00 2001 From: Christo du Toit Date: Thu, 19 Dec 2024 18:29:47 +0000 Subject: [PATCH 4/6] CODE RUB: Updated script --- .github/workflows/build.yml | 4 ++-- .../GithubPipelines/DotNets/RequireIssueOrTaskJob.cs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 514d979..2c4ab9f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -128,9 +128,9 @@ jobs: console.log(`PR Body: ${prBody}`); - name: Check For Associated Issues Or Tasks id: check_for_issues_or_tasks - if: ${{ steps.get-pr.outputs.prOwner != 'dependabot[bot]' }} + if: ${{ steps.get_pr_info.outputs.prOwner != 'dependabot[bot]' }} run: >2- - PR_BODY="${{ steps.get-pr.outputs.description }}" + PR_BODY="${{ steps.get_pr_info.outputs.description }}" echo "::notice::Raw PR Body: $PR_BODY" if [[ -z "$PR_BODY" ]]; then diff --git a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/RequireIssueOrTaskJob.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/RequireIssueOrTaskJob.cs index 62e46b4..cbbbab9 100644 --- a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/RequireIssueOrTaskJob.cs +++ b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/RequireIssueOrTaskJob.cs @@ -37,8 +37,8 @@ public RequireIssueOrTaskJob() Uses = "actions/github-script@v6", With = new Dictionary { - { - "script", + { + "script", """ const pr = await github.rest.pulls.get({ owner: context.repo.owner, @@ -60,12 +60,12 @@ public RequireIssueOrTaskJob() new GithubTask() { Name = "Check For Associated Issues Or Tasks", - If = "${{ steps.get-pr.outputs.prOwner != 'dependabot[bot]' }}", + If = "${{ steps.get_pr_info.outputs.prOwner != 'dependabot[bot]' }}", Id = "check_for_issues_or_tasks", Shell = "bash", Run = """ - PR_BODY="${{ steps.get-pr.outputs.description }}" + PR_BODY="${{ steps.get_pr_info.outputs.description }}" echo "::notice::Raw PR Body: $PR_BODY" if [[ -z "$PR_BODY" ]]; then From 77ee784b66e010f4645713969fd640f1d28e5f82 Mon Sep 17 00:00:00 2001 From: Christo du Toit Date: Thu, 19 Dec 2024 18:37:00 +0000 Subject: [PATCH 5/6] CODE RUB: Code cleanup --- .github/workflows/build.yml | 5 ++++- ADotNet.Infrastructure.Build/Program.cs | 13 +++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2c4ab9f..7935870 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -152,6 +152,7 @@ jobs: contents: read pull-requests: read build: + name: Build runs-on: ubuntu-latest steps: - name: Check out @@ -167,6 +168,7 @@ jobs: - name: Test run: dotnet test --no-build --verbosity normal add_tag: + name: Tag And Release runs-on: ubuntu-latest needs: - build @@ -248,6 +250,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.PAT_FOR_TAGGING }} publish: + name: Publish NuGet Package runs-on: ubuntu-latest needs: - add_tag @@ -258,7 +261,7 @@ jobs: - name: Setup .Net uses: actions/setup-dotnet@v3 with: - dotnet-version: 7.0.201 + dotnet-version: 9.0.100 - name: Restore run: dotnet restore - name: Build diff --git a/ADotNet.Infrastructure.Build/Program.cs b/ADotNet.Infrastructure.Build/Program.cs index 1182d7c..c7d4dc5 100644 --- a/ADotNet.Infrastructure.Build/Program.cs +++ b/ADotNet.Infrastructure.Build/Program.cs @@ -18,6 +18,7 @@ internal class Program static void Main(string[] args) { string branchName = "main"; + string dotNetVersion = "9.0.100"; var aDotNetClient = new ADotNetClient(); var githubPipeline = new GithubPipeline @@ -63,6 +64,7 @@ static void Main(string[] args) "build", new Job { + Name = "Build", RunsOn = BuildMachines.UbuntuLatest, Steps = new List @@ -78,7 +80,7 @@ static void Main(string[] args) With = new TargetDotNetVersionV3 { - DotNetVersion = "9.0.100" + DotNetVersion = dotNetVersion } }, @@ -107,13 +109,20 @@ static void Main(string[] args) projectRelativePath: "ADotNet/ADotNet.csproj", githubToken: "${{ secrets.PAT_FOR_TAGGING }}", branchName: branchName) + { + Name = "Tag And Release" + } }, { "publish", - new PublishJob( + new PublishJobV2( runsOn: BuildMachines.UbuntuLatest, dependsOn: "add_tag", + dotNetVersion: dotNetVersion, nugetApiKey: "${{ secrets.NUGET_ACCESS }}") + { + Name = "Publish NuGet Package" + } } } }; From 88c34ea504ab25a95fdd0a2d5e37a97d9a1084b1 Mon Sep 17 00:00:00 2001 From: Christo du Toit Date: Thu, 19 Dec 2024 19:08:39 +0000 Subject: [PATCH 6/6] CODE RUB: Code cleanup --- .github/workflows/build.yml | 142 --------------- .github/workflows/prLinter.yml | 138 +++++++++++++++ ADotNet.Infrastructure.Build/Program.cs | 130 +------------- .../Services/ScriptGenerationService.cs | 164 ++++++++++++++++++ 4 files changed, 309 insertions(+), 265 deletions(-) create mode 100644 .github/workflows/prLinter.yml create mode 100644 ADotNet.Infrastructure.Build/Services/ScriptGenerationService.cs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7935870..d39e9aa 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,153 +6,13 @@ on: pull_request: types: - opened - - edited - synchronize - reopened - closed branches: - main -env: - IS_RELEASE_CANDIDATE: >- - ${{ - ( - github.event_name == 'pull_request' && - startsWith(github.event.pull_request.title, 'RELEASES:') && - contains(github.event.pull_request.labels.*.name, 'RELEASES') - ) - || - ( - github.event_name == 'push' && - startsWith(github.event.head_commit.message, 'RELEASES:') && - startsWith(github.ref_name, 'RELEASE') - ) - }} jobs: - label: - name: Label - runs-on: ubuntu-latest - steps: - - name: Apply Label - uses: actions/github-script@v6 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: >- - const prefixes = [ - 'INFRA:', - 'PROVISIONS:', - 'RELEASES:', - 'DATA:', - 'BROKERS:', - 'FOUNDATIONS:', - 'PROCESSINGS:', - 'ORCHESTRATIONS:', - 'COORDINATIONS:', - 'MANAGEMENTS:', - 'AGGREGATIONS:', - 'CONTROLLERS:', - 'CLIENTS:', - 'EXPOSERS:', - 'PROVIDERS:', - 'BASE:', - 'COMPONENTS:', - 'VIEWS:', - 'PAGES:', - 'ACCEPTANCE:', - 'INTEGRATIONS:', - 'CODE RUB:', - 'MINOR FIX:', - 'MEDIUM FIX:', - 'MAJOR FIX:', - 'DOCUMENTATION:', - 'CONFIG:', - 'STANDARD:', - 'DESIGN:', - 'BUSINESS:' - ]; - - - const pullRequest = context.payload.pull_request; - - - if (!pullRequest) { - console.log('No pull request context available.'); - return; - } - - - const title = context.payload.pull_request.title; - - const existingLabels = context.payload.pull_request.labels.map(label => label.name); - - - for (const prefix of prefixes) { - if (title.startsWith(prefix)) { - const label = prefix.slice(0, -1); - if (!existingLabels.includes(label)) { - console.log(`Applying label: ${label}`); - await github.rest.issues.addLabels({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.payload.pull_request.number, - labels: [label] - }); - } - break; - } - } - permissions: - contents: read - pull-requests: write - requireIssueOrTask: - name: Require Issue Or Task Association - runs-on: ubuntu-latest - steps: - - name: Check out - uses: actions/checkout@v3 - - name: Get PR Information - id: get_pr_info - uses: actions/github-script@v6 - with: - script: >2- - const pr = await github.rest.pulls.get({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: context.payload.pull_request.number - }); - - const prOwner = pr.data.user.login || ""; - const prBody = pr.data.body || ""; - core.setOutput("prOwner", prOwner); - core.setOutput("description", prBody); - console.log(`PR Owner: ${prOwner}`); - console.log(`PR Body: ${prBody}`); - - name: Check For Associated Issues Or Tasks - id: check_for_issues_or_tasks - if: ${{ steps.get_pr_info.outputs.prOwner != 'dependabot[bot]' }} - run: >2- - PR_BODY="${{ steps.get_pr_info.outputs.description }}" - echo "::notice::Raw PR Body: $PR_BODY" - - if [[ -z "$PR_BODY" ]]; then - echo "Error: PR description does not contain any links to issue(s)/task(s) (e.g., 'closes #123' / 'closes AB#123' / 'fixes #123' / 'fixes AB#123')." - exit 1 - fi - - PR_BODY=$(echo "$PR_BODY" | tr -s '\r\n' ' ' | tr '\n' ' ' | xargs) - echo "::notice::Normalized PR Body: $PR_BODY" - - if echo "$PR_BODY" | grep -Piq "((close|closes|closed|fix|fixes|fixed|resolve|resolves|resolved)\s*(\[#\d+\]|\#\d+)|(?:close|closes|closed|fix|fixes|fixed|resolve|resolves|resolved)\s*(\[AB#\d+\]|AB#\d+))"; then - echo "Valid PR description." - else - echo "Error: PR description does not contain any links to issue(s)/task(s) (e.g., 'closes #123' / 'closes AB#123' / 'fixes #123' / 'fixes AB#123')." - exit 1 - fi - shell: bash - permissions: - contents: read - pull-requests: read build: - name: Build runs-on: ubuntu-latest steps: - name: Check out @@ -168,7 +28,6 @@ jobs: - name: Test run: dotnet test --no-build --verbosity normal add_tag: - name: Tag And Release runs-on: ubuntu-latest needs: - build @@ -250,7 +109,6 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.PAT_FOR_TAGGING }} publish: - name: Publish NuGet Package runs-on: ubuntu-latest needs: - add_tag diff --git a/.github/workflows/prLinter.yml b/.github/workflows/prLinter.yml new file mode 100644 index 0000000..31d7538 --- /dev/null +++ b/.github/workflows/prLinter.yml @@ -0,0 +1,138 @@ +name: PR Linter +on: + push: + branches: + - main + pull_request: + types: + - opened + - edited + - synchronize + - reopened + - closed + branches: + - main +jobs: + label: + name: Label + runs-on: ubuntu-latest + steps: + - name: Apply Label + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: >- + const prefixes = [ + 'INFRA:', + 'PROVISIONS:', + 'RELEASES:', + 'DATA:', + 'BROKERS:', + 'FOUNDATIONS:', + 'PROCESSINGS:', + 'ORCHESTRATIONS:', + 'COORDINATIONS:', + 'MANAGEMENTS:', + 'AGGREGATIONS:', + 'CONTROLLERS:', + 'CLIENTS:', + 'EXPOSERS:', + 'PROVIDERS:', + 'BASE:', + 'COMPONENTS:', + 'VIEWS:', + 'PAGES:', + 'ACCEPTANCE:', + 'INTEGRATIONS:', + 'CODE RUB:', + 'MINOR FIX:', + 'MEDIUM FIX:', + 'MAJOR FIX:', + 'DOCUMENTATION:', + 'CONFIG:', + 'STANDARD:', + 'DESIGN:', + 'BUSINESS:' + ]; + + + const pullRequest = context.payload.pull_request; + + + if (!pullRequest) { + console.log('No pull request context available.'); + return; + } + + + const title = context.payload.pull_request.title; + + const existingLabels = context.payload.pull_request.labels.map(label => label.name); + + + for (const prefix of prefixes) { + if (title.startsWith(prefix)) { + const label = prefix.slice(0, -1); + if (!existingLabels.includes(label)) { + console.log(`Applying label: ${label}`); + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + labels: [label] + }); + } + break; + } + } + permissions: + contents: read + pull-requests: write + requireIssueOrTask: + name: Require Issue Or Task Association + runs-on: ubuntu-latest + steps: + - name: Check out + uses: actions/checkout@v3 + - name: Get PR Information + id: get_pr_info + uses: actions/github-script@v6 + with: + script: >2- + const pr = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.payload.pull_request.number + }); + + const prOwner = pr.data.user.login || ""; + const prBody = pr.data.body || ""; + core.setOutput("prOwner", prOwner); + core.setOutput("description", prBody); + console.log(`PR Owner: ${prOwner}`); + console.log(`PR Body: ${prBody}`); + - name: Check For Associated Issues Or Tasks + id: check_for_issues_or_tasks + if: ${{ steps.get_pr_info.outputs.prOwner != 'dependabot[bot]' }} + run: >2- + PR_BODY="${{ steps.get_pr_info.outputs.description }}" + echo "::notice::Raw PR Body: $PR_BODY" + + if [[ -z "$PR_BODY" ]]; then + echo "Error: PR description does not contain any links to issue(s)/task(s) (e.g., 'closes #123' / 'closes AB#123' / 'fixes #123' / 'fixes AB#123')." + exit 1 + fi + + PR_BODY=$(echo "$PR_BODY" | tr -s '\r\n' ' ' | tr '\n' ' ' | xargs) + echo "::notice::Normalized PR Body: $PR_BODY" + + if echo "$PR_BODY" | grep -Piq "((close|closes|closed|fix|fixes|fixed|resolve|resolves|resolved)\s*(\[#\d+\]|\#\d+)|(?:close|closes|closed|fix|fixes|fixed|resolve|resolves|resolved)\s*(\[AB#\d+\]|AB#\d+))"; then + echo "Valid PR description." + else + echo "Error: PR description does not contain any links to issue(s)/task(s) (e.g., 'closes #123' / 'closes AB#123' / 'fixes #123' / 'fixes AB#123')." + exit 1 + fi + shell: bash + permissions: + contents: read + pull-requests: read diff --git a/ADotNet.Infrastructure.Build/Program.cs b/ADotNet.Infrastructure.Build/Program.cs index c7d4dc5..dc56517 100644 --- a/ADotNet.Infrastructure.Build/Program.cs +++ b/ADotNet.Infrastructure.Build/Program.cs @@ -4,12 +4,7 @@ // See License.txt in the project root for license information. // --------------------------------------------------------------------------- -using System.Collections.Generic; -using System.IO; -using ADotNet.Clients; -using ADotNet.Models.Pipelines.GithubPipelines.DotNets; -using ADotNet.Models.Pipelines.GithubPipelines.DotNets.Tasks; -using ADotNet.Models.Pipelines.GithubPipelines.DotNets.Tasks.SetupDotNetTaskV3s; +using ISL.ReIdentification.Infrastructure.Services; namespace ADotNet.Infrastructure.Build { @@ -17,125 +12,14 @@ internal class Program { static void Main(string[] args) { - string branchName = "main"; - string dotNetVersion = "9.0.100"; - var aDotNetClient = new ADotNetClient(); + var scriptGenerationService = new ScriptGenerationService(); - var githubPipeline = new GithubPipeline - { - Name = "Build", + scriptGenerationService.GenerateBuildScript( + branchName: "main", + projectName: "ADotNet", + dotNetVersion: "9.0.100"); - OnEvents = new Events - { - Push = new PushEvent - { - Branches = new string[] { branchName } - }, - - PullRequest = new PullRequestEvent - { - Types = new string[] { "opened", "edited", "synchronize", "reopened", "closed" }, - Branches = new string[] { branchName } - } - }, - - EnvironmentVariables = new Dictionary - { - { "IS_RELEASE_CANDIDATE", EnvironmentVariables.IsGitHubReleaseCandidate() } - }, - - Jobs = new Dictionary - { - { - "label", - new LabelJobV2(runsOn: BuildMachines.UbuntuLatest) - { - Name = "Label", - } - }, - { - "requireIssueOrTask", - new RequireIssueOrTaskJob() - { - Name = "Require Issue Or Task Association", - } - }, - { - "build", - new Job - { - Name = "Build", - RunsOn = BuildMachines.UbuntuLatest, - - Steps = new List - { - new CheckoutTaskV3 - { - Name = "Check out" - }, - - new SetupDotNetTaskV3 - { - Name = "Setup .Net", - - With = new TargetDotNetVersionV3 - { - DotNetVersion = dotNetVersion - } - }, - - new RestoreTask - { - Name = "Restore" - }, - - new DotNetBuildTask - { - Name = "Build" - }, - - new TestTask - { - Name = "Test" - } - } - } - }, - { - "add_tag", - new TagJob( - runsOn: BuildMachines.UbuntuLatest, - dependsOn: "build", - projectRelativePath: "ADotNet/ADotNet.csproj", - githubToken: "${{ secrets.PAT_FOR_TAGGING }}", - branchName: branchName) - { - Name = "Tag And Release" - } - }, - { - "publish", - new PublishJobV2( - runsOn: BuildMachines.UbuntuLatest, - dependsOn: "add_tag", - dotNetVersion: dotNetVersion, - nugetApiKey: "${{ secrets.NUGET_ACCESS }}") - { - Name = "Publish NuGet Package" - } - } - } - }; - - string buildScriptPath = "../../../../.github/workflows/build.yml"; - string directoryPath = Path.GetDirectoryName(buildScriptPath); - - if (!Directory.Exists(directoryPath)) - { - Directory.CreateDirectory(directoryPath); - } - - aDotNetClient.SerializeAndWriteToFile(githubPipeline, path: buildScriptPath); + scriptGenerationService.GeneratePrLintScript(branchName: "main"); } } } \ No newline at end of file diff --git a/ADotNet.Infrastructure.Build/Services/ScriptGenerationService.cs b/ADotNet.Infrastructure.Build/Services/ScriptGenerationService.cs new file mode 100644 index 0000000..753e94f --- /dev/null +++ b/ADotNet.Infrastructure.Build/Services/ScriptGenerationService.cs @@ -0,0 +1,164 @@ +// --------------------------------------------------------------------------- +// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// Licensed under the MIT License. +// See License.txt in the project root for license information. +// --------------------------------------------------------------------------- + +using System.Collections.Generic; +using System.IO; +using ADotNet.Clients; +using ADotNet.Models.Pipelines.GithubPipelines.DotNets; +using ADotNet.Models.Pipelines.GithubPipelines.DotNets.Tasks; +using ADotNet.Models.Pipelines.GithubPipelines.DotNets.Tasks.SetupDotNetTaskV3s; + +namespace ISL.ReIdentification.Infrastructure.Services +{ + internal class ScriptGenerationService + { + private readonly ADotNetClient adotNetClient; + + public ScriptGenerationService() => + adotNetClient = new ADotNetClient(); + + public void GenerateBuildScript(string branchName, string projectName, string dotNetVersion) + { + var githubPipeline = new GithubPipeline + { + Name = "Build", + + OnEvents = new Events + { + Push = new PushEvent { Branches = [branchName] }, + + PullRequest = new PullRequestEvent + { + Types = ["opened", "synchronize", "reopened", "closed"], + Branches = [branchName] + } + }, + + Jobs = new Dictionary + { + { + "build", + new Job + { + RunsOn = BuildMachines.UbuntuLatest, + + Steps = new List + { + new CheckoutTaskV3 + { + Name = "Check out" + }, + + new SetupDotNetTaskV3 + { + Name = "Setup .Net", + + With = new TargetDotNetVersionV3 + { + DotNetVersion = "9.0.100" + } + }, + + new RestoreTask + { + Name = "Restore" + }, + + new DotNetBuildTask + { + Name = "Build" + }, + + new TestTask + { + Name = "Test" + } + } + } + }, + { + "add_tag", + new TagJob( + runsOn: BuildMachines.UbuntuLatest, + dependsOn: "build", + projectRelativePath: "ADotNet/ADotNet.csproj", + githubToken: "${{ secrets.PAT_FOR_TAGGING }}", + branchName: branchName) + }, + { + "publish", + new PublishJobV2( + runsOn: BuildMachines.UbuntuLatest, + dependsOn: "add_tag", + dotNetVersion: dotNetVersion, + nugetApiKey: "${{ secrets.NUGET_ACCESS }}") + } + } + }; + + string buildScriptPath = "../../../../.github/workflows/build.yml"; + string directoryPath = Path.GetDirectoryName(buildScriptPath); + + if (!Directory.Exists(directoryPath)) + { + Directory.CreateDirectory(directoryPath); + } + + adotNetClient.SerializeAndWriteToFile( + adoPipeline: githubPipeline, + path: buildScriptPath); + } + + public void GeneratePrLintScript(string branchName) + { + var githubPipeline = new GithubPipeline + { + Name = "PR Linter", + + OnEvents = new Events + { + Push = new PushEvent { Branches = [branchName] }, + + PullRequest = new PullRequestEvent + { + Types = ["opened", "edited", "synchronize", "reopened", "closed"], + Branches = [branchName] + } + }, + + Jobs = new Dictionary + { + { + "label", + new LabelJobV2(runsOn: BuildMachines.UbuntuLatest) + { + Name = "Label", + } + }, + { + "requireIssueOrTask", + new RequireIssueOrTaskJob() + { + Name = "Require Issue Or Task Association", + } + }, + } + }; + + string buildScriptPath = "../../../../.github/workflows/prLinter.yml"; + string directoryPath = Path.GetDirectoryName(buildScriptPath); + + if (!Directory.Exists(directoryPath)) + { + Directory.CreateDirectory(directoryPath); + } + + adotNetClient.SerializeAndWriteToFile( + adoPipeline: githubPipeline, + path: buildScriptPath); + } + } +}