diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 258b06c..d39e9aa 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -11,96 +11,7 @@ on:
- 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:
- 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
build:
runs-on: ubuntu-latest
steps:
@@ -109,7 +20,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
@@ -208,7 +119,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/.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/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..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,106 +12,14 @@ internal class Program
{
static void Main(string[] args)
{
- string branchName = "main";
- 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", "synchronize", "reopened", "closed" },
- Branches = new string[] { branchName }
- }
- },
-
- EnvironmentVariables = new Dictionary
- {
- { "IS_RELEASE_CANDIDATE", EnvironmentVariables.IsGitHubReleaseCandidate() }
- },
-
- Jobs = new Dictionary
- {
- {
- "label",
- new LabelJobV2(runsOn: BuildMachines.UbuntuLatest)
- },
- {
- "build",
- new Job
- {
- RunsOn = BuildMachines.UbuntuLatest,
-
- Steps = new List
- {
- new CheckoutTaskV3
- {
- Name = "Check out"
- },
-
- new SetupDotNetTaskV3
- {
- Name = "Setup .Net",
-
- With = new TargetDotNetVersionV3
- {
- DotNetVersion = "7.0.201"
- }
- },
-
- 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 PublishJob(
- runsOn: BuildMachines.UbuntuLatest,
- dependsOn: "add_tag",
- nugetApiKey: "${{ secrets.NUGET_ACCESS }}")
- }
- }
- };
-
- 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);
+ }
+ }
+}
diff --git a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/RequireIssueOrTaskJob.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/RequireIssueOrTaskJob.cs
new file mode 100644
index 0000000..cbbbab9
--- /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_info.outputs.prOwner != 'dependabot[bot]' }}",
+ Id = "check_for_issues_or_tasks",
+ Shell = "bash",
+ Run =
+ """
+ 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
+ """,
+ },
+ };
+ }
+
+ [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; }
+ }
+}
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