Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: add back src
Browse files Browse the repository at this point in the history
guitarrapc committed Dec 18, 2024
1 parent 154fe49 commit 48007f7
Showing 17 changed files with 1,182 additions and 0 deletions.
34 changes: 34 additions & 0 deletions src/Actions.Tests/Actions.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="FluentAssertions" Version="7.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Actions\Actions.csproj" />
</ItemGroup>

<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>

</Project>
107 changes: 107 additions & 0 deletions src/Actions.Tests/FileExistsCommandTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
using Actions.Commands;

namespace Actions.Tests;

public class FileExistsCommandTest
{
[Fact]
public void SkipEmptyPathTest()
{
var command = new FileExsistsCommand("");
command.Validate();
}

[Fact]
public void FullPathTest()
{
var path = $".tests/{nameof(FileExistsCommandTest)}/{nameof(FullPathTest)}/dummy.nupkg";
var dir = Path.GetDirectoryName(path)!;
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
File.WriteAllText(path, "");

var command = new FileExsistsCommand(path);
command.Validate();
}

[Fact]
public void WildcardFileTest()
{
var dir = $".tests/{nameof(FileExistsCommandTest)}/{nameof(WildcardFileTest)}";
var items = new[] { "foo", "bar", "piyo", "test.txt", "hoge.txt" };
foreach (var item in items)
{
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
File.WriteAllText(Path.Combine(dir, item), "");
}

var command = new FileExsistsCommand($"{dir}/*");
command.Validate();

var command2 = new FileExsistsCommand($"{dir}/foo");
command2.Validate();

var command3 = new FileExsistsCommand($"{dir}/*.txt");
command3.Validate();

var command4 = new FileExsistsCommand($"{dir}/hoge.*");
command4.Validate();
}

[Fact]
public void WildcardDirectoryTest()
{
var dir = $".tests/{nameof(FileExistsCommandTest)}/{nameof(WildcardDirectoryTest)}";
var items = new[] { "foo", "bar", "piyo" };
foreach (var item in items)
{
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
File.WriteAllText(Path.Combine(dir, item), "");
}

var command = new FileExsistsCommand($".tests/{nameof(FileExistsCommandTest)}/*/foo");
command.Validate();

var command2 = new FileExsistsCommand($".tests/{nameof(FileExistsCommandTest)}/{nameof(WildcardDirectoryTest)}/foo");
command2.Validate();

var failCommand = new FileExsistsCommand($".tests/{nameof(FileExistsCommandTest)}/{nameof(WildcardDirectoryTest)}/*/foo");
Assert.Throws<ActionCommandException>(() => failCommand.Validate());
}

[Fact]
public void RecursiveWildcardDirectoryTest()
{
var dir = $".tests/{nameof(FileExistsCommandTest)}/{nameof(RecursiveWildcardDirectoryTest)}";
var items = new[] { "foo", "bar", "piyo" };
foreach (var item in items)
{
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
File.WriteAllText(Path.Combine(dir, item), "");
}

var command = new FileExsistsCommand($".tests/{nameof(FileExistsCommandTest)}/**/foo");
command.Validate();

var command2 = new FileExsistsCommand($".tests/{nameof(FileExistsCommandTest)}/{nameof(WildcardDirectoryTest)}/**/foo");
command2.Validate();
}

[Fact]
public void RecursiveWildcardDirectoryAndFileTest()
{
var dirBase = $".tests/{nameof(FileExistsCommandTest)}/{nameof(RecursiveWildcardDirectoryAndFileTest)}";
var items = new[] { "foo", "bar", "piyo" };
foreach (var item in items)
{
var dir = Path.Combine(dirBase, item);
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
File.WriteAllText(Path.Combine(dir, item), "");
}

var command = new FileExsistsCommand($"{dirBase}/**/*");
command.Validate();

var command2 = new FileExsistsCommand($"{dirBase}/foo/foo");
command2.Validate();
}
}
132 changes: 132 additions & 0 deletions src/Actions.Tests/UpdateVersionCommandTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
using Actions.Commands;
using FluentAssertions;

namespace Actions.Tests;

