Compare commits

..
Author SHA1 Message Date
69e0f824de feat(build): Stamp version onto bundle archive 2026-09-04 13:39:55 +10:00
fa0531f2c5 refactor(build): move BuildContext to file, add BuildSuffix and CommitHash configuration options 2026-09-04 10:46:34 +10:00
1491a26a3e chore(build-props): Bump version number to 0.0.1 2026-09-04 10:33:11 +10:00
479c433765 build(git-provider): Split bundle archive creation into separate task, update default task chain 2026-09-04 09:49:01 +10:00
a399b29df3 build(git-provider): Create bundle zip for module 2026-09-04 09:29:15 +10:00
5cfc4819fe build(git-provider): Generate initial import script 2026-09-03 22:08:48 +10:00
9665ed1ae3 build(git-provider): Bundle up minimal required module files 2026-09-03 21:47:28 +10:00
270d545e56 build(git-provider): Add GitRepoRegistration commands to build 2026-09-03 20:42:20 +10:00
5448fa3f9e docs(git-provider): Change info box to be normal-markdown instead of Obsidian-markdown 2026-09-02 14:10:44 +10:00
78974c732c docs(git-provider): Update general section to outline case-sensitivity 2026-09-02 11:30:15 +10:00
e2caa604c5 docs(git-provider): Small documentation updates to verb usage and default behaviour for New-GitRepoRegistration 2026-09-02 11:19:17 +10:00
c8717df44a docs(git-provider): Document Remove-GitRepoRegistration 2026-09-02 11:18:36 +10:00
b91a868a54 feat(git-provider): Update Remove-GitRepoRegistration to work from any directory when -Name is given 2026-09-02 11:18:26 +10:00
6550da8c99 docs(git-provider): Update Show-GitRepoRegistration 2026-08-26 17:26:51 +10:00
0fbb0775c1 docs(git-provider): Document Show-GitRepoRegistration 2026-08-26 17:18:34 +10:00
d53e5b2fe4 feat(git-provider): Change alias of NoStack to NoPushLocation 2026-08-26 17:17:47 +10:00
52944936ed docs(git-provider): Document Get-GitRepoRegistration
- update comments for related classes and methods
2026-08-26 14:34:18 +10:00
5e0e4ad2be docs(git-provider): Document New-GitRepoRegistration 2026-08-26 14:17:06 +10:00
46eeb0cb1e feat(git-provider): Update debug output when registering
- output when no name is given
- update message when current location is confirmed to be a git repo
2026-08-26 14:16:48 +10:00
8d64b1aa9e fix(git-provider): Return after successful registration removal 2026-08-26 13:30:18 +10:00
7c6a47bfcb docs(git-provider): Add initial git command documentation
- create solution folder for docs
2026-08-26 13:16:20 +10:00
5528a756f9 refactor(git-provider): Rename noun to GitRepoRegistration 2026-08-25 17:16:06 +10:00
5f04ed4262 feat(git-provider): Set OutputType for GetGitRepoCommand 2026-08-25 14:54:31 +10:00
e49b322ec0 chore(git-provider): Rename class to match file name 2026-08-25 13:41:20 +10:00
50752c0699 chore: Remove unneeded files 2026-08-25 13:40:23 +10:00
9eb58698fa chore(git-provider): Code style 2026-08-25 13:37:47 +10:00
27117ce64b refactor(git-provider): remove SetGitRepoCommand 2026-08-25 11:19:58 +10:00
a666a49db0 feat(git-provider): Implement RemoveGitRepoCommand 2026-08-25 11:18:03 +10:00
cf446fc5dc refactor(git-provider): move IsGitRepo to GitManager, refactor NewGitRepoCommand 2026-08-25 08:41:29 +10:00
c48549117f chore(git-provider): Trim output when getting current branch 2026-08-24 17:35:53 +10:00
7b59b90572 chore(git-provider): Use -C when checking if current directory is a git repo
- move WriteDebug to IsGitRepo
- inline variable for SessionState.Path.CurrentLocation.Path
2026-08-24 17:10:20 +10:00
164df4ecf4 chore(git-provider): Code style 2026-08-24 16:46:35 +10:00
cf336da271 feat(git-provider): Load previous registrations, add debug hook
- add way to redirect debug output
- remove unique constraint on Location
2026-08-24 16:45:42 +10:00
633b35f7e9 feat(git-provider): Initial git registration storage in database
- update PostBuild.ps1 to not delete data directory created during debug
- move sqlite-net-pcl to ModuleCore
2026-08-24 16:02:51 +10:00
caa65ca7c3 feat(git-provider): Initial DatabaseManager 2026-08-24 14:48:48 +10:00
93717b4e43 chore(git-provider): Code style 2026-08-24 14:25:53 +10:00
54d5dd25b9 refactor(git-provider): Rename GitReg to GitRegistration
- rename private GitRegistration to InternalGitRegistration
2026-08-24 10:17:08 +10:00
39022e4186 feat(git-provider): Display current branch when listing registered repos
- change setters to init when parsing git folders
- change Console.WriteLine to Debug.WriteLine in GitManager constructor
2026-08-24 10:12:35 +10:00
52f7260bf4 chore(git-provider): Remove output from command constructors, change informational output to use WriteDebug 2026-08-24 10:02:10 +10:00
48ad65e86c refactor(git-provider): Centralise *-GitRepo noun name 2026-08-23 09:58:16 +10:00
740d35d3e7 feat(git-provider): Initial Show-GitRepo implementation 2026-08-23 09:53:33 +10:00
af5c9e43b6 feat(git-provider): Initial Get-GitRepo implementation 2026-08-23 09:25:45 +10:00
a0e29619a4 chore(git-provider): Move commands to command folder 2026-08-23 09:04:38 +10:00
2b3c7d0153 feat(git-provider): Remove concept of directory registrations and keep it simple 2026-08-21 16:53:54 +10:00
30e103f021 tests(git-provider): Add duplicated repo registration test 2026-08-21 16:53:54 +10:00
cf2660b6b2 tests(git-provider): Add fact tests for empty name registrations 2026-08-10 16:01:15 +10:00
09236e826a tests(git-provider): Add basic registration tests
- make ModuleCore internals visible to ModuleTests
2026-08-10 15:50:24 +10:00
b9f856e716 feat(git-provider): First pass of creating directories from New-GitRepo command
- move GitManager to ModuleCore
- refactor SetGitRepoCommand to own file
2026-08-10 14:54:15 +10:00
1363f6ce02 chore(git-provider): explore setting the location based on a child fragment match 2026-08-07 16:49:30 +10:00
dedea9213b refactor(git-provider): Basic manager class implementation and dummy Set-GitRepo command 2026-08-07 14:58:16 +10:00
6193d30029 feat(git-provider): add Sqlite to PowershellModule
- add CopyLocalLockFileAssemblies to PowershellModule.csproj
- add postbuild script to copy Sqlite files and remove unneeded files in debug output
- add sqlite-net-pcl 1.11.285
2026-08-07 11:05:14 +10:00
830ae2c560 feat(git-provider): add inital implementation for New-GitRepo 2026-08-07 08:51:54 +10:00
1b09d15937 feat(git-provider): More placeholder overrides for future use 2026-08-06 15:31:17 +10:00
05d6eece99 chore(powershell-harness): refactor command code, add git provider to runspace, output command results to console 2026-08-06 14:50:09 +10:00
e6665a588b feat(git-provider): Add basic GitRepo provider 2026-08-06 14:44:24 +10:00
6ae34b3c83 chore(custom-host): Add way to manage PromptForChoice 2026-08-06 14:44:03 +10:00
d1ec31c6e9 chore: add placeholder content when generating module manifest 2026-08-06 14:42:56 +10:00
56 changed files with 192 additions and 371 deletions

