diff --git a/.build/Build.CI.cs b/.build/Build.CI.cs index bfd360b3..b1e3ad1f 100644 --- a/.build/Build.CI.cs +++ b/.build/Build.CI.cs @@ -31,6 +31,7 @@ internal class LocalConstants "ci-ignore", GitHubActionsImage.WindowsLatest, GitHubActionsImage.UbuntuLatest, + AutoGenerate = false, On = new[] { GitHubActionsTrigger.Push }, OnPushTags = new[] { "v*" }, OnPushBranches = new[] { "master", "main", "next" }, @@ -42,6 +43,7 @@ internal class LocalConstants GitHubActionsImage.MacOsLatest, GitHubActionsImage.WindowsLatest, GitHubActionsImage.UbuntuLatest, + AutoGenerate = false, On = new[] { GitHubActionsTrigger.Push }, OnPushTags = new[] { "v*" }, OnPushBranches = new[] { "master", "main", "next" }, @@ -101,6 +103,7 @@ RocketSurgeonGitHubActionsConfiguration configuration var checkoutStep = buildJob.Steps.OfType().Single(); // For fetch all checkoutStep.FetchDepth = 0; + buildJob.Environment["NUGET_PACKAGES"] = "${{ github.workspace }}/.nuget/packages"; buildJob.Steps.InsertRange( buildJob.Steps.IndexOf(checkoutStep) + 1, new BaseGitHubActionsStep[] @@ -109,9 +112,18 @@ RocketSurgeonGitHubActionsConfiguration configuration { Run = "git fetch --prune" }, - new SetupDotNetStep("Use .NET Core 2.1 SDK") + new UsingStep("NuGet Cache") { - DotNetVersion = "2.1.x" + Uses = "actions/cache@v2", + With = + { + ["path"] = "${{ github.workspace }}/.nuget/packages", + // keep in mind using central package versioning here + ["key"] = + "${{ runner.os }}-nuget-${{ hashFiles('**/Directory.Packages.props') }}-${{ hashFiles('**/Directory.Packages.support.props') }}", + ["restore-keys"] = @"| + ${{ runner.os }}-nuget-" + } }, new SetupDotNetStep("Use .NET Core 3.1 SDK") { @@ -120,7 +132,7 @@ RocketSurgeonGitHubActionsConfiguration configuration new SetupDotNetStep("Use .NET Core 6.0 SDK") { DotNetVersion = "6.0.x" - }, + } } ); diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 52cad320..08b53c79 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,6 +62,8 @@ on: jobs: Build: + env: + NUGET_PACKAGES: '${{ github.workspace }}/.nuget/packages' strategy: fail-fast: false matrix: @@ -76,10 +78,13 @@ jobs: - name: Fetch all history for all tags and branches run: | git fetch --prune - - name: 🔨 Use .NET Core 2.1 SDK - uses: actions/setup-dotnet@v1.9.0 + - name: NuGet Cache + uses: actions/cache@v2 with: - dotnet-version: '2.1.x' + path: '${{ github.workspace }}/.nuget/packages' + key: "${{ runner.os }}-nuget-${{ hashFiles('**/Directory.Packages.props') }}-${{ hashFiles('**/Directory.Packages.support.props') }}" + restore-keys: | + ${{ runner.os }}-nuget- - name: 🔨 Use .NET Core 3.1 SDK uses: actions/setup-dotnet@v1.9.0 with: @@ -134,7 +139,7 @@ jobs: Publish: needs: - Build - uses: RocketSurgeonsGuild/actions/.github/workflows/publish-nuget.yml@v0.3.0 secrets: RSG_NUGET_API_KEY: '${{ secrets.RSG_NUGET_API_KEY }}' RSG_AZURE_DEVOPS: '${{ secrets.RSG_AZURE_DEVOPS }}' + uses: RocketSurgeonsGuild/actions/.github/workflows/publish-nuget.yml@v0.3.0 diff --git a/.husky/pre-commit b/.husky/pre-commit index c32e97ff..104008d0 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -2,3 +2,7 @@ . "$(dirname "$0")/_/husky.sh" npx lint-staged -d -r +dotnet nuke --generate-configuration GitHubActions_ci --host GitHubActions +git add .github/workflows/ci.yml +dotnet nuke --generate-configuration GitHubActions_ci-ignore --host GitHubActions +git add .github/workflows/ci-ignore.yml diff --git a/.nuke/build.schema.json b/.nuke/build.schema.json index fbe6772a..f99d257e 100644 --- a/.nuke/build.schema.json +++ b/.nuke/build.schema.json @@ -1,149 +1,157 @@ { - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "Build Schema", - "$ref": "#/definitions/build", - "definitions": { - "build": { - "type": "object", - "properties": { - "Artifacts": { - "type": "string", - "description": "The directory where artifacts are to be dropped" - }, - "Configuration": { - "type": "string", - "description": "Configuration to build", - "enum": ["Debug", "Release"] - }, - "Continue": { - "type": "boolean", - "description": "Indicates to continue a previously failed build attempt" - }, - "Coverage": { - "type": "string", - "description": "The directory where coverage artifacts are to be dropped" - }, - "Help": { - "type": "boolean", - "description": "Shows the help text for this build assembly" - }, - "Host": { - "type": "string", - "description": "Host for execution. Default is 'automatic'", - "enum": [ - "AppVeyor", - "AzurePipelines", - "Bamboo", - "Bitrise", - "GitHubActions", - "GitLab", - "Jenkins", - "Rider", - "SpaceAutomation", - "TeamCity", - "Terminal", - "TravisCI", - "VisualStudio", - "VSCode" - ] - }, - "NoLogo": { - "type": "boolean", - "description": "Disables displaying the NUKE logo" - }, - "Partition": { - "type": "string", - "description": "Partition to use on CI" - }, - "Plan": { - "type": "boolean", - "description": "Shows the execution plan (HTML)" - }, - "Profile": { - "type": "array", - "description": "Defines the profiles to load", - "items": { - "type": "string" - } - }, - "Root": { - "type": "string", - "description": "Root directory during build execution" - }, - "Skip": { - "type": "array", - "description": "List of targets to be skipped. Empty list skips all dependencies", - "items": { - "type": "string", - "enum": [ - "Build", - "BuildVersion", - "Clean", - "CoreBuild", - "CorePack", - "CoreRestore", - "CoreTest", - "Default", - "DotnetToolRestore", - "Generate_Code_Coverage_Badges", - "Generate_Code_Coverage_Report", - "Generate_Code_Coverage_Report_Cobertura", - "Generate_Code_Coverage_Summary", - "GenerateCodeCoverageBadges", - "GenerateCodeCoverageReport", - "GenerateCodeCoverageReportCobertura", - "GenerateCodeCoverageSummary", - "GenerateReadme", - "Pack", - "Restore", - "Test", - "Trigger_Code_Coverage_Reports", - "TriggerCodeCoverageReports" - ] - } - }, - "Solution": { - "type": "string", - "description": "Path to a solution file that is automatically loaded" - }, - "Target": { - "type": "array", - "description": "List of targets to be invoked. Default is '{default_target}'", - "items": { - "type": "string", - "enum": [ - "Build", - "BuildVersion", - "Clean", - "CoreBuild", - "CorePack", - "CoreRestore", - "CoreTest", - "Default", - "DotnetToolRestore", - "Generate_Code_Coverage_Badges", - "Generate_Code_Coverage_Report", - "Generate_Code_Coverage_Report_Cobertura", - "Generate_Code_Coverage_Summary", - "GenerateCodeCoverageBadges", - "GenerateCodeCoverageReport", - "GenerateCodeCoverageReportCobertura", - "GenerateCodeCoverageSummary", - "GenerateReadme", - "Pack", - "Restore", - "Test", - "Trigger_Code_Coverage_Reports", - "TriggerCodeCoverageReports" - ] - } - }, - "Verbosity": { - "type": "string", - "description": "Logging verbosity during build execution. Default is 'Normal'", - "enum": ["Minimal", "Normal", "Quiet", "Verbose"] - } - } + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Build Schema", + "$ref": "#/definitions/build", + "definitions": { + "build": { + "type": "object", + "properties": { + "Artifacts": { + "type": "string", + "description": "The directory where artifacts are to be dropped" + }, + "Configuration": { + "type": "string", + "description": "Configuration to build", + "enum": [ + "Debug", + "Release" + ] + }, + "Continue": { + "type": "boolean", + "description": "Indicates to continue a previously failed build attempt" + }, + "Coverage": { + "type": "string", + "description": "The directory where coverage artifacts are to be dropped" + }, + "Help": { + "type": "boolean", + "description": "Shows the help text for this build assembly" + }, + "Host": { + "type": "string", + "description": "Host for execution. Default is 'automatic'", + "enum": [ + "AppVeyor", + "AzurePipelines", + "Bamboo", + "Bitrise", + "GitHubActions", + "GitLab", + "Jenkins", + "Rider", + "SpaceAutomation", + "TeamCity", + "Terminal", + "TravisCI", + "VisualStudio", + "VSCode" + ] + }, + "NoLogo": { + "type": "boolean", + "description": "Disables displaying the NUKE logo" + }, + "Partition": { + "type": "string", + "description": "Partition to use on CI" + }, + "Plan": { + "type": "boolean", + "description": "Shows the execution plan (HTML)" + }, + "Profile": { + "type": "array", + "description": "Defines the profiles to load", + "items": { + "type": "string" + } + }, + "Root": { + "type": "string", + "description": "Root directory during build execution" + }, + "Skip": { + "type": "array", + "description": "List of targets to be skipped. Empty list skips all dependencies", + "items": { + "type": "string", + "enum": [ + "Build", + "BuildVersion", + "Clean", + "CoreBuild", + "CorePack", + "CoreRestore", + "CoreTest", + "Default", + "DotnetToolRestore", + "Generate_Code_Coverage_Badges", + "Generate_Code_Coverage_Report", + "Generate_Code_Coverage_Report_Cobertura", + "Generate_Code_Coverage_Summary", + "GenerateCodeCoverageBadges", + "GenerateCodeCoverageReport", + "GenerateCodeCoverageReportCobertura", + "GenerateCodeCoverageSummary", + "GenerateReadme", + "Pack", + "Restore", + "Test", + "Trigger_Code_Coverage_Reports", + "TriggerCodeCoverageReports" + ] + } + }, + "Solution": { + "type": "string", + "description": "Path to a solution file that is automatically loaded" + }, + "Target": { + "type": "array", + "description": "List of targets to be invoked. Default is '{default_target}'", + "items": { + "type": "string", + "enum": [ + "Build", + "BuildVersion", + "Clean", + "CoreBuild", + "CorePack", + "CoreRestore", + "CoreTest", + "Default", + "DotnetToolRestore", + "Generate_Code_Coverage_Badges", + "Generate_Code_Coverage_Report", + "Generate_Code_Coverage_Report_Cobertura", + "Generate_Code_Coverage_Summary", + "GenerateCodeCoverageBadges", + "GenerateCodeCoverageReport", + "GenerateCodeCoverageReportCobertura", + "GenerateCodeCoverageSummary", + "GenerateReadme", + "Pack", + "Restore", + "Test", + "Trigger_Code_Coverage_Reports", + "TriggerCodeCoverageReports" + ] + } + }, + "Verbosity": { + "type": "string", + "description": "Logging verbosity during build execution. Default is 'Normal'", + "enum": [ + "Minimal", + "Normal", + "Quiet", + "Verbose" + ] } + } } -} + } +} \ No newline at end of file diff --git a/.prettierignore b/.prettierignore index 2faf3835..5c3c2255 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,2 +1,3 @@ azure-pipelines.nuke.yml .github/workflows/ci.yml +.nuke/build.schema.json diff --git a/src/Nuke/GithubActions/BaseGitHubActionsStep.cs b/src/Nuke/GithubActions/BaseGitHubActionsStep.cs index ba43220c..26a69e94 100644 --- a/src/Nuke/GithubActions/BaseGitHubActionsStep.cs +++ b/src/Nuke/GithubActions/BaseGitHubActionsStep.cs @@ -1,5 +1,4 @@ using Nuke.Common.CI.GitHubActions.Configuration; -using Nuke.Common.Utilities.Collections; // ReSharper disable MemberCanBePrivate.Global #pragma warning disable CA2227 @@ -37,9 +36,14 @@ protected BaseGitHubActionsStep(string name) public GithubActionCondition? If { get; set; } /// - /// The environment variables for the step + /// The dependencies of this job /// - public Dictionary Environment { get; set; } = new(); + public Dictionary Environment { get; set; } = new(StringComparer.OrdinalIgnoreCase); + + /// + /// The properties to use with the action + /// + public Dictionary Secrets { get; set; } = new(StringComparer.OrdinalIgnoreCase); /// public override void Write(CustomFileWriter writer) @@ -52,19 +56,13 @@ public override void Write(CustomFileWriter writer) writer.WriteLine($"id: {Id}"); } + writer.WriteKeyValues("env", Environment); + writer.WriteKeyValues("secrets", Secrets); + if (!string.IsNullOrWhiteSpace(If?.ToString())) { writer.WriteLine($"if: {If}"); } - - if (Environment.Any()) - { - writer.WriteLine("env:"); - using (writer.Indent()) - { - Environment.ForEach(x => { writer.WriteLine($"{x.Key}: {x.Value}"); }); - } - } } } diff --git a/src/Nuke/GithubActions/GitHubActionsStepsAttribute.cs b/src/Nuke/GithubActions/GitHubActionsStepsAttribute.cs index f60243fa..3bdf7472 100644 --- a/src/Nuke/GithubActions/GitHubActionsStepsAttribute.cs +++ b/src/Nuke/GithubActions/GitHubActionsStepsAttribute.cs @@ -343,7 +343,7 @@ IReadOnlyCollection relevantTargets new RocketSurgeonsGithubWorkflowJob("Publish") { Needs = { "Build" }, - Uses = "RocketSurgeonsGuild/actions/.github/workflows/publish-nuget.yml@v0.2.6", + Uses = "RocketSurgeonsGuild/actions/.github/workflows/publish-nuget.yml@v0.3.0", Secrets = new Dictionary { ["RSG_NUGET_API_KEY"] = "${{ secrets.RSG_NUGET_API_KEY }}", @@ -470,7 +470,7 @@ string GetSecretValue(string secret) // public CloseMilestoneWorkflowAttribute(string name) : base(name) // { // -// Uses = "RocketSurgeonsGuild/actions/.github/workflows/close-milestone.yml@v0.2.6"; +// Uses = "RocketSurgeonsGuild/actions/.github/workflows/close-milestone.yml@v0.3.0"; // Secrets = new Dictionary() // { // ["GITHUB_TOKEN"] = "${{ secrets.GITHUB_TOKEN }}", diff --git a/src/Nuke/GithubActions/RocketSurgeonGitHubActionsConfiguration.cs b/src/Nuke/GithubActions/RocketSurgeonGitHubActionsConfiguration.cs index 7f65e202..2534d155 100644 --- a/src/Nuke/GithubActions/RocketSurgeonGitHubActionsConfiguration.cs +++ b/src/Nuke/GithubActions/RocketSurgeonGitHubActionsConfiguration.cs @@ -2,7 +2,6 @@ using Nuke.Common.CI.GitHubActions; using Nuke.Common.CI.GitHubActions.Configuration; using Nuke.Common.Tooling; -using Nuke.Common.Utilities.Collections; #pragma warning disable CA1002 #pragma warning disable CA1308 @@ -36,9 +35,14 @@ public class RocketSurgeonGitHubActionsConfiguration : ConfigurationEntity public List Jobs { get; set; } = new(); /// - /// The environment + /// The dependencies of this job /// - public Dictionary Environment { get; set; } = new(); + public Dictionary Environment { get; set; } = new(StringComparer.OrdinalIgnoreCase); + + /// + /// The properties to use with the action + /// + public Dictionary Secrets { get; set; } = new(StringComparer.OrdinalIgnoreCase); /// public override void Write(CustomFileWriter writer) @@ -59,6 +63,9 @@ public override void Write(CustomFileWriter writer) } } + writer.WriteKeyValues("env", Environment); + writer.WriteKeyValues("secrets", Secrets); + writer.WriteLine(); writer.WriteLine("jobs:"); @@ -66,14 +73,5 @@ public override void Write(CustomFileWriter writer) { Jobs.ForEach(x => x.Write(writer)); } - - if (Environment.Any()) - { - writer.WriteLine("env:"); - using (writer.Indent()) - { - Environment.ForEach(x => writer.WriteLine($"{x.Key}: {x.Value}")); - } - } } } diff --git a/src/Nuke/GithubActions/RocketSurgeonsGithubActionsJob.cs b/src/Nuke/GithubActions/RocketSurgeonsGithubActionsJob.cs index 720ab276..202ca414 100644 --- a/src/Nuke/GithubActions/RocketSurgeonsGithubActionsJob.cs +++ b/src/Nuke/GithubActions/RocketSurgeonsGithubActionsJob.cs @@ -2,19 +2,18 @@ using Nuke.Common.CI.GitHubActions; using Nuke.Common.CI.GitHubActions.Configuration; using Nuke.Common.Tooling; -using Nuke.Common.Utilities.Collections; #pragma warning disable CA1002 #pragma warning disable CA2227 namespace Rocket.Surgery.Nuke.GithubActions; /// -/// Base job used for generation github actions yaml +/// Base job used for generation github actions yaml /// public abstract class RocketSurgeonsGithubActionsJobBase : ConfigurationEntity { /// - /// Create the base job + /// Create the base job /// /// /// @@ -30,15 +29,30 @@ protected RocketSurgeonsGithubActionsJobBase(string name) public string Name { get; } /// - /// The dependencies of this job + /// The dependencies of this job /// - public Dictionary Outputs { get; set; } = new(); + public Dictionary Outputs { get; set; } = new(StringComparer.OrdinalIgnoreCase); /// - /// The dependencies of this job + /// The dependencies of this job + /// + public Dictionary Environment { get; set; } = new(StringComparer.OrdinalIgnoreCase); + + /// + /// The properties to use with the action + /// + public Dictionary Secrets { get; set; } = new(StringComparer.OrdinalIgnoreCase); + + /// + /// The dependencies of this job /// public List Needs { get; set; } = new(); + /// + /// The condition to run this job under + /// + public GithubActionCondition? If { get; set; } + /// public override void Write(CustomFileWriter writer) { @@ -57,13 +71,13 @@ public override void Write(CustomFileWriter writer) } } - if (Outputs.Any()) + writer.WriteKeyValues("outputs", Outputs); + writer.WriteKeyValues("env", Environment); + writer.WriteKeyValues("secrets", Secrets); + + if (!string.IsNullOrWhiteSpace(If?.ToString())) { - writer.WriteLine("outputs:"); - using (writer.Indent()) - { - Outputs.ForEach(x => writer.WriteLine($"{x.Key}: '{x.Value}'")); - } + writer.WriteLine($"if: {If}"); } } } @@ -89,18 +103,13 @@ public RocketSurgeonsGithubActionsJob(string name) : base(name) /// public IEnumerable Images { get; set; } = Enumerable.Empty(); - /// - /// The condition to run this job under - /// - public GithubActionCondition? If { get; set; } - /// /// The steps to run /// public List Steps { get; set; } = new(); /// - /// Should the job matrix fail fast, or wait for all to fail + /// Should the job matrix fail fast, or wait for all to fail /// public bool FailFast { get; set; } = true; @@ -146,11 +155,6 @@ public override void Write(CustomFileWriter writer) writer.WriteLine("runs-on: ${{ matrix.os }}"); } - if (!string.IsNullOrWhiteSpace(If?.ToString())) - { - writer.WriteLine($"if: {If}"); - } - writer.WriteLine("steps:"); using (writer.Indent()) { @@ -188,11 +192,6 @@ public RocketSurgeonsGithubWorkflowJob(string name) : base(name) /// public Dictionary With { get; set; } = new(StringComparer.OrdinalIgnoreCase); - /// - /// The properties to use with the action - /// - public Dictionary Secrets { get; set; } = new(StringComparer.OrdinalIgnoreCase); - /// public override void Write(CustomFileWriter writer) { @@ -201,24 +200,7 @@ public override void Write(CustomFileWriter writer) using (writer.Indent()) { writer.WriteLine($"uses: {Uses}"); - - if (With.Any()) - { - writer.WriteLine("with:"); - using (writer.Indent()) - { - With.ForEach(x => writer.WriteLine($"{x.Key}: '{x.Value}'")); - } - } - - if (Secrets.Any()) - { - writer.WriteLine("secrets:"); - using (writer.Indent()) - { - Secrets.ForEach(x => writer.WriteLine($"{x.Key}: '{x.Value}'")); - } - } + writer.WriteKeyValues("with", With); } } } diff --git a/src/Nuke/GithubActions/UsingStep.cs b/src/Nuke/GithubActions/UsingStep.cs index b85fa9d9..3a178c16 100644 --- a/src/Nuke/GithubActions/UsingStep.cs +++ b/src/Nuke/GithubActions/UsingStep.cs @@ -62,15 +62,50 @@ public override void Write(CustomFileWriter writer) using (writer.Indent()) { writer.WriteLine($"uses: {Uses}"); + writer.WriteKeyValues("with", With); + } + } +} - if (With.Any()) +public static class CustomFileWriterExtensions +{ + public static void WriteKeyValues(this CustomFileWriter writer, string key, IDictionary dictionary) + { + if (dictionary.Any()) + { + writer.WriteLine(key + ":"); + using (writer.Indent()) { - writer.WriteLine("with:"); - using (writer.Indent()) + dictionary.ForEach(z => WriteValue(writer, z)); + } + } + } + + private static void WriteValue(CustomFileWriter writer, KeyValuePair kvp) + { + var (key, value) = kvp; + if (value.StartsWith(">", StringComparison.Ordinal) || value.StartsWith("|", StringComparison.Ordinal)) + { + var values = value.Split('\n'); + writer.WriteLine($"{key}: {values[0].TrimEnd()}"); + using (writer.Indent()) + { + foreach (var v in values.Skip(1)) { - With.ForEach(x => writer.WriteLine($"{x.Key}: '{x.Value}'")); + writer.WriteLine(v.Trim()); } } + + return; + } + + if (value.Contains('\'', StringComparison.Ordinal)) + { + writer.WriteLine($"{key}: \"{value}\""); + } + else + { + writer.WriteLine($"{key}: '{value}'"); } } }