public class UpdateVersionCommandTest
{
[Fact]
public void UnityUpmTest()
{
var version = "1.0.0";
var path = $".tests/{nameof(UpdateVersionCommandTest)}{nameof(UnityUpmTest)}/package.json";
var contents = """
{
"name": "com.unity.plugin.example",
"version": "1.2.310",
"displayName": "Package Example Plugin",
"description": "This is an example package",
"unity": "2019.1",
"unityRelease": "0b5",
"dependencies": {
"com.unity.example": "1.0.0"
},
"keywords": [
"keyword1",
"keyword2",
"keyword3"
],
"author": {
"name": "Unity",
"email": "[email protected]",
"url": "https://www.unity3d.com"
}
}
""";

var dir = Path.GetDirectoryName(path)!;
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
File.WriteAllText(path, contents);

var command = new UpdateVersionCommand(version, path);
var (before, after) = command.UpdateVersion(false);

after.Should().Be("""
{
"name": "com.unity.plugin.example",
"version": "1.0.0",
"displayName": "Package Example Plugin",
"description": "This is an example package",
"unity": "2019.1",
"unityRelease": "0b5",
"dependencies": {
"com.unity.example": "1.0.0"
},
"keywords": [
"keyword1",
"keyword2",
"keyword3"
],
"author": {
"name": "Unity",
"email": "[email protected]",
"url": "https://www.unity3d.com"
}
}
""");
}

[Fact]
public void GodotPluginTest()
{
var version = "1.0.0";
var path = $".tests/{nameof(UpdateVersionCommandTest)}/{nameof(GodotPluginTest)}/plugin.cfg";
var contents = """
[plugin]
name="Sandbox.Godot"
description="Sample."
author="Cysharp"
version="1.2.310"
language="C-sharp"
script="GodotPlugin.cs"
""";

var dir = Path.GetDirectoryName(path)!;
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
File.WriteAllText(path, contents);

var command = new UpdateVersionCommand(version, path);
var (before, after) = command.UpdateVersion(false);

after.Should().Be("""
[plugin]
name="Sandbox.Godot"
description="Sample."
author="Cysharp"
version="1.0.0"
language="C-sharp"
script="GodotPlugin.cs"
""");
}

[Fact]
public void DirectoryBuildPropsTest()
{
var version = "1.0.0";
var path = $".tests/{nameof(UpdateVersionCommandTest)}/{nameof(DirectoryBuildPropsTest)}/Directory.Build.props";
var contents = """
<Project>
<PropertyGroup>
<VersionPrefix>1.2.310</VersionPrefix>
</PropertyGroup>
</Project>
""";

var dir = Path.GetDirectoryName(path)!;
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
File.WriteAllText(path, contents);

var command = new UpdateVersionCommand(version, path);
var (before, after) = command.UpdateVersion(false);

after.Should().Be("""
<Project>
<PropertyGroup>
<VersionPrefix>1.0.0</VersionPrefix>
</PropertyGroup>
</Project>
""");
}
}
159 changes: 159 additions & 0 deletions src/Actions.Tests/UtilsSedTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
using FluentAssertions;

namespace Actions.Tests;

public class UtilsSedTest
{
private readonly string _dummyMultilineText;

public UtilsSedTest()
{
_dummyMultilineText = """
1 foobar
2 abcde
3 piyopiyo
4 okonomiyaki
5 takoyaki
""".NormalizeEol();
}

[Fact]
public void CharacterReplaceTest()
{
var input = @"Hello world!";
var (_, after) = Utils.Sed.Replace(input, "world", "Japan");

after.Should().Be("Hello Japan!");
}

[Fact]
public void CharacterChainReplaceTest()
{
var input = @"/a/b/c";
var (_, after) = Utils.Sed.Replace(input, "a", "A");
var (_, after2) = Utils.Sed.Replace(after, "b", "B");
var (_, after3) = Utils.Sed.Replace(after2, "c", "C");

after.Should().Be("/A/b/c");
after2.Should().Be("/A/B/c");
after3.Should().Be("/A/B/C");
}

[Fact]
public void CharacterInsertTest()
{
var input = @"ABC DEF";
var (_, after) = Utils.Sed.Replace(input, "ABC", "ABCD");

after.Should().Be("ABCD DEF");
}

[Fact]
public void CharacterDeleteTest()
{
var input = @"ABC DEF";
var (_, after) = Utils.Sed.Replace(input, "ABC", "");

after.Should().Be(" DEF");
}

[Fact]
public void SentenceInsertTest()
{
var input = _dummyMultilineText;

// insert sentence before the line contains `abcde`
var (_, after) = Utils.Sed.Replace(input, $"^(.*?abcde.*?$)", "foo\n$1");

after.Should().Be("""
1 foobar
foo
2 abcde
3 piyopiyo
4 okonomiyaki
5 takoyaki
""".NormalizeEol());
}

[Fact]
public void SentenceDeleteTest()
{
var input = _dummyMultilineText;
// delete the line contains `abcde`, but keep brank line as is
var (_, after) = Utils.Sed.Replace(input, $"^(.*?abcde.*?$)", "");

after.Should().Be("""
1 foobar
3 piyopiyo
4 okonomiyaki
5 takoyaki
""".NormalizeEol());
}

[Fact]
public void SentenceDeleteCompactionTest()
{
var input = _dummyMultilineText;

// delete the line contains `abcde`, then compaction deleted line.
var (_, after) = Utils.Sed.Replace(input, $"^(.*?abcde.*?$).*(\r?\n)?", "");

after.Should().Be("""
1 foobar
3 piyopiyo
4 okonomiyaki
5 takoyaki
""".NormalizeEol());
}

[Fact]
public void NoWriteBackTest()
{
var path = $"{nameof(NoWriteBackTest)}.txt";
if (File.Exists(path)) File.Delete(path);

var input = _dummyMultilineText;
File.WriteAllText(path, input);

// delete the line contains `abcde`, then compaction deleted line.
var (before, after) = Utils.Sed.Replace(path, $"^(.*?abcde.*?$).*(\r?\n)?", "", false);

after.Should().Be("""
1 foobar
3 piyopiyo
4 okonomiyaki
5 takoyaki
""".NormalizeEol());
File.ReadAllText(path).Should().Be(before);
}

[Fact]
public void WriteBackTest()
{
var path = $"{nameof(WriteBackTest)}.txt";
if (File.Exists(path)) File.Delete(path);

var input = _dummyMultilineText;
File.WriteAllText(path, input);

// delete the line contains `abcde`, then compaction deleted line.
var (_, after) = Utils.Sed.Replace(path, $"^(.*?abcde.*?$).*(\r?\n)?", "", true);

after.Should().Be("""
1 foobar
3 piyopiyo
4 okonomiyaki
5 takoyaki
""".NormalizeEol());
File.ReadAllText(path).Should().Be(after);
}
}

public static class StringExtentions
{
public static string NormalizeEol(this string input)
{
return input.Replace("\r\n", "\n");
}
}
46 changes: 46 additions & 0 deletions src/Actions.Tests/VersioningCommandTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using Actions.Commands;
using FluentAssertions;

namespace Actions.Tests;

public class VersioningCommandTest
{
[Theory]
[InlineData("0.1.0", VersionIncrement.Major, "1.1.0")]
[InlineData("0.1.0", VersionIncrement.Minor, "0.2.0")]
[InlineData("0.1.0", VersionIncrement.Patch, "0.1.1")]
public void VersionIncrementTest(string tag, VersionIncrement versionIncrement, string actual)
{
var command = new VersioningCommand(tag, prefix: "", versionIncrement: versionIncrement, isPrelease: false, prerelease: "");
var versioning = command.Versioning();

versioning.Should().Be(actual);
}

[Theory]
[InlineData("v0.1.0", "v", true, "0.1.1")]
[InlineData("Ver0.1.0", "Ver", true, "0.1.1")]
[InlineData("Ver.0.1.0", "Ver.", true, "0.1.1")]
[InlineData("v0.1.0", "v", false, "v0.1.1")]
[InlineData("Ver0.1.0", "Ver", false, "Ver0.1.1")]
[InlineData("Ver.0.1.0", "Ver.", false, "Ver.0.1.1")]
public void VersionPrefixTest(string tag, string prefix, bool withoutPrefix, string actual)
{
var command = new VersioningCommand(tag, prefix: prefix, versionIncrement: VersionIncrement.Patch, isPrelease: false, prerelease: "");
var versioning = command.Versioning(withoutPrefix);

versioning.Should().Be(actual);
}

[Theory]
[InlineData("0.1.0", "", "0.1.1")]
[InlineData("0.1.0", "alpha", "0.1.1-alpha")]
[InlineData("0.1.0", "preview", "0.1.1-preview")]
public void VersionPrereleaseTest(string tag, string prerelease, string actual)
{
var command = new VersioningCommand(tag, prefix: "", versionIncrement: VersionIncrement.Patch, isPrelease: true, prerelease: prerelease);
var versioning = command.Versioning();

versioning.Should().Be(actual);
}
}
17 changes: 17 additions & 0 deletions src/Actions/Actions.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="ConsoleAppFramework" Version="5.3.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

