diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100644
index 0000000..448c9e3
--- /dev/null
+++ b/.github/pull_request_template.md
@@ -0,0 +1,11 @@
+
+
+## Why is the change needed?
+
+
+
+## What was changed?
+
+
diff --git a/.gitignore b/.gitignore
index 708828b..25e787d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -39,4 +39,7 @@ obj
**/*.DotSettings.user
# JetBrains
-.idea
\ No newline at end of file
+.idea
+
+# Temp file
+temp.md
\ No newline at end of file
diff --git a/AzureLiquid.Preview/PreviewProcess.cs b/AzureLiquid.Preview/PreviewProcess.cs
index 8cbd8f3..95e6d97 100644
--- a/AzureLiquid.Preview/PreviewProcess.cs
+++ b/AzureLiquid.Preview/PreviewProcess.cs
@@ -12,6 +12,11 @@ namespace AzureLiquid.Preview;
///
public class PreviewProcess
{
+ ///
+ /// The argument parser.
+ ///
+ private readonly PreviewProcessArguments _args;
+
///
/// Handles writing console output to private persisted log.
///
@@ -32,6 +37,7 @@ public class PreviewProcess
///
public PreviewProcess()
{
+ _args = new PreviewProcessArguments();
Template = string.Empty;
Content = string.Empty;
Output = "./preview.txt";
@@ -68,10 +74,10 @@ public PreviewProcess()
public string Output { get; set; }
///
- /// Gets a value indicating whether the process should watch for changes to template or content files.
+ /// Gets or sets a value indicating whether the process should watch for changes to template or content files.
///
///
- /// true if should watch; otherwise, false.
+ /// true if the process should watch for changes; otherwise, false.
///
[ExcludeFromCodeCoverage]
private bool ShouldWatch { get; set; }
@@ -87,16 +93,23 @@ public PreviewProcess()
///
/// Start a new instance of the class using the incoming arguments.
///
- /// The process arguments
+ /// The process arguments.
/// A new instance of the class.
[ExcludeFromCodeCoverage]
public static PreviewProcess Create(string[] args)
{
var preview = new PreviewProcess();
- // deepcode ignore XmlInjection: XML is not used by this application, it is passed back to the user, deepcode ignore XXE:
- preview.ParseArguments(args);
- HandleNoArgumentsPassed(args, preview);
+ preview.Template = preview._args.ParsePath(args, "template");
+ preview.Content = preview._args.ParsePath(args, "content");
+ preview.Output = preview._args.ParsePath(args, "output");
+ preview.ShouldWatch = PreviewProcessArguments.HasArgument(args, "watch");
+
+ if (args.Length == 0)
+ {
+ preview.WriteHelpOutput();
+ }
+
if (preview.CanRender)
{
RenderAndWatch(preview);
@@ -109,107 +122,6 @@ public static PreviewProcess Create(string[] args)
return preview;
}
- /// Parses the arguments and sets process options.
- ///
- /// The arguments. Values are expected to be "--template", "--help", "--content", "--output" or
- /// "--watch".
- ///
- private void ParseArguments(string[] args)
- {
- for (var index = 0; index < args.Length; index++)
- {
- var arg = args[index];
- var path = Directory.GetCurrentDirectory();
- ParseTemplate(args, index, arg, path);
- ParseContent(args, index, arg, path);
- ParseOutputResults(args, index, arg, path);
-
- // Switch watch param if needed
- if (IsArgMatch(arg, "watch"))
- {
- ShouldWatch = true;
- }
-
- // Show help info
- if (IsArgMatch(arg, "help"))
- {
- WriteHelpOutput();
- }
- }
- }
-
- ///
- /// Parses the output results file path if specified.
- ///
- /// The passed command arguments.
- /// The parameter index.
- /// The current argument.
- /// The target path.
- private void ParseOutputResults(string[] args, int index, string arg, string path)
- {
- if (!IsArgMatch(arg, "output") || index - 1 >= args.Length)
- {
- return;
- }
-
- try
- {
- Output = Path.GetFullPath(args[index + 1], path);
- }
- catch
- {
- WriteErrorLine($"Invalid output path: {args[index + 1]}");
- }
- }
-
- ///
- /// Parses the incoming content file path if specified.
- ///
- /// The passed command arguments.
- /// The parameter index.
- /// The current argument.
- /// The target path.
- private void ParseContent(string[] args, int index, string arg, string path)
- {
- if (!IsArgMatch(arg, "content") || index - 1 >= args.Length)
- {
- return;
- }
-
- try
- {
- Content = Path.GetFullPath(args[index + 1], path);
- }
- catch
- {
- WriteErrorLine($"Invalid content path: {args[index + 1]}");
- }
- }
-
- ///
- /// Parses the incoming template file path if specified.
- ///
- /// The passed command arguments.
- /// The parameter index.
- /// The current argument.
- /// The target path.
- private void ParseTemplate(string[] args, int index, string arg, string path)
- {
- if (!IsArgMatch(arg, "template") || index - 1 >= args.Length)
- {
- return;
- }
-
- try
- {
- Template = Path.GetFullPath(args[index + 1], path);
- }
- catch
- {
- WriteErrorLine($"Invalid template path: {args[index + 1]}");
- }
- }
-
///
/// Renders the output and watches for changes if specified.
///
@@ -246,19 +158,6 @@ private static void LogMissingFiles(PreviewProcess preview)
preview.LogMessage();
}
- ///
- /// Handles the scenario where no arguments are passed to the application.
- ///
- /// The array of arguments passed to the application.
- /// The instance of to handle the output.
- private static void HandleNoArgumentsPassed(string[] args, PreviewProcess preview)
- {
- if (args.Length == 0)
- {
- preview.WriteHelpOutput();
- }
- }
-
///
/// Writes the help output.
///
@@ -287,19 +186,6 @@ private static void WriteErrorLine(string error)
Console.ForegroundColor = ConsoleColor.White;
}
- ///
- /// Determines whether the argument matches the partial argument key name.
- ///
- /// The argument.
- /// The key.
- ///
- /// true if argument found; otherwise, false.
- ///
- private static bool IsArgMatch(string arg, string key)
- {
- return string.CompareOrdinal(arg, "--" + key) == 0;
- }
-
///
/// Renders the output using the specified properties of the instance.
///
@@ -308,67 +194,99 @@ public string Render()
{
if (!CanRender)
{
- WriteErrorLine("Unable to render as inputs our outputs not found or not specified");
+ WriteErrorLine("Unable to render as inputs or outputs not found or not specified");
return string.Empty;
}
- string content;
- try
+ var content = ReadFileContent(Content);
+ if (string.IsNullOrEmpty(content))
{
- content = File.ReadAllText(Content);
+ return string.Empty;
}
- catch (IOException)
+
+ var template = ReadFileContent(Template);
+ if (string.IsNullOrEmpty(template))
{
- // Lock issue, wait and retry
- Thread.Sleep(TimeSpan.FromSeconds(1));
- return Render();
+ return string.Empty;
}
- string template;
+ var parser = new LiquidParser();
+ return !SetParserContent(parser, content) ? string.Empty : RenderTemplate(parser, template);
+ }
+
+ ///
+ /// Reads the file content.
+ ///
+ /// The file path.
+ /// The operation is being retried.
+ /// The file content.
+ internal string ReadFileContent(string filePath, bool retry = false)
+ {
try
{
- template = File.ReadAllText(Template);
+ return File.ReadAllText(filePath);
}
- catch (IOException)
+ catch
{
// Lock issue, wait and retry
Thread.Sleep(TimeSpan.FromSeconds(1));
- return Render();
- }
+ if (!retry)
+ {
+ return ReadFileContent(filePath, true);
+ }
- var parser = new LiquidParser();
+ LogWarning($"Unable to read file: {filePath}");
+ return string.Empty;
+ }
+ }
- if (Content.ToLowerInvariant().EndsWith(".json"))
+ ///
+ /// Sets the parser content.
+ ///
+ /// The parser.
+ /// The content.
+ ///
+ /// true if the content was set; otherwise, false.
+ ///
+ private bool SetParserContent(LiquidParser parser, string content)
+ {
+ try
{
- try
+ if (Content.ToLowerInvariant().EndsWith(".json"))
{
parser.SetContentJson(content);
}
- catch (Exception e)
- {
- LogWarning(" Unable to read input JSON file", e);
- return string.Empty;
- }
- }
-
- if (Content.ToLowerInvariant().EndsWith(".xml"))
- {
- try
+ else if (Content.ToLowerInvariant().EndsWith(".xml"))
{
parser.SetContentXml(content);
}
- catch (Exception ex)
+ else
{
- LogWarning(" Unable to read input XML file", ex);
- return string.Empty;
+ WriteErrorLine("Unsupported content type");
+ return false;
}
}
+ catch (Exception e)
+ {
+ LogWarning("Unable to set parser content", e);
+ return false;
+ }
+ return true;
+ }
+
+ ///
+ /// Renders the template.
+ ///
+ /// The parser.
+ /// The template.
+ /// The output from the template.
+ private string RenderTemplate(LiquidParser parser, string template)
+ {
try
{
var output = parser.Parse(template).Render();
File.WriteAllText(Output, output);
-
return output;
}
catch (Exception e)
@@ -376,8 +294,6 @@ public string Render()
WriteErrorLine($"Error: {e.Message}");
return string.Empty;
}
-
- // TODO: Refactor this method
}
///
diff --git a/AzureLiquid.Preview/PreviewProcessArguments.cs b/AzureLiquid.Preview/PreviewProcessArguments.cs
new file mode 100644
index 0000000..812a0d3
--- /dev/null
+++ b/AzureLiquid.Preview/PreviewProcessArguments.cs
@@ -0,0 +1,77 @@
+namespace AzureLiquid.Preview;
+
+///
+/// Handles the arguments passed to the preview process.
+///
+public class PreviewProcessArguments
+{
+ ///
+ /// The current path of the process.
+ ///
+ private readonly string _path;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public PreviewProcessArguments() => _path = Directory.GetCurrentDirectory();
+
+ ///
+ /// Gets the index of the argument, if it exists.
+ ///
+ /// The arguments.
+ /// The key.
+ /// The index of the argument.
+ internal static int GetArgumentIndex(string[] args, string key)
+ {
+ for (int i = 0; i < args.Length; i++)
+ {
+ if (IsArgMatch(args[i], key))
+ {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ ///
+ /// Determines whether the argument matches the partial argument key name.
+ ///
+ /// The argument.
+ /// The key.
+ ///
+ /// true if argument found; otherwise, false.
+ ///
+ internal static bool IsArgMatch(string arg, string key)
+ {
+ return string.CompareOrdinal(arg, "--" + key) == 0;
+ }
+
+ ///
+ /// Gets a value indicating whether the specific argument key was found in the arguments.
+ ///
+ /// The passed arguments.
+ /// The key.
+ /// true if the argument was found, otherwise false.
+ public static bool HasArgument(string[] args, string key) => args?.Length > 0 && args.Any(arg => IsArgMatch(arg, key));
+
+ ///
+ /// Parses the argument value.
+ ///
+ /// The arguments.
+ /// The key.
+ /// The argument value.
+ public string ParsePath(string[] args, string key)
+ {
+ if (args == null || args.Length == 0)
+ {
+ return string.Empty;
+ }
+
+ var index = GetArgumentIndex(args, key);
+ return
+ index == -1 || index - 1 >= args.Length
+ ? string.Empty // No match, or no arguments passed
+ : Path.GetFullPath(args[index + 1], _path); // Argument found, parsing path
+ }
+}
\ No newline at end of file
diff --git a/AzureLiquid.Preview/Program.cs b/AzureLiquid.Preview/Program.cs
index 238c068..bcc91a8 100644
--- a/AzureLiquid.Preview/Program.cs
+++ b/AzureLiquid.Preview/Program.cs
@@ -2,8 +2,9 @@
// Licensed under the open source Apache License, Version 2.0.
//
-// deepcode ignore XXE: All input is returned to original source and is not used internally
-
+using System.Runtime.CompilerServices;
using AzureLiquid.Preview;
+[assembly: InternalsVisibleTo("AzureLiquid.Tests")]
+
PreviewProcess.Create(args);
\ No newline at end of file
diff --git a/AzureLiquid.Tests/PreviewProcessArgumentsTests.cs b/AzureLiquid.Tests/PreviewProcessArgumentsTests.cs
new file mode 100644
index 0000000..bd49493
--- /dev/null
+++ b/AzureLiquid.Tests/PreviewProcessArgumentsTests.cs
@@ -0,0 +1,100 @@
+//
+// Licensed under the open source Apache License, Version 2.0.
+//
+
+using AzureLiquid.Preview;
+using FluentAssertions;
+using Xunit;
+
+namespace AzureLiquid.Tests;
+
+///
+/// Unit tests for the class.
+///
+public class PreviewProcessArgumentsTests
+{
+ ///
+ /// Tests the method.
+ ///
+ /// The array of arguments.
+ /// The key to search for.
+ /// The expected index of the key in the arguments array.
+ [Theory]
+ [InlineData(new[] { "--template", "template.liquid" }, "template", 0)]
+ [InlineData(new[] { "--content", "content.json" }, "content", 0)]
+ [InlineData(new[] { "--output", "output.txt" }, "output", 0)]
+ [InlineData(new[] { "--watch" }, "watch", 0)]
+ [InlineData(new[] { "--template", "template.liquid" }, "content", -1)]
+ public void GetArgumentIndex_ShouldReturnCorrectIndex(string[] args, string key, int expectedIndex)
+ {
+ // Act
+ var index = PreviewProcessArguments.GetArgumentIndex(args, key);
+
+ // Assert
+ index.Should().Be(expectedIndex);
+ }
+
+ ///
+ /// Tests the method.
+ ///
+ /// The argument to check.
+ /// The key to match against.
+ /// The expected result of the match.
+ [Theory]
+ [InlineData("--template", "template", true)]
+ [InlineData("--content", "content", true)]
+ [InlineData("--output", "output", true)]
+ [InlineData("--watch", "watch", true)]
+ [InlineData("--template", "content", false)]
+ public void IsArgMatch_ShouldReturnCorrectResult(string arg, string key, bool expectedResult)
+ {
+ // Act
+ var result = PreviewProcessArguments.IsArgMatch(arg, key);
+
+ // Assert
+ result.Should().Be(expectedResult);
+ }
+
+ ///
+ /// Tests the method.
+ ///
+ /// The array of arguments.
+ /// The key to search for.
+ /// The expected result of the search.
+ [Theory]
+ [InlineData(new[] { "--template", "template.liquid" }, "template", true)]
+ [InlineData(new[] { "--content", "content.json" }, "content", true)]
+ [InlineData(new[] { "--output", "output.txt" }, "output", true)]
+ [InlineData(new[] { "--watch" }, "watch", true)]
+ [InlineData(new[] { "--template", "template.liquid" }, "content", false)]
+ public void HasArgument_ShouldReturnCorrectResult(string[] args, string key, bool expectedResult)
+ {
+ // Act
+ var result = PreviewProcessArguments.HasArgument(args, key);
+
+ // Assert
+ result.Should().Be(expectedResult);
+ }
+
+ ///
+ /// Tests the method.
+ ///
+ /// The array of arguments.
+ /// The key to search for.
+ /// The expected path associated with the key.
+ [Theory]
+ [InlineData(new[] { "--template", "template.liquid" }, "template", "template.liquid")]
+ [InlineData(new[] { "--content", "content.json" }, "content", "content.json")]
+ [InlineData(new[] { "--output", "output.txt" }, "output", "output.txt")]
+ public void ParsePath_ShouldReturnCorrectPath(string[] args, string key, string expectedPath)
+ {
+ // Arrange
+ var previewArgs = new PreviewProcessArguments();
+
+ // Act
+ var path = previewArgs.ParsePath(args, key);
+
+ // Assert
+ path.Should().Contain(expectedPath);
+ }
+}
\ No newline at end of file
diff --git a/AzureLiquid.Tests/PreviewProcessTests.cs b/AzureLiquid.Tests/PreviewProcessTests.cs
index 0a3e993..6b54340 100644
--- a/AzureLiquid.Tests/PreviewProcessTests.cs
+++ b/AzureLiquid.Tests/PreviewProcessTests.cs
@@ -72,7 +72,6 @@ public void EnsurePreviewParsingCSharpArguments()
///
/// Ensure the preview process can be created from a set of arguments.
///
- /// Determine if a log should be produced.
/// First argument.
/// Second argument.
/// Third argument.
@@ -82,16 +81,16 @@ public void EnsurePreviewParsingCSharpArguments()
/// Seventh argument.
/// Eighth argument.
[Theory]
- [InlineData(false, "", "", "", "", "", "", "", "")]
- [InlineData(false, "--template", "./Resources/event.liquid", "--content", "./Resources/event.json", "--output",
- "./Resources/preview.txt", "", "")]
- [InlineData(false, "--template", "./Resources/event.liquid", "", "", "", "", "", "")]
- [InlineData(false, "--watch", "", "", "", "", "", "", "")]
- [InlineData(true, "--help", "", "", "", "", "", "", "")]
- [InlineData(true, "--template", "./Resources/event_not_found.liquid", "--content", "./Resources/event.xml",
- "--output", "./Resources/preview.txt", "", "")]
- public void EnsureArgumentParsing(bool shouldLog, string arg1, string arg2, string arg3, string arg4, string arg5,
- string arg6, string arg7, string arg8)
+ [InlineData("", "", "", "", "", "", "", "")]
+ [InlineData("--template", "./Resources/event.liquid", "--content", "./Resources/event.json", "--output", "./Resources/preview.txt", "", "")]
+ [InlineData("--template", "./Resources/event.liquid", "", "", "", "", "", "")]
+ [InlineData("--watch", "", "", "", "", "", "", "")]
+ [InlineData("--help", "", "", "", "", "", "", "")]
+ [InlineData("--template", "./Resources/event_not_found.liquid", "--content", "./Resources/event.xml", "--output", "./Resources/preview.txt", "", "")]
+ [InlineData("--template", "./Resources/empty.liquid", "--content", "./Resources/event.json", "--output", "./Resources/preview.txt", "", "")]
+ [InlineData("--template", "./Resources/empty.liquid", "--content", "./Resources/empty.json", "--output", "./Resources/preview.txt", "", "")]
+ [InlineData("--template", "./Resources/empty.liquid", "--content", "./Resources/empty.pdf", "--output", "./Resources/preview.txt", "", "")]
+ public void EnsureArgumentParsing(string arg1, string arg2, string arg3, string arg4, string arg5, string arg6, string arg7, string arg8)
{
// Arrange
var args = new[] { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 };
@@ -101,17 +100,27 @@ public void EnsureArgumentParsing(bool shouldLog, string arg1, string arg2, stri
// Assert
preview.Should().NotBeNull("A preview process should have been created");
+ }
- if (shouldLog)
- {
- preview.Log.Should().NotBeEmpty("A log should have been created");
- }
- else
- {
- preview.Log.Should().BeEmpty("No log should have been created");
- }
+ ///
+ /// Ensure the preview process cannot run with correct input.
+ ///
+ [Fact]
+ public void EnsureCannotRenderWithoutContent()
+ {
+ // Arrange
+ var instance = new PreviewProcess();
+
+ // Act
+ var result = instance.Render();
+
+ // Assert
+ result.Should().BeEmpty("A result should not have been created");
}
+ ///
+ /// Ensure the preview process can be created from a set of arguments.
+ ///
[Fact]
public void EnsureObjectCreation()
{
@@ -153,7 +162,41 @@ public void EnsureWatcher()
instance.Log.Should().NotBeEmpty("A log should have been created");
}
- #region Nested type: Arrangement
+ ///
+ /// Ensure the preview process can read a file.
+ ///
+ [Fact]
+ public void EnsureFileReadExceptionHandling()
+ {
+ // Arrange
+ var instance = new PreviewProcess();
+ const string file = "notfound.liquid";
+
+ // Act
+ var result = instance.ReadFileContent(file);
+
+ // Assert
+ result.Should().BeEmpty("A result should not have been created");
+ instance.Log.Should().NotBeEmpty("A log should not have been created");
+ }
+
+ ///
+ /// Ensure the preview process can handle missing arguments.
+ ///
+ [Fact]
+ public void EnsureHelpMessageShown()
+ {
+ // Arrange
+ var empty = new string[0];
+ var instance = PreviewProcess.Create(empty);
+
+ // Act
+ var result = instance.Render();
+
+ // Assert
+ result.Should().BeEmpty("A result should not have been created");
+ instance.Log.Should().NotBeEmpty("A log should not have been created");
+ }
///
/// Contains arranged values used for testing, containing mock instances and expected return values.
@@ -222,6 +265,4 @@ public static string GetPath(string path)
return Path.GetFullPath(path, basePath);
}
}
-
- #endregion
}
\ No newline at end of file
diff --git a/AzureLiquid.Tests/Resources/empty.json b/AzureLiquid.Tests/Resources/empty.json
new file mode 100644
index 0000000..e69de29
diff --git a/AzureLiquid.Tests/Resources/empty.liquid b/AzureLiquid.Tests/Resources/empty.liquid
new file mode 100644
index 0000000..e69de29
diff --git a/Create-PullRequest.ps1 b/Create-PullRequest.ps1
new file mode 100644
index 0000000..538d650
--- /dev/null
+++ b/Create-PullRequest.ps1
@@ -0,0 +1,130 @@
+Param (
+ [switch]$all,
+ [string]$filter,
+ [switch]$help
+)
+
+# Define the base branch (e.g., main or master)
+$BASE_BRANCH = "main"
+
+# Function to display help message
+function Show-Help {
+ Write-Output "Usage: .\New-ChangeSet.ps1 [--all] [--filter ] [--help]"
+ Write-Output ""
+ Write-Output "Options:"
+ Write-Output " --all Include 'Chores' and 'Other' sections in the output."
+ Write-Output " --filter Filter out commits matching the given regular expression."
+ Write-Output " --help Show this help message and exit."
+}
+
+# Parse command-line arguments
+$show_all = $false
+$filter_regex = ""
+
+if ($help) {
+ Show-Help
+ exit 0
+}
+
+if ($all) {
+ $show_all = $true
+}
+
+if ($filter) {
+ $filter_regex = $filter
+}
+
+# Get the current branch name
+$current_branch = git rev-parse --abbrev-ref HEAD
+
+# Extract the issue identifier from the branch name
+$issue_identifier = if ($current_branch -match '(patch|feature)/[a-z0-9]+-[0-9]+') { $matches[0] -match '[A-Z]+-[0-9]+'; $matches[0] }
+
+# Fetch the commit messages from the current branch that are not in the base branch and reverse the order
+$commits = git log "$BASE_BRANCH..HEAD" --pretty=format:"%s" | ForEach-Object { $_ } | Sort-Object { $_ } -Descending
+
+# Initialize the changeset
+$changeset = ""
+
+# Initialize variables for each conventional commit type
+$docs_commits = ""
+$feat_commits = ""
+$fix_commits = ""
+$test_commits = ""
+$chore_commits = ""
+$ci_commits = ""
+$other_commits = ""
+
+# Filter and format the commits
+foreach ($commit in $commits) {
+ # Apply filter if specified
+ if ($filter_regex -ne "" -and $commit -match $filter_regex) {
+ continue
+ }
+
+ # Extract the conventional commit type and the message
+ if ($commit -match '^(Merge.*|.*\(#\d+\))$') {
+ continue
+ }
+
+ if ($commit -match '^(feat|fix|docs|test|chore|ci)(\([^\)]+\))?:\s*(.*)$') {
+ $commit_type = $matches[1]
+ $commit_message = $matches[3]
+ switch ($commit_type) {
+ "docs" { $docs_commits += "- $commit_message`n" }
+ "feat" { $feat_commits += "- $commit_message`n" }
+ "test" { $test_commits += "- $commit_message`n" }
+ "fix" { $fix_commits += "- $commit_message`n" }
+ "chore" { $chore_commits += "- $commit_message`n" }
+ default { $other_commits += "- $commit_message`n" }
+ }
+ }
+ else {
+ if ($show_all) {
+ $other_commits += "- $commit`n"
+ }
+ }
+}
+
+# Format the changeset
+if ($feat_commits -ne "") {
+ $changeset += "### Features:`n`n$feat_commits`n"
+}
+if ($fix_commits -ne "") {
+ $changeset += "### Fixes:`n`n$fix_commits`n"
+}
+if ($test_commits -ne "") {
+ $changeset += "### Tests:`n`n$test_commits`n"
+}
+if ($docs_commits -ne "") {
+ $changeset += "### Documentation:`n`n$docs_commits`n"
+}
+if ($ci_commits -ne "") {
+ $changeset += "### Continuous Integration:`n`n$ci_commits`n"
+}
+if ($show_all) {
+ if ($chore_commits -ne "") {
+ $changeset += "### Chores:`n`n$chore_commits`n"
+ }
+ if ($other_commits -ne "") {
+ $changeset += "### Other:`n`n$other_commits`n"
+ }
+}
+
+# Find the first pull_request_template.md file in the repository
+$template_file = Get-ChildItem -Recurse -Filter "pull_request_template.md" -Force | Select-Object -First 1
+
+# Check if the template file was found
+if (-not $template_file) {
+ Write-Warning "pull_request_template.md file not found in the repository."
+}
+else {
+ # Read the pull request template
+ $template_content = Get-Content -Path $template_file.FullName -Raw
+}
+
+# Append updated content to end of the template
+$updated_content = $template_content + $changeset
+
+# Output the updated content
+Write-Output $updated_content
\ No newline at end of file