4
.gitattributes vendored
View file

@ -1,4 +0,0 @@
*.verified.txt text eol=lf working-tree-encoding=UTF-8
*.verified.xml text eol=lf working-tree-encoding=UTF-8
*.verified.json text eol=lf working-tree-encoding=UTF-8
*.verified.bin binary

View file

@ -1,7 +1,6 @@
using Cake.Common.IO;
using Cake.Common.IO;
using Cake.Common.IO.Paths;
using Cake.Core;
using Cake.Core.IO;
using Cake.Frosting;
namespace Build;
@ -11,7 +10,7 @@ public class BuildContext : FrostingContext
/// <summary>
/// Base source directory
/// </summary>
public DirectoryPath BaseSourceLocation { get; set; }
public ConvertableDirectoryPath BaseSourceLocation { get; set; }
/// <summary>
/// Powershell module project folder
@ -33,29 +32,19 @@ public class BuildContext : FrostingContext
/// </summary>
public ConvertableFilePath CreateModuleManifestScript { get; set; }
/// <summary>
/// Suffix to tag the build with, defaults to pre-release.
/// <para>
/// Immediately follows the version number and before the commit hash.
/// </para>
/// </summary>
public string BuildSuffix { get; set; }
/// <summary>
/// Disable the commit hash from being added
/// </summary>
public bool DisableCommitHash { get; set; }
public BuildContext(ICakeContext context)
: base(context)
{
BaseSourceLocation = context.Directory("../src").Path.MakeAbsolute(context.Environment);
BaseSourceLocation = context.Directory("../src");
PowershellModuleProjectDirectory = BaseSourceLocation + context.Directory("PowershellModule");
PowershellModuleCsproj = PowershellModuleProjectDirectory + context.File("PowershellModule.csproj");
PowershellModuleOutputDir = context.Directory("../") + context.Directory("output") + context.Directory("PowershellModule");
var buildScriptDirectory = context.Directory("./Scripts");
CreateModuleManifestScript = buildScriptDirectory + context.File("CreateModuleManifest.ps1");
BuildSuffix = context.Configuration.GetValue(nameof(BuildSuffix)) ?? "pre-release";
BuildSuffix = context.Configuration.GetValue(nameof(BuildSuffix));
DisableCommitHash = context.Configuration.GetBoolValue(nameof(DisableCommitHash));
}
}

View file

@ -1,48 +0,0 @@
using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;
namespace Build;
public class Helpers
{
/// <summary>
/// Returns a formatted version of the assembly that contains PowerShell cmdlets
/// </summary>
/// <param name="powershelModuleOutputLocation"></param>
/// <returns></returns>
public static string GetPowershellModuleVersion(string powershelModuleOutputLocation)
{
// When the project name changes, this dll will also need to be updated
var moduleVersionInfo = FileVersionInfo.GetVersionInfo(Path.Combine(powershelModuleOutputLocation, "PowershellModule.dll"));
// It's (almost) impossible to not have a product version tag here. There is a reason why the implementation
// returns a string? but I can't find it and I don't really care too much. If we have null return an empty string
// and append no version to the archive bundle.
if (moduleVersionInfo.ProductVersion == null)
{
return string.Empty;
}
var versionRegex = new Regex(@"(\d+\.\d+\.\d+)(?:\-?([\w\-]+)\+?(\w+)?)?");
var match = versionRegex.Match(moduleVersionInfo.ProductVersion);
if (match.Success)
{
// If we have 4 groups, we've got a version number, suffix, and commit hash, so we return the first 2 as is
// and the commit has capped to 8 characters
if (match.Groups.Count == 4)
{
return $"{match.Groups[1]}-{match.Groups[2]}+{match.Groups[3].Value.Substring(0, 8)}";
}
if (match.Groups.Count == 3)
{
return $"{match.Groups[1]}-{match.Groups[2]}";
}
return $"{match.Groups[1]}";
}
return string.Empty;
}
}

View file

@ -8,6 +8,8 @@ public static class Program
{
return new CakeHost()
.UseContext<BuildContext>()
// Uncomment this if you don't want to set the suffix via the run profile program arguments
//.UseCakeSetting(nameof(BuildContext.BuildSuffix), "<your suffix here>")
.Run(args);
}
}

View file

@ -1,4 +1,4 @@
param (
param (
[string]$powershellModuleFileLocation,
[string]$guid,
[string]$author,

View file

@ -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;

View file

@ -1,4 +1,4 @@
using Cake.Common;
using Cake.Common;
using Cake.Common.Tools.DotNet;
using Cake.Core.Diagnostics;
using Cake.Frosting;

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using Cake.Core.Diagnostics;
@ -15,8 +15,8 @@ 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
// TODO: 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
{
PowershellModuleFileLocation = $"{context.PowershellModuleOutputDir}/{powershellModuleName}.psd1",

View file

@ -1,5 +1,4 @@
using System;
using System.Diagnostics;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Text.RegularExpressions;
@ -80,7 +79,7 @@ public class CreateBundleArchiveTask : FrostingTask<BuildContext>
context.Log.Information($"Bundle files copied");
GenerateModuleImportScript(moduleFolderLocation, context);
var moduleVersionForFilename = Helpers.GetPowershellModuleVersion(powershelModuleOutputLocation);
var moduleVersionForFilename = GetPowershellModuleVersion(powershelModuleOutputLocation);
CreateBundleZip(bundleRootLocation, moduleFolderLocation, moduleVersionForFilename, context);
}
@ -133,4 +132,38 @@ public class CreateBundleArchiveTask : FrostingTask<BuildContext>
ZipFile.CreateFromDirectory(moduleFolderLocation, bundleZipFileLocaiton, CompressionLevel.Fastest, true);
context.Log.Information($"Archive created at '{bundleZipFileLocaiton}'");
}
private static string GetPowershellModuleVersion(string powershelModuleOutputLocation)
{
var moduleVersionInfo = FileVersionInfo.GetVersionInfo(Path.Combine(powershelModuleOutputLocation, "PowershellModule.dll"));
// It's (almost) impossible to not have a product version tag here. There is a reason why the implementation
// returns a string? but I can't find it and I don't really care too much. If we have null return an empty string
// and append no version to the archive bundle.
if (moduleVersionInfo.ProductVersion == null)
{
return string.Empty;
}
var versionRegex = new Regex(@"(\d+\.\d+\.\d+)(?:\-?([\w\-]+)\+?(\w+)?)?");
var match = versionRegex.Match(moduleVersionInfo.ProductVersion);
if (match.Success)
{
// If we have 4 groups, we've got a version number, suffix, and commit hash, so we return the first 2 as is
// and the commit has capped to 8 characters
if (match.Groups.Count == 4)
{
return $"{match.Groups[1]}-{match.Groups[2]}+{match.Groups[3].Value.Substring(0, 8)}";
}
if (match.Groups.Count == 3)
{
return $"{match.Groups[1]}-{match.Groups[2]}";
}
return $"{match.Groups[1]}";
}
return string.Empty;
}
}

View file

@ -1,4 +1,4 @@
using Cake.Core;
using Cake.Core;
using Cake.Core.Diagnostics;
using Cake.Frosting;
@ -7,7 +7,6 @@ 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

View file

@ -1,54 +0,0 @@
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}");
}
}

View file

@ -1,4 +1,4 @@
# Git Repo Registration
# Git Repo Registration
- all commands support `-debug`
- at its simplest level these commands allow you to register a git repo against a simple name, and provide the ability to quickly pushd to a location

View file

@ -1,4 +1,4 @@
using System.Globalization;
using System.Globalization;
using System.Text;
namespace ModuleCore.Calendar;

View file

@ -1,4 +1,4 @@
using SQLite;
using SQLite;
namespace ModuleCore.Database;
@ -26,22 +26,12 @@ public class DatabaseManager
}
}
/// <summary>
/// Runs the given action within a new database connection
/// </summary>
/// <param name="dbAction"></param>
public void InConnection(Action<SQLiteConnection> dbAction)
{
using var conn = new SQLiteConnection(_databaseLocation.FullName);
dbAction(conn);
}
/// <summary>
/// Runs the given func in a new database connection, returning the result
/// </summary>
/// <param name="dbAction"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public T InConnection<T>(Func<SQLiteConnection, T> dbAction)
{
using var conn = new SQLiteConnection(_databaseLocation.FullName);
@ -61,18 +51,6 @@ public class DatabaseManager
return exists ?? false;
}
/// <summary>
/// Deletes the current database file. This will cause any future instance methods to fail on database action if
/// a new instance is not created.
/// <para>
/// This method should be avoided unless calling from a test.
/// </para>
/// </summary>
internal void DeleteDatabase()
{
_databaseLocation.Delete();
}
/// <summary>
/// Removes double dots from the filename and removes the file extension
/// </summary>

View file

@ -1,4 +1,4 @@
<Project>
<Project>
<PropertyGroup>
<VersionPrefix>0.0.1</VersionPrefix>
<VersionSuffix>dev</VersionSuffix>

View file

@ -1,4 +1,4 @@
using System.Collections.Concurrent;
using System.Collections.Concurrent;
using System.Diagnostics;
using ModuleCore.Database;
using ModuleCore.Git.Models;
@ -6,44 +6,28 @@ using SQLite;
namespace ModuleCore.Git;
/// <summary>
/// Manages git repo registration, including creating any registration persistence via a backing <see cref="DatabaseManager"/>
/// </summary>
public class GitRepoRegistrationManager
// TODO: better name for this
public class GitManager
{
private static readonly Lazy<GitRepoRegistrationManager> GitManagerInstance = new(() => new GitRepoRegistrationManager());
private static readonly Lazy<GitManager> GitManagerInstance = new(() => new GitManager());
private static Action<string>? _debugWriterDelegate;
private readonly DatabaseManager _db;
private readonly ConcurrentDictionary<string, InternalGitRegistration> _registrations;
private GitRepoRegistrationManager(string? databaseName = null)
private GitManager()
{
_registrations = new ConcurrentDictionary<string, InternalGitRegistration>();
// Regular usage of this constructor will never pass a database name in. Currently only tests should be hitting
// a code path that has a different database name
_db = new DatabaseManager(databaseName ?? "git.db");
_db = new DatabaseManager("git.db");
InitialiseRegistrations();
}
/// <summary>
/// Returns the current <see cref="GitRepoRegistrationManager"/> instance. If no instance has been created, returns a new instance
/// and then the same instance every call after.
/// </summary>
public static GitRepoRegistrationManager Instance => GitManagerInstance.Value;
public static GitManager Instance => GitManagerInstance.Value;
/// <summary>
/// Always returns a new clean instance of GitManager
/// </summary>
internal static GitRepoRegistrationManager InternalFreshInstance(string databaseName) => new(databaseName);
/// <summary>
/// Deletes the underlying database file.
/// <para>
/// Avoid calling this outside of tests.
/// </para>
/// </summary>
internal void DeleteDatabase() => _db.DeleteDatabase();
internal static GitManager InternalFreshInstance => new();
/// <summary>
/// Creates up any database tables and loads all previously saved git registrations.
@ -134,11 +118,6 @@ public class GitRepoRegistrationManager
});
}
/// <summary>
/// Unregisters a git repo registration by name. If no registration exists an exception will be thrown.
/// </summary>
/// <param name="registrationName"></param>
/// <exception cref="Exception"></exception>
public void UnregisterRepo(string registrationName)
{
_db.InConnection(conn =>
@ -206,13 +185,7 @@ public class GitRepoRegistrationManager
.ToList();
}
/// <summary>
/// Returns the file location for a git repo registration by name.
/// </summary>
/// <param name="registeredName"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public string GetDirectoryForRegisteredRepo(string? registeredName)
public string GetRepo(string? registeredName)
{
if (string.IsNullOrEmpty(registeredName))
{
@ -339,7 +312,7 @@ public class GitRepoRegistrationManager
/// </summary>
public string CurrentBranch => GetCurrentBranch();
// TODO: [#13] Create GitManager to centralise calls to git process
// TODO: not fully decided on if I want this feature or not, but keeping it in for now
private string GetCurrentBranch()
{
var now = DateTime.Now;
@ -390,4 +363,20 @@ public class GitRepoRegistrationManager
return _currentBranch.Trim();
}
}
}
/// <summary>
/// The directory details of the directory returned from git rev-parse --show-toplevel
/// </summary>
public class ParsedGitFolderDetails
{
/// <summary>
/// The full path to the top level folder containing a git repository
/// </summary>
public string Directory { get; init; } = null!;
/// <summary>
/// The last folder name of the directory
/// </summary>
public string Folder { get; init; } = null!;
}

View file

@ -1,21 +1,8 @@
namespace ModuleCore.Git.Models;
namespace ModuleCore.Git.Models;
/// <summary>
/// Details for a registered git repo
/// </summary>
public class GitRegistration
{
/// <summary>
/// Display name for the registration. Can either be the name of the repo retrieved from git, or a user supplied
/// alias
/// </summary>
public required string Name { get; set; }
/// <summary>
/// Location on disk for the top level of the registered git repo. Not guaranteed to exist on disk
/// </summary>
public required string Location { get; set; }
/// <summary>
/// Current branch for the git repo
/// </summary>
public required string CurrentBranch { get; set; }
}

View file

@ -1,17 +0,0 @@
namespace ModuleCore.Git.Models;
/// <summary>
/// The directory details of the directory returned from git rev-parse --show-toplevel
/// </summary>
public class ParsedGitFolderDetails
{
/// <summary>
/// The full path to the top level folder containing a git repository
/// </summary>
public string Directory { get; init; } = null!;
/// <summary>
/// The last folder name of the directory
/// </summary>
public string Folder { get; init; } = null!;
}

View file

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
@ -14,7 +14,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="sqlite-net-pcl"/>
<PackageReference Include="sqlite-net-pcl" />
</ItemGroup>
</Project>

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Globalization;
using System.Management.Automation;
using ModuleCore.Calendar;

View file

@ -1,4 +1,4 @@
<Project>
<Project>
<PropertyGroup>
<VersionPrefix>0.0.1</VersionPrefix>
<VersionSuffix>dev</VersionSuffix>

View file

@ -1,4 +1,4 @@
using System.Management.Automation;
using System.Management.Automation;
using ModuleCore.Git;
using ModuleCore.Git.Models;
@ -13,7 +13,7 @@ public class GetGitRepoRegistrationCommand : PSCmdlet
{
protected override void BeginProcessing()
{
var repos = GitRepoRegistrationManager.Instance.ListRepos();
var repos = GitManager.Instance.ListRepos();
WriteObject(repos);

View file

@ -1,4 +1,4 @@
namespace PowershellModule.Git.Commands;
namespace PowershellModule.Git.Commands;
public class GitCommands
{

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Management.Automation;
using ModuleCore.Git;
@ -17,19 +17,19 @@ public sealed class NewGitRepoRegistrationCommand : PSCmdlet
{
try
{
GitRepoRegistrationManager.SetDebugWriter(WriteDebug);
GitManager.SetDebugWriter(WriteDebug);
// Test that we're in a git repo first. If we aren't (or git isn't available), this method will throw
// so we don't need to handle for null (yet).
var repoFolder = GitRepoRegistrationManager.IsGitRepo(SessionState.Path.CurrentLocation.Path);
var repoFolder = GitManager.IsGitRepo(SessionState.Path.CurrentLocation.Path);
if (string.IsNullOrWhiteSpace(Name))
{
WriteDebug("No name given for registration, defaulting to git folder root.");
}
GitRepoRegistrationManager.Instance.RegisterRepo(repoFolder.Directory, Name ?? repoFolder.Folder);
GitRepoRegistrationManager.ClearDebugWriter();
GitManager.Instance.RegisterRepo(repoFolder.Directory, Name ?? repoFolder.Folder);
GitManager.ClearDebugWriter();
base.BeginProcessing();
}

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Management.Automation;
using ModuleCore.Git;
@ -17,21 +17,21 @@ public class RemoveGitRepoRegistrationCommand : PSCmdlet
{
try
{
GitRepoRegistrationManager.SetDebugWriter(WriteDebug);
GitManager.SetDebugWriter(WriteDebug);
// If we aren't given a value for the Name argument, default behaviour is to attempt to remove a registration
// by the current git repo folder name for the current location.
// If we have a name, don't bother testing for a git repo, just attempt to remove the registration by name
// regardless of where we're being called from
var registrationNameToRemove = string.IsNullOrEmpty(Name)
? GitRepoRegistrationManager.IsGitRepo(SessionState.Path.CurrentLocation.Path).Folder
? GitManager.IsGitRepo(SessionState.Path.CurrentLocation.Path).Folder
: Name;
GitRepoRegistrationManager.Instance.UnregisterRepo(registrationNameToRemove);
GitManager.Instance.UnregisterRepo(registrationNameToRemove);
// Removing a registration works similar to registering a new one - we either remove by exact name, or by
// the folder if no name is given (so a user can remove a registration from a git repo they're currently in)
GitRepoRegistrationManager.ClearDebugWriter();
GitManager.ClearDebugWriter();
base.BeginProcessing();
}

View file

@ -1,4 +1,4 @@
using System.Management.Automation;
using System.Management.Automation;
using ModuleCore.Git;
namespace PowershellModule.Git.Commands;
@ -21,7 +21,7 @@ public class ShowGitRepoRegistrationCommand : PSCmdlet
protected override void BeginProcessing()
{
var location = GitRepoRegistrationManager.Instance.GetDirectoryForRegisteredRepo(Name);
var location = GitManager.Instance.GetRepo(Name);
// By default instead of doing the same as cd, we instead do pushd so a user can popd straight back to where
// they came from.

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Management.Automation;
using System.Management.Automation.Provider;

View file

@ -1,4 +1,4 @@
using System.Management.Automation;
using System.Management.Automation;
namespace PowershellModule.Git;

View file

@ -1,4 +1,4 @@
<#
<#
.SYNOPSIS
Removes all non-core files from build output
#>

View file

@ -1,25 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<AssemblyName>PowershellModule</AssemblyName>
<LangVersion>latestmajor</LangVersion>
<Nullable>enable</Nullable>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<AssemblyName>PowershellModule</AssemblyName>
<LangVersion>latestmajor</LangVersion>
<Nullable>enable</Nullable>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="PowerShellStandard.Library">
<PrivateAssets>All</PrivateAssets>
</PackageReference>
<PackageReference Include="System.Management.Automation"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="PowerShellStandard.Library" >
<PrivateAssets>All</PrivateAssets>
</PackageReference>
<PackageReference Include="System.Management.Automation" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ModuleCore\ModuleCore.csproj"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ModuleCore\ModuleCore.csproj" />
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="pwsh.exe -file &quot;$(ProjectDir)PostBuild.ps1&quot; $(TargetDir)"/>
</Target>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="pwsh.exe -file &quot;$(ProjectDir)PostBuild.ps1&quot; $(TargetDir)" />
</Target>
</Project>

View file

@ -1,4 +1,4 @@
using ModuleCore.Calendar;
using ModuleCore.Calendar;
using ModuleTests.Calendar.TestData;
using System.Globalization;

View file

@ -1,4 +1,4 @@
using System.Text;
using System.Text;
using ModuleCore.Calendar;
namespace ModuleTests.Calendar;

View file

@ -1,4 +1,4 @@
using System.Text;
using System.Text;
using ModuleCore.Calendar;
namespace ModuleTests.Calendar;

View file

@ -1,4 +1,4 @@
namespace ModuleTests.Calendar.TestData;
namespace ModuleTests.Calendar.TestData;
public class CalendarTestDates : TestDataEnumerator<DateTime>
{

View file

@ -1,4 +1,4 @@
namespace ModuleTests.Calendar.TestData;
namespace ModuleTests.Calendar.TestData;
public class CultureCodeTestData : TestDataEnumerator<string>
{

View file

@ -1,4 +1,4 @@
┌─────────────────────────────────────────┐
┌─────────────────────────────────────────┐
│ June 2026 │
├─────┬─────┬─────┬─────┬─────┬─────┬─────┤
│ Mon │ Tue │ Wed │ Thu │ Fri │ Sat │ Sun │

View file

@ -1,4 +1,4 @@
┌─────────────────────────────────────────┐
┌─────────────────────────────────────────┐
│ July 2026 │
├─────┬─────┬─────┬─────┬─────┬─────┬─────┤
│ Mon │ Tue │ Wed │ Thu │ Fri │ Sat │ Sun │

View file

@ -1,4 +1,4 @@
┌─────────────────────────────────────────┐
┌─────────────────────────────────────────┐
│ August 2026 │
├─────┬─────┬─────┬─────┬─────┬─────┬─────┤
│ Mon │ Tue │ Wed │ Thu │ Fri │ Sat │ Sun │

View file

@ -1,4 +1,4 @@
┌──────────────────────────────────┐
┌──────────────────────────────────┐
│ srpen 2026 │
├────┬────┬────┬────┬────┬────┬────┤
│ po │ út │ st │ čt │ pá │ so │ ne │

View file

@ -1,4 +1,4 @@
┌─────────────────────────────────────────┐
┌─────────────────────────────────────────┐
│ august 2026 │
├─────┬─────┬─────┬─────┬─────┬─────┬─────┤
│ man │ tir │ ons │ tor │ fre │ lør │ søn │

View file

@ -1,4 +1,4 @@
┌─────────────────────────────────────────┐
┌─────────────────────────────────────────┐
│ August 2026 │
├─────┬─────┬─────┬─────┬─────┬─────┬─────┤
│ Mon │ Tue │ Wed │ Thu │ Fri │ Sat │ Sun │

View file

@ -1,4 +1,4 @@
┌────────────────────────────────────────────────┐
┌────────────────────────────────────────────────┐
│ agosto 2026 │
├──────┬──────┬──────┬──────┬──────┬──────┬──────┤
│ lun. │ mar. │ mié. │ jue. │ vie. │ sáb. │ dom. │

View file

@ -1,4 +1,4 @@
┌────────────────────────────────────────────────┐
┌────────────────────────────────────────────────┐
│ août 2026 │
├──────┬──────┬──────┬──────┬──────┬──────┬──────┤
│ lun. │ mar. │ mer. │ jeu. │ ven. │ sam. │ dim. │

View file

@ -1,4 +1,4 @@
┌──────────────────────────────────┐
┌──────────────────────────────────┐
│ augustus 2026 │
├────┬────┬────┬────┬────┬────┬────┤
│ ma │ di │ wo │ do │ vr │ za │ zo │

View file

@ -1,4 +1,4 @@
┌───────────────────────────────────────────────────────┐
┌───────────────────────────────────────────────────────┐
│ ఆగస్టు 2026 │
├───────┬───────┬───────┬───────┬───────┬───────┬───────┤
│ సోమ │ మంగళ │ బుధ │ గురు │ శుక్ర │ శని │ ఆది │

View file

@ -1,4 +1,4 @@
┌─────────────────────────────────────────┐
┌─────────────────────────────────────────┐
│ August 2026 │
├─────┬─────┬─────┬─────┬─────┬─────┬─────┤
│ Mon │ Tue │ Wed │ Thu │ Fri │ Sat │ Sun │

View file

@ -1,4 +1,4 @@
Double-wide characters will display incorrectly but still tested until rendering can be improved
Double-wide characters will display incorrectly but still tested until rendering can be improved
┌─────────────────────────────────────────┐
│ August 2026 │
├─────┬─────┬─────┬─────┬─────┬─────┬─────┤

View file

@ -1,4 +1,4 @@
Emojis will display incorrectly in text but output correctly in terminals with UTF-8 encoding.
Emojis will display incorrectly in text but output correctly in terminals with UTF-8 encoding.
┌─────────────────────────────────────────┐
│ August 2026 │
├─────┬─────┬─────┬─────┬─────┬─────┬─────┤

View file

@ -1,4 +1,4 @@
┌──────────────────────────────────────────────────────────────┐
┌──────────────────────────────────────────────────────────────┐
│ August 2026 │
├────────┬────────┬────────┬────────┬────────┬────────┬────────┤
│ Mon │ Tue │ Wed │ Thu │ Fri │ Sat │ Sun │

View file

@ -1,4 +1,4 @@
┌─────────────────────────────────────────┐
┌─────────────────────────────────────────┐
│ August 2026 │
├─────┬─────┬─────┬─────┬─────┬─────┬─────┤
│ Mon │ Tue │ Wed │ Thu │ Fri │ Sat │ Sun │

View file

@ -1,4 +1,4 @@
Start Day: Sunday
Start Day: Sunday
┌─────────────────────────────────────────┐
│ August 2026 │
├─────┬─────┬─────┬─────┬─────┬─────┬─────┤

View file

@ -1,10 +1,10 @@
using System.Text;
using System.Text;
using ModuleCore.Calendar;
using ModuleCore.Git;
using ModuleTests.Git.TestData;
namespace ModuleTests.Git;
// TODO: [#12] GitRepoRegistration tests should reset database at start of each test
public class AddRegistrationTests
{
private static readonly VerifySettings Settings;
@ -23,60 +23,43 @@ public class AddRegistrationTests
public Task BasicRepoRegistration((int testId, string path) testData)
{
Settings.UseFileName($"{nameof(BasicRepoRegistration)}_{testData.testId}");
var gitManager = GitRepoRegistrationManager.InternalFreshInstance(nameof(BasicRepoRegistration));
try
{
var repoRegistration = gitManager.RegisterRepo("Test:/some/test/repo", testData.path);
var sb = new StringBuilder();
sb.AppendLine($"Attempted to register: {testData.path}")
.AppendLine($"Registration result: {repoRegistration}");
var gitManager = GitManager.InternalFreshInstance;
return Verify(sb, Settings);
}
finally
{
gitManager.DeleteDatabase();
}
var repoRegistration = gitManager.RegisterRepo("Test:/some/test/repo", testData.path);
var sb = new StringBuilder();
sb.AppendLine($"Attempted to register: {testData.path}")
.AppendLine($"Registration result: {repoRegistration}");
return Verify(sb, Settings);
}
[Fact]
public void RepoRegistrationWithEmptyName()
{
Settings.UseFileName(nameof(RepoRegistrationWithEmptyName));
var gitManager = GitRepoRegistrationManager.InternalFreshInstance(nameof(RepoRegistrationWithEmptyName));
var gitManager = GitManager.InternalFreshInstance;
var testRepoAbsolutePath = "Test:/some/test/repo";
try
{
var emptyName = gitManager.RegisterRepo(testRepoAbsolutePath, "");
var emptyName = gitManager.RegisterRepo(testRepoAbsolutePath, "");
Assert.Equal("repo", emptyName);
}
finally
{
gitManager.DeleteDatabase();
}
Assert.Equal("repo", emptyName);
}
[Fact]
public void RepoRegistrationWithNullName()
{
Settings.UseFileName(nameof(RepoRegistrationWithNullName));
var gitManager = GitRepoRegistrationManager.InternalFreshInstance(nameof(RepoRegistrationWithNullName));
var testRepoAbsolutePath = "Test:/some/test/repo";
try
{
// Name is technically not-nullable, but string is a reference type so null can be passed in so we should test
// it regardless
var nullName = gitManager.RegisterRepo(testRepoAbsolutePath, null!);
Assert.Equal("repo", nullName);
}
finally
{
gitManager.DeleteDatabase();
}
var gitManager = GitManager.InternalFreshInstance;
var testRepoAbsolutePath = "Test:/some/test/repo";
// Name is technically not-nullable, but string is a reference type so null can be passed in so we should test
// it regardless
var nullName = gitManager.RegisterRepo(testRepoAbsolutePath, null!);
Assert.Equal("repo", nullName);
}
[Fact]
@ -84,67 +67,51 @@ public class AddRegistrationTests
{
Settings.UseFileName(nameof(RepoRegistrationWithWhitespaceName));
var gitManager = GitRepoRegistrationManager.InternalFreshInstance(nameof(RepoRegistrationWithWhitespaceName));
var gitManager = GitManager.InternalFreshInstance;
var testRepoAbsolutePath = "Test:/some/test/repo";
try
{
var whitespaceName = gitManager.RegisterRepo(testRepoAbsolutePath, " ");
Assert.Equal("repo", whitespaceName);
}
finally
{
gitManager.DeleteDatabase();
}
var whitespaceName = gitManager.RegisterRepo(testRepoAbsolutePath, " ");
Assert.Equal("repo", whitespaceName);
}
[Fact]
public void DuplicateRepoRegistrationShouldFail()
{
Settings.UseFileName(nameof(RepoRegistrationWithWhitespaceName));
var gitManager = GitRepoRegistrationManager.InternalFreshInstance(nameof(RepoRegistrationWithWhitespaceName));
var gitManager = GitManager.InternalFreshInstance;
var testRepoAbsolutePath = "Test:/some/test/repo";
string[] paths = ["test", "nested", "path"];
var names = (NormalSeparator: string.Join(Path.DirectorySeparatorChar, paths), AltSeparator: string.Join(Path.AltDirectorySeparatorChar, paths));
try
{
var firstRegistration = gitManager.RegisterRepo(testRepoAbsolutePath, names.NormalSeparator);
var secondRegistration = gitManager.RegisterRepo(testRepoAbsolutePath, Path.Combine(paths[..1]));
var firstRegistration = gitManager.RegisterRepo(testRepoAbsolutePath, names.NormalSeparator);
// TODO: make nested registrations fail in both directions and test
var secondRegistration = gitManager.RegisterRepo(testRepoAbsolutePath, Path.Combine(paths[..1]));
Assert.Equal(Path.Combine(paths), firstRegistration);
Assert.Equal(Path.Combine(paths[..1]), secondRegistration);
Assert.Equal(Path.Combine(paths), firstRegistration);
Assert.Equal(Path.Combine(paths[..1]), secondRegistration);
Assert.Throws<Exception>(() => gitManager.RegisterRepo(testRepoAbsolutePath, Path.Combine(paths[..1])));
}
finally
{
gitManager.DeleteDatabase();
}
Assert.Throws<Exception>(() => gitManager.RegisterRepo(testRepoAbsolutePath, Path.Combine(paths[..1])));
}
[Fact]
public void DuplicateRepoRegistrationDifferentSlashShouldNotFail()
{
Settings.UseFileName(nameof(RepoRegistrationWithWhitespaceName));
var gitManager = GitRepoRegistrationManager.InternalFreshInstance(nameof(RepoRegistrationWithWhitespaceName));
var gitManager = GitManager.InternalFreshInstance;
var testRepoAbsolutePath = "Test:/some/test/repo";
string[] paths = ["test", "nested", "path"];
var names = (NormalSeparator: string.Join(Path.DirectorySeparatorChar, paths), AltSeparator: string.Join(Path.AltDirectorySeparatorChar, paths));
try
{
var firstRegistration = gitManager.RegisterRepo(testRepoAbsolutePath, names.NormalSeparator);
var secondRegistration = gitManager.RegisterRepo(testRepoAbsolutePath, Path.Combine(paths[..1]));
var differentPathSeparatorRegistration = gitManager.RegisterRepo(testRepoAbsolutePath, names.AltSeparator);
var firstRegistration = gitManager.RegisterRepo(testRepoAbsolutePath, names.NormalSeparator);
// TODO: make nested registrations fail in both directions and test
var secondRegistration = gitManager.RegisterRepo(testRepoAbsolutePath, Path.Combine(paths[..1]));
var differentPathSeparatorRegistration = gitManager.RegisterRepo(testRepoAbsolutePath, names.AltSeparator);
Assert.Equal(Path.Combine(paths), firstRegistration);
Assert.Equal(Path.Combine(paths[..1]), secondRegistration);
Assert.Equal(names.AltSeparator, differentPathSeparatorRegistration);
}
finally
{
gitManager.DeleteDatabase();
}
Assert.Equal(Path.Combine(paths), firstRegistration);
Assert.Equal(Path.Combine(paths[..1]), secondRegistration);
Assert.Equal(names.AltSeparator, differentPathSeparatorRegistration);
}
}

View file

@ -1,4 +1,4 @@
using System.Linq;
using System.Linq;
namespace ModuleTests.Git.TestData;

View file

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>

View file

@ -1,4 +1,4 @@
namespace ModuleTests;
namespace ModuleTests;
public class TestConstants
{

View file

@ -1,4 +1,4 @@
using System.Collections;
using System.Collections;
namespace ModuleTests;