</Project>
15 changes: 15 additions & 0 deletions src/Actions/CommandEnums.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace Actions;

public enum VersionIncrement
{
Major,
Minor,
Patch,
//Prerelease, // TODO: how to calculate count since last tag?
}

public enum OutputFormatType
{
Console,
GitHubActions,
}
79 changes: 79 additions & 0 deletions src/Actions/Commands/CreateDummyCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
namespace Actions.Commands;

public class CreateDummyCommand
{
public void CreateDummy(string basePath)
{
DummyVersionFiles(basePath);
DummyAssetFiles(Path.Combine(basePath, "downloads/"));
}

private void DummyVersionFiles(string basePath)
{
var upm = ("package.json", """
{
"name": "com.unity.plugin.example",
"version": "1.2.310",
"displayName": "Package Example Plugin",
"description": "This is an example package",
"unity": "2019.1",
"unityRelease": "0b5",
"dependencies": {
"com.unity.example": "1.0.0"
},
"keywords": [
"keyword1",
"keyword2",
"keyword3"
],
"author": {
"name": "Unity",
"email": "unity@example.com",
"url": "https://www.unity3d.com"
}
}
""");
var godot = ("plugin.cfg", """
[plugin]
name="Sandbox.Godot"
description="Sample."
author="Cysharp"
version="1.2.310"
language="C-sharp"
script="GodotPlugin.cs"
""");
var directoryBuildProps = ("Directory.Build.props", """
<Project>
<PropertyGroup>
<VersionPrefix>1.2.310</VersionPrefix>
</PropertyGroup>
</Project>
""");

Console.WriteLine($"{nameof(DummyVersionFiles)}");
foreach (var (file, contents) in new[] { upm, godot, directoryBuildProps })
{
var path = Path.Combine(basePath, file);
if (!Directory.Exists(basePath))
Directory.CreateDirectory(basePath);

Console.WriteLine($"- {path} ...");
File.WriteAllText(path, contents);
}
}

private void DummyAssetFiles(string basePath)
{
Console.WriteLine($"{nameof(DummyAssetFiles)}");
var items = new[] { "foo", "bar", "piyo", "foo.nupkg", "bar.nupkg" };
foreach (var item in items)
{
var path = Path.Combine(basePath, item);
if (!Directory.Exists(basePath))
Directory.CreateDirectory(basePath);

Console.WriteLine($"- {path} ...");
File.WriteAllText(path, "");
}
}
}
38 changes: 38 additions & 0 deletions src/Actions/Commands/FileExsistsCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using Actions.Utils;

namespace Actions.Commands;

public class FileExsistsCommand(string pathPattern, bool allowMissing = false)
{
public void Validate()
{
// do nothing for empty input
if (string.IsNullOrWhiteSpace(pathPattern))
return;

var pattern = GlobFiles.Normalize(pathPattern);

// Handle glob path pattern.
// /foo/bar/**/*
// /foo/bar/*.txt
if (GlobFiles.IsGlobPattern(pattern))
{
if (!GlobFiles.Exists(pattern))
{
// allow file not exists option
if (allowMissing)
return;

throw new ActionCommandException(pattern, new FileNotFoundException(pathPattern));
}
return;
}

// Specified full path pattern...
// /foo/bar/piyo/poyo.txt
if (!File.Exists(pattern))
{
throw new ActionCommandException(pattern, new FileNotFoundException(pathPattern));
}
}
}
110 changes: 110 additions & 0 deletions src/Actions/Commands/UpdateVersionCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
using Actions.Utils;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace Actions.Commands;

