feat: Add GitRepoRegistration cmdlets (#5)
- adds GitRepoRegistration cmdlets - adds basic tests for GitRepoRegistration implementations - adds initial build project and scripts Refs: #5, #6
This commit is contained in:
parent
3547e32b6e
commit
5c09b7c5a8
66 changed files with 1619 additions and 142 deletions
|
|
@ -1,4 +1,4 @@
|
|||
using Cake.Common.Tools.DotNet;
|
||||
using Cake.Common.Tools.DotNet;
|
||||
using Cake.Common.Tools.DotNet.Build;
|
||||
using Cake.Common.Tools.DotNet.MSBuild;
|
||||
using Cake.Core.Diagnostics;
|
||||
|
|
@ -17,14 +17,27 @@ public class BuildTask : FrostingTask<BuildContext>
|
|||
|
||||
var buildSettings = new DotNetBuildSettings()
|
||||
{
|
||||
MSBuildSettings = new DotNetMSBuildSettings()
|
||||
{
|
||||
VersionSuffix = "cake"
|
||||
},
|
||||
MSBuildSettings = new DotNetMSBuildSettings(),
|
||||
OutputDirectory = context.PowershellModuleOutputDir,
|
||||
DiagnosticOutput = true
|
||||
DiagnosticOutput = true,
|
||||
};
|
||||
|
||||
// If a build suffix is provided, use it, otherwise whatever comes from Directory.Build.props will be used.
|
||||
// It's not possible to set the actual version number for a build in this project as that is purely controlled
|
||||
// from individual Directory.Build.props files
|
||||
if (!string.IsNullOrEmpty(context.BuildSuffix))
|
||||
{
|
||||
buildSettings.MSBuildSettings.VersionSuffix = context.BuildSuffix;
|
||||
}
|
||||
|
||||
// Disable SourceLink automatic commit hash addition. Documentation about how that package work is garbage and
|
||||
// this property is only mentioned in breaking changes https://learn.microsoft.com/en-us/dotnet/core/compatibility/sdk/8.0/source-link
|
||||
// which is pretty on par for shit like this.
|
||||
if (context.DisableCommitHash)
|
||||
{
|
||||
buildSettings.MSBuildSettings.Properties.Add("IncludeSourceRevisionInInformationalVersion", ["false"]);
|
||||
}
|
||||
|
||||
// /p:DebugSymbols=false
|
||||
buildSettings.MSBuildSettings.Properties.Add("DebugSymbols", ["false"]);
|
||||
// /p:DebugType=None
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using Cake.Common;
|
||||
using Cake.Common;
|
||||
using Cake.Common.Tools.DotNet;
|
||||
using Cake.Core.Diagnostics;
|
||||
using Cake.Frosting;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Cake.Core.Diagnostics;
|
||||
using Cake.Core.IO;
|
||||
|
|
@ -8,22 +9,26 @@ using Cake.Powershell;
|
|||
namespace Build.Tasks;
|
||||
|
||||
[TaskName("CopyOutput")]
|
||||
[IsDependeeOf(typeof(CreateBundleArchiveTask))]
|
||||
public class CopyOutputTask : FrostingTask<BuildContext>
|
||||
{
|
||||
public override void Run(BuildContext context)
|
||||
{
|
||||
var powershellModuleName = "PowershellModule";
|
||||
// TODO: [#10] Refactor file paths used in build project to be more explict and easier to understand
|
||||
// Probably don't create full file locations when I can pass the output dir in and have the script make the path
|
||||
var scriptParams = new
|
||||
{
|
||||
Path = $"{context.PowershellModuleOutputDir}/{powershellModuleName}.psd1",
|
||||
PowershellModuleFileLocation = $"{context.PowershellModuleOutputDir}/{powershellModuleName}.psd1",
|
||||
Guid = Guid.Parse("5cdf4635-edb0-428c-8d9b-92d0bcd47443"),
|
||||
Author = "Me",
|
||||
NestedModules = new[] { $"{powershellModuleName}.dll" },
|
||||
RootModule = $"{powershellModuleName}.psm1",
|
||||
CmdletsToExport = new[] { "Get-Calendar" },
|
||||
ManifestFileLocation = $"{context.PowershellModuleOutputDir}/{powershellModuleName}.psm1"
|
||||
CmdletsToExport = GetExportedCmdlets(),
|
||||
ManifestFileLocation = $"{context.PowershellModuleOutputDir}/{powershellModuleName}.psm1",
|
||||
OutputLocation = context.PowershellModuleOutputDir,
|
||||
};
|
||||
|
||||
|
||||
var psSettings = new PowershellSettings()
|
||||
{
|
||||
Arguments = new ProcessArgumentBuilder(),
|
||||
|
|
@ -31,19 +36,35 @@ public class CopyOutputTask : FrostingTask<BuildContext>
|
|||
|
||||
// This feels a bit ugly/redundant seeing as I've defined the scriptParams above, but I'm leaving it as is
|
||||
// until I want to come back and clean this up properly
|
||||
psSettings.Arguments.Append("path", ToPowershellSafeString(scriptParams.Path));
|
||||
psSettings.Arguments.Append("powershellModuleFileLocation", ToPowershellSafeString(scriptParams.PowershellModuleFileLocation));
|
||||
psSettings.Arguments.Append("guid", ToPowershellSafeString(scriptParams.Guid.ToString()));
|
||||
psSettings.Arguments.Append("author", ToPowershellSafeString(scriptParams.Author));
|
||||
psSettings.Arguments.Append("nestedModules", $"@({string.Join(",", scriptParams.NestedModules.Select(ToPowershellSafeString))})");
|
||||
psSettings.Arguments.Append("rootModule", ToPowershellSafeString(scriptParams.RootModule));
|
||||
psSettings.Arguments.Append("cmdletsToExport", $"@({string.Join(",", scriptParams.CmdletsToExport.Select(ToPowershellSafeString))})");
|
||||
psSettings.Arguments.Append("manifestFileLocation", ToPowershellSafeString(scriptParams.ManifestFileLocation));
|
||||
psSettings.Arguments.Append("outputDir", ToPowershellSafeString(scriptParams.OutputLocation));
|
||||
|
||||
context.StartPowershellFile(context.CreateModuleManifestScript, psSettings);
|
||||
context.Log.Information($"Module output to: {context.PowershellModuleOutputDir.Path.MakeAbsolute(context.Environment)}");
|
||||
context.Log.Information($"Module files output to: {context.PowershellModuleOutputDir.Path.MakeAbsolute(context.Environment)}");
|
||||
|
||||
base.Run(context);
|
||||
}
|
||||
|
||||
private static string ToPowershellSafeString(string unescapedString) => $"'{unescapedString}'";
|
||||
|
||||
private static List<string> GetExportedCmdlets()
|
||||
{
|
||||
return ["Get-Calendar", .. GetGitRepoRegistrationVerbs()];
|
||||
}
|
||||
|
||||
private static List<string> GetGitRepoRegistrationVerbs()
|
||||
{
|
||||
// TODO: I should make some reference file for these verbs and name but that'd pull in powershell dependencies
|
||||
// to the build and I'd like to avoid that.
|
||||
// This isn't great but commands are unlikely to change that frequently
|
||||
string[] verbs = ["Get", "New", "Remove", "Show"];
|
||||
var commandName = "GitRepoRegistration";
|
||||
return [.. verbs.Select(x => $"{x}-{commandName}")];
|
||||
}
|
||||
}
|
||||
136
build/Tasks/CreateBundleArchiveTask.cs
Normal file
136
build/Tasks/CreateBundleArchiveTask.cs
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Text.RegularExpressions;
|
||||
using Cake.Common.Build;
|
||||
using Cake.Common.Tools.MSBuild;
|
||||
using Cake.Core.Diagnostics;
|
||||
using Cake.Frosting;
|
||||
|
||||
namespace Build.Tasks;
|
||||
|
||||
[TaskName("CreateBundleArchive")]
|
||||
[IsDependentOn(typeof(CopyOutputTask))]
|
||||
public class CreateBundleArchiveTask : FrostingTask<BuildContext>
|
||||
{
|
||||
public override void Run(BuildContext context)
|
||||
{
|
||||
context.Log.Information($"Creating bundle archive");
|
||||
BundleMinimalFiles(context);
|
||||
base.Run(context);
|
||||
}
|
||||
|
||||
private static void BundleMinimalFiles(BuildContext context)
|
||||
{
|
||||
// We're effectively replicating PostBuild.ps1 with all of this as Remove-Item at the end of it does not remove files the same way.
|
||||
// TODO: See if that's something I can fix
|
||||
var powershellModuleBuildLocation = context.PowershellModuleOutputDir.Path.MakeAbsolute(context.Environment).FullPath;
|
||||
// PowershellModule is here so that the script that adds to the users $Env:PSModulePath correctly resolves when using
|
||||
// Import-Module "PowershellModule"
|
||||
// TODO: Generate the script to add to the PSModulePath in the bundleOutputLocation directory
|
||||
|
||||
// Top level bundle output location
|
||||
var bundleRootLocation = Directory.CreateDirectory(Path.Combine(powershellModuleBuildLocation, "bundle")).FullName;
|
||||
// Module folder location that will be zipped up and will contain any setup scripts/readme etc
|
||||
var moduleFolderLocation = Directory.CreateDirectory(Path.Combine(bundleRootLocation, "NulahModule")).FullName;
|
||||
// Location to put all the module dlls and other files
|
||||
var powershelModuleOutputLocation = Directory.CreateDirectory(Path.Combine(moduleFolderLocation, "PowershellModule")).FullName;
|
||||
|
||||
context.Log.Information($"Bundle location: {bundleRootLocation}");
|
||||
context.Log.Information($"Built file location: {powershellModuleBuildLocation}");
|
||||
|
||||
// PostBuild.ps1 for PowershellModule should have already been run at this point - it won't remove files, but it will copy the
|
||||
// correct e_sqlite3.dll that we need.
|
||||
// This code is all commented out because I'm not entirely satisfied with PostBuild.ps1 doing some things but
|
||||
// this build doing slightly different.
|
||||
// // Get the location for the specific version of e_sqlite3.dll we need - PowerShell binary modules resolve dependent dlls
|
||||
// // from the executing assembly directory first, and the built output of the module includes a lot of other dlls that
|
||||
// // are already available with the dotnet runtime so there's no need for us to include them.
|
||||
// // I mean, we could, but we'd also be terrible software engineers if we couldn't do something as basic as reducing files we need.
|
||||
// // TODO: Account for other runtimes such as linux based ones where the dotnet runtime might _not_ provide these files for free.
|
||||
// // It'd require looking into how the module loads for PowerShell on those operating systems, and I might never
|
||||
// // get around to doing that because I use windows (currently).
|
||||
// // TODO: Update this later to handle other runtimes, for now we hardcode to win-x64 because it's what I use
|
||||
// var nativeSqliteDllLocation = System.IO.Path.Combine(powershellModuleBuildLocation, "runtimes", "win-x64", "native", "e_sqlite3.dll");
|
||||
//
|
||||
// if (!File.Exists(nativeSqliteDllLocation))
|
||||
// {
|
||||
// context.Log.Error($"BUNDLE FAILED: Unable to locate ${nativeSqliteDllLocation}");
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// context.Log.Information($"Copying '${nativeSqliteDllLocation}' to '${bundleOutputLocation}'");
|
||||
//
|
||||
// File.Copy(nativeSqliteDllLocation, System.IO.Path.Combine(bundleOutputLocation, System.IO.Path.GetFileName(nativeSqliteDllLocation)));
|
||||
|
||||
var moduleCoreFiles = Directory.GetFiles(powershellModuleBuildLocation, "ModuleCore*", searchOption: SearchOption.TopDirectoryOnly);
|
||||
var powershellModuleFiles = Directory.GetFiles(powershellModuleBuildLocation, "PowershellModule*", searchOption: SearchOption.TopDirectoryOnly);
|
||||
var sqliteFiles = Directory.GetFiles(powershellModuleBuildLocation, "*SQLite*", searchOption: SearchOption.TopDirectoryOnly);
|
||||
|
||||
string[] allFiles = [.. moduleCoreFiles, .. powershellModuleFiles, .. sqliteFiles];
|
||||
|
||||
context.Log.Information($"Copying {allFiles.Length} files to the module folder {powershelModuleOutputLocation}");
|
||||
foreach (var file in allFiles)
|
||||
{
|
||||
context.Log.Information($"Copying {Path.GetFileName(file)}");
|
||||
File.Copy(file, Path.Combine(powershelModuleOutputLocation, Path.GetFileName(file)));
|
||||
}
|
||||
|
||||
context.Log.Information($"Bundle files copied");
|
||||
|
||||
GenerateModuleImportScript(moduleFolderLocation, context);
|
||||
var moduleVersionForFilename = Helpers.GetPowershellModuleVersion(powershelModuleOutputLocation);
|
||||
CreateBundleZip(bundleRootLocation, moduleFolderLocation, moduleVersionForFilename, context);
|
||||
}
|
||||
|
||||
private static void GenerateModuleImportScript(string bundleLocation, BuildContext context)
|
||||
{
|
||||
context.Log.Information($"Generating module import script");
|
||||
var scriptFileName = Path.Combine(bundleLocation, "NulahPowershell.ps1");
|
||||
|
||||
using var scriptFile = File.Create(scriptFileName);
|
||||
using var fileWriter = new StreamWriter(scriptFile);
|
||||
// TODO: Document the set up function
|
||||
// TODO: Create a ticket for fleshing out the set up function (or just do that work later)
|
||||
fileWriter.Write(
|
||||
"""
|
||||
# To use, first copy all files to the same location as your $profile under ./NulahModule, then open your powershell profile located at $profile and add the following:
|
||||
|
||||
# Script setup style: Using the setup script to import as needed (this method will also set custom prompts and other alias functions)
|
||||
# . "$PSScriptRoot/NulahModule/NulahPowershell.ps1"
|
||||
# SetupNulahPowershell
|
||||
# Just the module: Use the following to just import just the powershell module
|
||||
# Import-Module -Name "$PSScriptRoot/NulahModule/PowershellModule"
|
||||
|
||||
function SetupNulahPowershell
|
||||
{
|
||||
# only add to our module path once
|
||||
# The intent for this is that a user will have copied this bundle folder alongside their $profile
|
||||
# location, eg, they'll have a folder called NulahModule that will contain everything within the bundle zip
|
||||
$testBundleDirectory = $PSScriptRoot
|
||||
$bundleInPath = ($Env:PSModulePath -split ';').TrimEnd('\') -contains $testBundleDirectory;
|
||||
|
||||
if ($false -eq $bundleInPath)
|
||||
{
|
||||
$env:PSModulePath = @(
|
||||
$env:PSModulePath
|
||||
$testBundleDirectory
|
||||
) -Join [System.IO.Path]::PathSeparator
|
||||
}
|
||||
|
||||
Import-Module "PowershellModule"
|
||||
}
|
||||
"""
|
||||
);
|
||||
context.Log.Information($"Module import script created at '{scriptFileName}'");
|
||||
}
|
||||
|
||||
private static void CreateBundleZip(string bundleLocation, string moduleFolderLocation, string version, BuildContext context)
|
||||
{
|
||||
var bundleZipFileLocaiton = Path.Combine(bundleLocation, $"NulahModule-{version}.zip");
|
||||
context.Log.Information($"Bundling module into archive '{bundleZipFileLocaiton}'");
|
||||
ZipFile.CreateFromDirectory(moduleFolderLocation, bundleZipFileLocaiton, CompressionLevel.Fastest, true);
|
||||
context.Log.Information($"Archive created at '{bundleZipFileLocaiton}'");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
using Cake.Core;
|
||||
using Cake.Core;
|
||||
using Cake.Core.Diagnostics;
|
||||
using Cake.Frosting;
|
||||
|
||||
|
|
@ -6,7 +6,10 @@ namespace Build.Tasks;
|
|||
|
||||
// Consider this the "entry" point for builds, task order is defined by a chain of IsDependentOn
|
||||
[TaskName("Default")]
|
||||
[IsDependentOn(typeof(BuildTask))]
|
||||
[IsDependentOn(typeof(TagCommitTask))]
|
||||
[IsDependentOn(typeof(CopyOutputTask))]
|
||||
[IsDependentOn(typeof(CreateBundleArchiveTask))]
|
||||
public class DefaultTask : FrostingTask
|
||||
{
|
||||
public override void Run(ICakeContext context)
|
||||
|
|
|
|||
54
build/Tasks/TagCommitTask.cs
Normal file
54
build/Tasks/TagCommitTask.cs
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using Cake.Core.Diagnostics;
|
||||
using Cake.Frosting;
|
||||
|
||||
namespace Build.Tasks;
|
||||
|
||||
[TaskName("TagCurrentCommitWithVersion")]
|
||||
[IsDependentOn(typeof(BuildTask))]
|
||||
public class TagCommitTask : FrostingTask<BuildContext>
|
||||
{
|
||||
public override void Run(BuildContext context)
|
||||
{
|
||||
// This task only really exists to create pre-release builds for pull requests.
|
||||
// It'll tag the current commit with the same version that'll be used for the bundle archive filename.
|
||||
// TODO: update build.ps1 in the solution root so it doesn't do this tagging task
|
||||
TagCommit(context);
|
||||
base.Run(context);
|
||||
}
|
||||
|
||||
private static void TagCommit(BuildContext context)
|
||||
{
|
||||
var moduleVersionForFilename = Helpers.GetPowershellModuleVersion(context.PowershellModuleOutputDir.Path.MakeAbsolute(context.Environment).FullPath);
|
||||
|
||||
var ps = new ProcessStartInfo("git",
|
||||
["-C", context.BaseSourceLocation.FullPath, "tag", moduleVersionForFilename])
|
||||
{
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
};
|
||||
|
||||
// If the user doesn't have git on their path, this will throw an exception that I don't have to do anything
|
||||
// special with, it'll be unhandled and powershell will handle it
|
||||
var gitProcess = Process.Start(ps);
|
||||
|
||||
// This probably shouldn't be possible?
|
||||
if (gitProcess is null)
|
||||
{
|
||||
throw new Exception("git failed to start")
|
||||
{
|
||||
Source = "git-process",
|
||||
};
|
||||
}
|
||||
|
||||
gitProcess.WaitForExit();
|
||||
|
||||
if (!gitProcess.StandardError.EndOfStream)
|
||||
{
|
||||
throw new Exception($"Unable to tag: {gitProcess.StandardError.ReadToEnd().Trim()}. Either remove the existing tag, or commit your changes before running a build");
|
||||
}
|
||||
|
||||
context.Log.Information($"Tagged commit with {moduleVersionForFilename}");
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue