diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3c613e4..3ee8c1c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,9 +19,9 @@ jobs: - name: Check out uses: actions/checkout@v3 - name: Setup .Net - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: - dotnet-version: 9.0.100 + dotnet-version: 10.x - name: Restore run: dotnet restore - name: Build @@ -120,7 +120,7 @@ jobs: - name: Check out uses: actions/checkout@v3 - name: Setup .Net - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: 10.x - name: Restore diff --git a/.github/workflows/prLinter.yml b/.github/workflows/prLinter.yml index 95d87f1..ccf94ca 100644 --- a/.github/workflows/prLinter.yml +++ b/.github/workflows/prLinter.yml @@ -131,3 +131,41 @@ jobs: permissions: contents: read pull-requests: read + setAuthorAsPrAssignee: + name: Set Author As PR Assignee + runs-on: ubuntu-latest + steps: + - name: Set Author As PR Assignee + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: > + const pr = context.payload.pull_request; + + if (!pr) { + console.log('No pull request context available.'); + return; + } + + + const author = pr.user.login; + + if (author.endsWith('[bot]')) { + console.log(`Skipping bot author: ${author}`); + return; + } + + + console.log(`Assigning PR to author: ${author}`); + + + await github.rest.issues.addAssignees({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: pr.number, + assignees: [author] + }); + permissions: + contents: read + issues: write + pull-requests: write diff --git a/ADotNet.Infrastructure.Build/Services/ScriptGenerationService.cs b/ADotNet.Infrastructure.Build/Services/ScriptGenerationService.cs index 1ce917b..ebd1e92 100644 --- a/ADotNet.Infrastructure.Build/Services/ScriptGenerationService.cs +++ b/ADotNet.Infrastructure.Build/Services/ScriptGenerationService.cs @@ -9,7 +9,7 @@ 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 ADotNet.Models.Pipelines.GithubPipelines.DotNets.Tasks.SetupDotNetTaskV4s; namespace ADotNet.Infrastructure.Build.Services { @@ -53,11 +53,11 @@ public void GenerateBuildScript(string branchName, string projectName, string do Name = "Check out" }, - new SetupDotNetTaskV3 + new SetupDotNetTaskV4 { Name = "Setup .Net", - With = new TargetDotNetVersionV3 + With = new TargetDotNetVersionV4 { DotNetVersion = dotNetVersion } @@ -82,7 +82,7 @@ public void GenerateBuildScript(string branchName, string projectName, string do }, { "add_tag", - new TagJob( + new TagJobV1( runsOn: BuildMachines.UbuntuLatest, dependsOn: "build", projectRelativePath: "ADotNet/ADotNet.csproj", @@ -94,7 +94,7 @@ public void GenerateBuildScript(string branchName, string projectName, string do }, { "publish", - new PublishJobV2( + new PublishJobV3( runsOn: BuildMachines.UbuntuLatest, dependsOn: "add_tag", dotNetVersion: dotNetVersion, @@ -150,6 +150,13 @@ public void GeneratePrLintScript(string branchName) Name = "Require Issue Or Task Association", } }, + { + "setAuthorAsPrAssignee", + new SetAuthorAsPrAssigneeJob(runsOn: BuildMachines.UbuntuLatest) + { + Name = "Set Author As PR Assignee", + } + } } }; diff --git a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/PublishJobV3.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/PublishJobV3.cs new file mode 100644 index 0000000..6bb4a33 --- /dev/null +++ b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/PublishJobV3.cs @@ -0,0 +1,102 @@ +// --------------------------------------------------------------------------- +// 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 ADotNet.Models.Pipelines.GithubPipelines.DotNets.Tasks.SetupDotNetTaskV4s; +using YamlDotNet.Serialization; + +namespace ADotNet.Models.Pipelines.GithubPipelines.DotNets +{ + public class PublishJobV3 : Job + { + public PublishJobV3( + string runsOn, + string dependsOn, + string dotNetVersion, + string nugetApiKey) + { + RunsOn = runsOn; + Needs = new string[] { dependsOn }; + + If = $"needs.{dependsOn}.result == 'success'"; + + Steps = new List { + new CheckoutTaskV3 + { + Name = "Check out" + }, + + new SetupDotNetTaskV4 + { + Name = "Setup .Net", + + With = new TargetDotNetVersionV4 + { + DotNetVersion = dotNetVersion + } + }, + + new RestoreTask + { + Name = "Restore" + }, + + new DotNetBuildReleaseTask + { + Name = "Build", + }, + + new PackNugetTaskWithSymbols + { + Name = "Pack NuGet Package", + }, + + new NugetPushTask(nugetApiKey) + { + Name = "Push NuGet Package", + } + }; + + } + + [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; } + } +} + diff --git a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/SetAuthorAsPrAssigneeJob.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/SetAuthorAsPrAssigneeJob.cs new file mode 100644 index 0000000..71f1c49 --- /dev/null +++ b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/SetAuthorAsPrAssigneeJob.cs @@ -0,0 +1,101 @@ +// --------------------------------------------------------------------------- +// 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 SetAuthorAsPrAssigneeJob : Job + { + public SetAuthorAsPrAssigneeJob(string runsOn) + { + RunsOn = runsOn; + + Permissions = new Dictionary + { + { "contents", "read" }, + { "issues", "write" }, + { "pull-requests", "write" } + }; + + Steps = new List + { + new GithubTask() + { + Name = "Set Author As PR Assignee", + Uses = "actions/github-script@v6", + With = new Dictionary + { + { "github-token", "${{ secrets.GITHUB_TOKEN }}" }, + { "script", + "const pr = context.payload.pull_request;\n" + + "if (!pr) {\n" + + " console.log('No pull request context available.');\n" + + " return;\n" + + "}\n\n" + + "const author = pr.user.login;\n" + + "if (author.endsWith('[bot]')) {\n" + + " console.log(`Skipping bot author: ${author}`);\n" + + " return;\n" + + "}\n\n" + + "console.log(`Assigning PR to author: ${author}`);\n\n" + + "await github.rest.issues.addAssignees({\n" + + " owner: context.repo.owner,\n" + + " repo: context.repo.repo,\n" + + " issue_number: pr.number,\n" + + " assignees: [author]\n" + + "});\n" + } + } + }, + }; + } + + [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/ADotNet/Models/Pipelines/GithubPipelines/DotNets/TagJobV1.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/TagJobV1.cs new file mode 100644 index 0000000..3209704 --- /dev/null +++ b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/TagJobV1.cs @@ -0,0 +1,141 @@ +// --------------------------------------------------------------------------- +// 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 TagJobV1 : Job + { + public TagJobV1( + string runsOn, + string dependsOn, + string projectRelativePath, + string githubToken, + string branchName) + : this(runsOn, new string[] { dependsOn }, projectRelativePath, githubToken, branchName) + { } + + public TagJobV1( + string runsOn, + string[] dependsOn, + string projectRelativePath, + string githubToken, + string branchName) + { + RunsOn = runsOn; + Needs = dependsOn; + + If = + $"needs.{string.Join(",", dependsOn)}.result == 'success' && {System.Environment.NewLine}" + + $"github.event.pull_request.merged && {System.Environment.NewLine}" + + $"github.event.pull_request.base.ref == '{branchName}' && {System.Environment.NewLine}" + + $"startsWith(github.event.pull_request.title, 'RELEASES:') && {System.Environment.NewLine}" + + "contains(github.event.pull_request.labels.*.name, 'RELEASES')"; + + Steps = new List + { + new CheckoutTaskV3 + { + Name = "Checkout code", + With = new Dictionary + { + { "token", githubToken } + } + }, + + new ConfigureGitTask() + { + Name = "Configure Git", + }, + + new ExtractProjectPropertyTask( + name: "Extract Version", + id: "extract_version", + projectRelativePath, + propertyName: "Version", + stepVariableName: "version_number", + runsOn), + + new GithubTask() + { + Name = "Display Version", + Run = "echo \"Version number: ${{ steps.extract_version.outputs.version_number }}\"" + }, + + new ExtractProjectPropertyTask( + name: $"Extract Package Release Notes", + id: "extract_package_release_notes", + projectRelativePath, + propertyName: "PackageReleaseNotes", + stepVariableName: "package_release_notes", + runsOn), + + new GithubTask() + { + Name = "Display Package Release Notes", + Run = + "echo \"Package Release Notes: " + + "${{ steps.extract_package_release_notes.outputs.package_release_notes }}\"" + }, + + new CreateGitHubTagTask( + tagName: "v${{ steps.extract_version.outputs.version_number }}", + tagMessage: "Release - v${{ steps.extract_version.outputs.version_number }}") + { + Name = "Create GitHub Tag", + }, + + new CreateGitHubReleaseTask( + releaseName: "Release - v${{ steps.extract_version.outputs.version_number }}", + tagName: "v${{ steps.extract_version.outputs.version_number }}", + releaseNotes: "${{ steps.extract_package_release_notes.outputs.package_release_notes }}", + githubToken) + { + Name = "Create GitHub Release", + Uses = "actions/create-release@v1", + }, + }; + } + + [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; } + } +} diff --git a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Tasks/SetupDotNetTaskV4s/SetupDotNetTaskV4.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Tasks/SetupDotNetTaskV4s/SetupDotNetTaskV4.cs new file mode 100644 index 0000000..8ca0e17 --- /dev/null +++ b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Tasks/SetupDotNetTaskV4s/SetupDotNetTaskV4.cs @@ -0,0 +1,31 @@ +// --------------------------------------------------------------------------- +// 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 YamlDotNet.Serialization; + +namespace ADotNet.Models.Pipelines.GithubPipelines.DotNets.Tasks.SetupDotNetTaskV4s +{ + public class SetupDotNetTaskV4 : GithubTask + { + public SetupDotNetTaskV4() + { + this.Uses = "actions/setup-dotnet@v4"; + } + + /// + /// Represents the usage of an external action or a specific version of an action in a GitHub Actions job step. + /// Default value: actions/setup-dotnet@v4 + /// + [YamlMember(Order = 4, DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public new string Uses { get; private set; } + + /// + /// Used to provide additional configuration or parameters for a specific step in the workflow. + /// + [YamlMember(Order = 5, DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public new TargetDotNetVersionV4 With { get; set; } + } +} diff --git a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Tasks/SetupDotNetTaskV4s/TargetDotNetVersionV4.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Tasks/SetupDotNetTaskV4s/TargetDotNetVersionV4.cs new file mode 100644 index 0000000..ca4eb30 --- /dev/null +++ b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Tasks/SetupDotNetTaskV4s/TargetDotNetVersionV4.cs @@ -0,0 +1,16 @@ +// --------------------------------------------------------------------------- +// 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 YamlDotNet.Serialization; + +namespace ADotNet.Models.Pipelines.GithubPipelines.DotNets.Tasks.SetupDotNetTaskV4s +{ + public class TargetDotNetVersionV4 + { + [YamlMember(Alias = "dotnet-version")] + public string DotNetVersion { get; set; } + } +}