public record struct UpdateVersionCommandResult(string Before, string After);
public class UpdateVersionCommand(string version, string path)
{
public UpdateVersionCommandResult UpdateVersion(bool dryRun)
{
if (!File.Exists(path)) throw new FileNotFoundException(path);

var writeBack = !dryRun;
var fileName = Path.GetFileName(path);
return fileName switch
{
// UPM
"package.json" => HandleUpm(writeBack),
// Godot
"plugin.cfg" => HandleGodot(writeBack),
// .NET
"Directory.Build.props" => HandleDirectoryBuildProps(writeBack),
// Other
_ => throw new NotImplementedException(fileName),
};
}

private UpdateVersionCommandResult HandleUpm(bool writeBack)
{
// replace
var (before, after) = Sed.Replace(path, @"""version"":\s*""(.*?)""", $@"""version"": ""{version}""", writeBack);

// validate
Validate(after, version);

return new UpdateVersionCommandResult(before, after);

static void Validate(string contents, string version)
{
var packageJson = JsonSerializer.Deserialize<UpmPackageJson>(contents) ?? throw new ActionCommandException($"UPM package.json updated, but failed to load as valid JSON. contents: {contents}");
if (packageJson.Version != version)
throw new ActionCommandException($"UPM package.json updated, but version miss-match. actual {packageJson?.Version}, expected {version}");
}
}

private UpdateVersionCommandResult HandleGodot(bool writeBack)
{
// replace
var (before, after) = Sed.Replace(path, @"(version=)""(.*?)""", $@"$1""{version}""", writeBack);

// validate
Validate(after, version);

return new UpdateVersionCommandResult(before, after);

static void Validate(string contents, string version)
{
var lines = contents.Split("\n");
Span<Range> destination = stackalloc Range[2];
foreach (var line in lines)
{
// find the line befin with "version=", then split with = to get version
if (!line.StartsWith("version="))
continue;

var span = line.AsSpan();
var range = span.Split(destination, '=', StringSplitOptions.TrimEntries);
if (range != 2)
continue;

// validate version is expceted
var versionValue = span[destination[1]].ToString();
if (versionValue != $"\"{version}\"")
{
throw new ActionCommandException($"Godot plugin.cfg updated, but version miss-match. actual {versionValue}, expected {version}");
}
return;
}
throw new ActionCommandException($"Godot plugin.cfg updated, but version key not found.");
}
}

private UpdateVersionCommandResult HandleDirectoryBuildProps(bool writeBack)
{
// replace
var (before, after) = Sed.Replace(path, @"<VersionPrefix>.*</VersionPrefix>", $@"<VersionPrefix>{version}</VersionPrefix>", writeBack);

// validate
Validate(after, version);

return new UpdateVersionCommandResult(before, after);

static void Validate(string contents, string version)
{
var xmlDoc = new System.Xml.XmlDocument();
xmlDoc.LoadXml(contents);
var versionPrefixNode = xmlDoc.SelectSingleNode("//VersionPrefix") ?? throw new ActionCommandException($"Directory.Build.props updated, but VersionPrefix key not found.");
if (versionPrefixNode.InnerText != version)
throw new ActionCommandException($"Directory.Build.props updated, but version miss-match. actual {versionPrefixNode.InnerText}, expected {version}");

}
}

private record UpmPackageJson
{
[JsonPropertyName("version")]
public required string Version { get; set; }
}
}
67 changes: 67 additions & 0 deletions src/Actions/Commands/VersioningCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
namespace Actions.Commands;

public class VersioningCommand(string tag, string prefix, VersionIncrement versionIncrement, bool isPrelease, string prerelease)
{
/// <summary>
/// Handling versioning
/// </summary>
/// <returns></returns>
public string Versioning(bool withoutPrefix = false)
{
var version = GetNormalizedVersion(tag, prefix);
var increment = IncrementVersion(version, versionIncrement);
var format = FormatVersion(increment, prefix, isPrelease, prerelease, withoutPrefix);
return format;
}

/// <summary>
/// Normalize tag. Tag may contains prefix, remove prefix and retrun version.
/// </summary>
/// <param name="tag"></param>
/// <param name="prefix"></param>
/// <returns></returns>
/// <exception cref="ActionCommandException"></exception>
private static Version GetNormalizedVersion(string tag, string prefix)
{
if (string.IsNullOrEmpty(tag)) throw new ActionCommandException("tag missing");
if (tag.StartsWith(prefix, StringComparison.Ordinal))
{
var span = tag.AsSpan();
var substring = span[prefix.Length..];
return Version.Parse(substring);
}
else
{
return Version.Parse(tag);
}
}

/// <summary>
/// Increment version for specific place
/// </summary>
/// <param name="version"></param>
/// <param name="versionIncrement"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
private static Version IncrementVersion(Version version, VersionIncrement versionIncrement) => versionIncrement switch
{
VersionIncrement.Major => new Version(version.Major + 1, version.Minor, version.Build),
VersionIncrement.Minor => new Version(version.Major, version.Minor + 1, version.Build),
VersionIncrement.Patch => new Version(version.Major, version.Minor, version.Build + 1),
_ => throw new NotImplementedException(nameof(versionIncrement)),
};

/// <summary>
/// Format version
/// </summary>
/// <param name="version"></param>
/// <param name="prefix"></param>
/// <param name="isPrelease"></param>
/// <param name="prerelease"></param>
/// <returns></returns>
private static string FormatVersion(Version version, string prefix, bool isPrelease, string prerelease, bool withoutPrefix)
{
var preReleaseSuffix = isPrelease && prerelease != "" ? $"-{prerelease}" : "";
return withoutPrefix ? $"{version}{preReleaseSuffix}" : $"{prefix}{version}{preReleaseSuffix}";
}
}
163 changes: 163 additions & 0 deletions src/Actions/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
using Actions;
using Actions.Commands;
using Actions.Utils;
using ConsoleAppFramework;

var app = ConsoleApp.Create();
app.Add<ActionsBatch>();
app.Run(args);

namespace Actions
{
public class ActionsBatch
{
#pragma warning disable CA1822 // Mark members as static

private bool _verbose;

/// <summary>Get version string from tag</summary>
/// <param name="tag"></param>
/// <param name="prefix"></param>
/// <param name="versionIncrement"></param>
/// <param name="isPrelease"></param>
/// <param name="prerelease"></param>
/// <param name="outputFormat"></param>
[Command("versioning")]
public void Versioning(string tag, string prefix = "", VersionIncrement versionIncrement = VersionIncrement.Patch, bool isPrelease = false, string prerelease = "preview", bool withoutPrefix = false, OutputFormatType outputFormat = OutputFormatType.Console)
{
var command = new VersioningCommand(tag, prefix, versionIncrement, isPrelease, prerelease);
var versioning = command.Versioning(withoutPrefix);

var output = OutputFormat("version", versioning, outputFormat);
WriteLog(output);
}

/// <summary>
/// Update Version for specified path
/// </summary>
/// <param name="version"></param>
/// <param name="path"></param>
/// <param name="dryRun"></param>
[Command("update-version")]
public void UpdateVersion(string version, string path, bool dryRun)
{
WriteLog($"Update begin, {path} ...");
if (string.IsNullOrWhiteSpace(path))
{
WriteLog("Empty path detected, skip execution.");
return;
}

using (var githubGroup = new GitHubActionsGroupLogger("Before"))
WriteLog(File.ReadAllText(path));

var command = new UpdateVersionCommand(version, path);
var result = command.UpdateVersion(dryRun);

using (var githubGroup = new GitHubActionsGroupLogger("After"))
WriteLog(result.After);

WriteLog($"Completed ...");
}

/// <summary>
/// Validate specified path contains file
/// </summary>
/// <param name="pathPattern"></param>
/// <param name="verbose"></param>
[Command("validate-file-exists")]
public void ValidateFileExists(string pathPattern, bool verbose)
{
SetOptions(verbose);

WriteLog($"Validating path, {pathPattern} ...");
WriteVerbose($"UTF8: {DebugTools.ToUtf8Base64String(pathPattern)}");
if (string.IsNullOrWhiteSpace(pathPattern))
{
WriteLog("Empty path detected, skip execution.");
return;
}

var command = new FileExsistsCommand(pathPattern);
command.Validate();

WriteLog($"Completed ...");
}

/// <summary>
/// Validate specified path contains nuget packages
/// </summary>
/// <param name="pathPattern"></param>
/// <param name="verbose"></param>
[Command("validate-nupkg-exists")]
public void ValidateNupkgExists(string pathPattern, bool verbose)
{
SetOptions(verbose);
var fileName = Path.GetFileName(pathPattern);
var allowMissing = Path.GetExtension(fileName) == ".snupkg";

WriteLog($"Validating path, {pathPattern} ...");
WriteVerbose($"UTF8: {DebugTools.ToUtf8Base64String(pathPattern)}");
if (string.IsNullOrWhiteSpace(pathPattern))
{
WriteLog("Empty path detected, skip execution.");
return;
}
if (allowMissing)
{
WriteLog(".snupkg detected, allow missing file.");
}

var command = new FileExsistsCommand(pathPattern, allowMissing);
command.Validate();

WriteLog($"Completed ...");
}

/// <summary>
/// Create dummy files
/// </summary>
/// <param name="basePath"></param>
[Command("create-dummy")]
public void CreateDummy(string basePath)
{
WriteLog($"Creating dummy files, under {basePath} ...");

var command = new CreateDummyCommand();
command.CreateDummy(basePath);

WriteLog($"Completed ...");
}

#pragma warning restore CA1822 // Mark members as static

private static string OutputFormat(string key, string value, OutputFormatType format) => format switch
{
OutputFormatType.Console => value,
OutputFormatType.GitHubActions => $"{key}={value}",
_ => throw new NotImplementedException(nameof(format)),
};

private void SetOptions(bool verbose)
{
_verbose = verbose;
}

void WriteLog(string value)
{
Console.WriteLine($"[{DateTime.Now:s}] {value}");
}

void WriteVerbose(string value)
{
if (_verbose)
{
Console.WriteLine($"[{DateTime.Now:s}] {value}");
}
}
}

public class ActionCommandException(string message, Exception? innterException = null) : Exception(message, innterException)
{
}
}
81 changes: 81 additions & 0 deletions src/Actions/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
{
"profiles": {
"help": {
"commandName": "Project",
"commandLineArgs": "--help"
},
// versioning
"versioning (help)": {
"commandName": "Project",
"commandLineArgs": "versioning --help"
},
"versioning (major)": {
"commandName": "Project",
"commandLineArgs": "versioning --tag 0.1.0 --version-increment major"
},
"versioning (minor)": {
"commandName": "Project",
"commandLineArgs": "versioning --tag 0.1.0 --version-increment minor"
},
"versioning (patch)": {
"commandName": "Project",
"commandLineArgs": "versioning --tag 0.1.0"
},
"versioning (patch - prefix)": {
"commandName": "Project",
"commandLineArgs": "versioning --tag v0.1.0 --prefix v"
},
"versioning (patch - github actions)": {
"commandName": "Project",
"commandLineArgs": "versioning --tag 0.1.0 --output-format GitHubActions"
},
// update-version (run create-dummy before run these)
"update-version (help)": {
"commandName": "Project",
"commandLineArgs": "update-version --help"
},
"update-version (upm)": {
"commandName": "Project",
"commandLineArgs": "update-version --version 1.0.0 --path ./dummy/package.json"
},
"update-version (godot)": {
"commandName": "Project",
"commandLineArgs": "update-version --version 1.0.0 --path ./dummy/plugin.cfg"
},
"update-version (Directory.Build.props)": {
"commandName": "Project",
"commandLineArgs": "update-version --version 1.0.0 --path ./dummy/Directory.Build.props"
},
// validate-file-exsits
"validate-file-exsits (path: foo)": {
"commandName": "Project",
"commandLineArgs": "validate-file-exists --path-pattern ./dummy/downloads/foo"
},
"validate-file-exsits (path: *)": {
"commandName": "Project",
"commandLineArgs": "validate-file-exists --path-pattern ./dummy/downloads/*"
},
"validate-file-exsits (path: */foo)": {
"commandName": "Project",
"commandLineArgs": "validate-file-exists --path-pattern ./dummy/*/foo"
},
"validate-file-exsits (path: **/foo)": {
"commandName": "Project",
"commandLineArgs": "validate-file-exists --path-pattern ./dummy/**/foo"
},
// validate-nupkg-exsits
"validate-nupkg-exsits (path: *.nupkg)": {
"commandName": "Project",
"commandLineArgs": "validate-nupkg-exists --path-pattern ./dummy/downloads/*.nupkg"
},
"validate-nupkg-exsits (path: *.snupkg)": {
"commandName": "Project",
"commandLineArgs": "validate-nupkg-exists --path-pattern ./dummy/downloads/*.snupkg"
},
// create-dummy
"create-dummy (init)": {
"commandName": "Project",
"commandLineArgs": "create-dummy --base-path dummy/"
}
}
}
8 changes: 8 additions & 0 deletions src/Actions/Utils/DebugTools.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using System.Text;

namespace Actions.Utils;

public static class DebugTools
{
public static string ToUtf8Base64String(string input) => string.Join(" ", Encoding.UTF8.GetBytes(input).Select(b => b.ToString("X2")));
}
12 changes: 12 additions & 0 deletions src/Actions/Utils/GitHubActionsGroupLogger.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace Actions.Utils
{
public class GitHubActionsGroupLogger : IDisposable
{
public GitHubActionsGroupLogger(string title)
{
Console.WriteLine($"::group::{title}");
}

public void Dispose() => Console.WriteLine("::endgroup::");
}
}
74 changes: 74 additions & 0 deletions src/Actions/Utils/GlobFiles.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
namespace Actions.Utils;

public static class GlobFiles
{
public static bool IsGlobPattern(string pattern) => pattern.Contains('*') || pattern.Contains('?');
public static bool Exists(string pattern)
{
try
{
var directory = GetParentDirectoryPath(pattern) ?? Directory.GetCurrentDirectory();
var searchPattern = Path.GetFileName(pattern);

if (pattern.Contains("/**/"))
{
return Directory.EnumerateFiles(directory, searchPattern, SearchOption.AllDirectories).Any();
}
if (pattern.Contains("*/"))
{
return EnumerateFileWildcard(pattern, searchPattern, SearchOption.AllDirectories).Any();
}
else
{
return Directory.EnumerateFiles(directory, searchPattern, SearchOption.TopDirectoryOnly).Any();
}
}
catch (Exception ex)
{
throw new ActionCommandException($"Error happen on checking file. Is specified path correct? path: {pattern}", ex);
}
}

/// <summary>
/// Normalize \r\n to \n
/// </summary>
/// <param name="pattern"></param>
/// <returns></returns>
public static string Normalize(string pattern) => pattern.Replace("\\", "/");

private static string GetParentDirectoryPath(string path)
{
var limit = 5;
var current = 0;
var dir = path;
var fileName = Path.GetFileName(dir);
var next = false;
do
{
dir = Path.GetDirectoryName(dir) ?? "";
fileName = Path.GetFileName(dir);
current++;
if (current >= limit)
{
throw new ActionCommandException($"Recursively approaced parent directory but reached limit. {current}/{limit}");
}
next = fileName == "**" || fileName == "*";
} while (next);

return dir;
}

private static IEnumerable<string> EnumerateFileWildcard(string path, string searchPattern, SearchOption searchOption)
{
var fileName = Path.GetFileName(path);
if (fileName == "*")
{
return Directory.GetDirectories(Path.GetDirectoryName(path)!).SelectMany(x => Directory.GetFiles(x, searchPattern, searchOption));
}
else
{
return EnumerateFileWildcard(Path.GetDirectoryName(path)!, searchPattern, searchOption);
}
}

}
40 changes: 40 additions & 0 deletions src/Actions/Utils/Sed.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System.Text.RegularExpressions;

namespace Actions.Utils;

public static class Sed
{
/// <summary>
/// Replace file contents with pattern and replacement.
/// </summary>
/// <param name="path"></param>
/// <param name="pattern"></param>
/// <param name="replacement"></param>
/// <param name="writeBack"></param>
/// <returns></returns>
public static (string before, string after) Replace(string path, string pattern, string replacement, bool writeBack)
{
var input = File.ReadAllText(path);
var result = Replace(input, pattern, replacement);

if (writeBack)
{
File.WriteAllText(path, result.after);
}

return result;
}

/// <summary>
/// Replace contents with pattern and replacement.
/// </summary>
/// <param name="input"></param>
/// <param name="pattern"></param>
/// <param name="replacement"></param>
/// <returns></returns>
public static (string before, string after) Replace(string input, string pattern, string replacement)
{
var updatedContent = Regex.Replace(input, pattern, replacement, RegexOptions.Multiline);
return (input, updatedContent);
}
}

0 comments on commit 48007f7

Please sign in to comment.