115 lines
4.3 KiB
C#
115 lines
4.3 KiB
C#
namespace YMhut.Box.Core.DevEnvironments;
|
|
|
|
public static class DevEnvironmentBuildScript
|
|
{
|
|
public static IReadOnlyList<string> Create(DevEnvironmentInstallPlan plan, string archivePath)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(plan);
|
|
ArgumentException.ThrowIfNullOrWhiteSpace(archivePath);
|
|
|
|
var recipeCommands = plan.BuildRecipe
|
|
.Split(["\r\n", "\n"], StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
|
|
.Skip(1)
|
|
.ToArray();
|
|
var environmentId = SafePathPart(plan.EnvironmentId);
|
|
var version = SafePathPart(plan.Version.Version);
|
|
var lines = new List<string>
|
|
{
|
|
"@echo off",
|
|
"setlocal EnableExtensions",
|
|
"chcp 65001 >nul 2>&1",
|
|
"set \"SCRIPT_PATH=%~f0\"",
|
|
$"set \"ARCHIVE={EscapeSetValue(archivePath)}\"",
|
|
$"set \"WORKDIR=%USERPROFILE%\\YMhutBuilds\\{environmentId}-{version}\"",
|
|
$"title YMhut Build - {EscapeEcho(environmentId)} {EscapeEcho(version)}",
|
|
$"echo Waiting for {EscapeEcho(plan.EnvironmentName)} {EscapeEcho(plan.Version.Version)} source archive...",
|
|
":wait_download",
|
|
"if not exist \"%ARCHIVE%\" (",
|
|
" timeout /t 2 >nul",
|
|
" goto :wait_download",
|
|
")",
|
|
"if not exist \"%WORKDIR%\" mkdir \"%WORKDIR%\"",
|
|
"if errorlevel 1 goto :failed",
|
|
"cd /d \"%WORKDIR%\"",
|
|
"if errorlevel 1 goto :failed",
|
|
"echo Extracting source archive...",
|
|
"tar -xf \"%ARCHIVE%\" --strip-components=1",
|
|
"if errorlevel 1 goto :failed"
|
|
};
|
|
|
|
foreach (var command in recipeCommands)
|
|
{
|
|
var executable = NormalizeRecipeCommand(command);
|
|
if (string.IsNullOrWhiteSpace(executable))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
lines.Add($"echo ^> {EscapeEcho(executable)}");
|
|
lines.Add(executable);
|
|
lines.Add("if errorlevel 1 goto :failed");
|
|
}
|
|
|
|
lines.AddRange([
|
|
"echo.",
|
|
"echo Build completed successfully.",
|
|
"del \"%SCRIPT_PATH%\" >nul 2>&1",
|
|
"exit /b 0",
|
|
":failed",
|
|
"set \"EXIT_CODE=%ERRORLEVEL%\"",
|
|
"if \"%EXIT_CODE%\"==\"0\" set \"EXIT_CODE=1\"",
|
|
"echo.",
|
|
"echo [ERROR] Source build failed with exit code %EXIT_CODE%.",
|
|
"echo [ERROR] Working directory: %WORKDIR%",
|
|
"echo [ERROR] This script was kept at: %SCRIPT_PATH%",
|
|
"pause",
|
|
"exit /b %EXIT_CODE%"
|
|
]);
|
|
return lines;
|
|
}
|
|
|
|
private static string NormalizeRecipeCommand(string command)
|
|
{
|
|
if (command.StartsWith("tar -xf ", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return string.Empty;
|
|
}
|
|
|
|
if (command.StartsWith("cd ", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
var target = command[3..].Trim().Trim('"');
|
|
if (target.Contains('*') || target.Contains('?'))
|
|
{
|
|
return string.Empty;
|
|
}
|
|
|
|
if (target.Equals("go\\src", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return "cd /d \"%WORKDIR%\\src\"";
|
|
}
|
|
|
|
return $"cd /d \"%WORKDIR%\\{target}\"";
|
|
}
|
|
|
|
var firstToken = command.Split(' ', 2, StringSplitOptions.RemoveEmptyEntries)[0].Trim('"');
|
|
return firstToken.EndsWith(".bat", StringComparison.OrdinalIgnoreCase) ||
|
|
firstToken.EndsWith(".cmd", StringComparison.OrdinalIgnoreCase)
|
|
? "call " + command
|
|
: command;
|
|
}
|
|
|
|
private static string SafePathPart(string value)
|
|
=> string.Concat((value ?? string.Empty).Select(character =>
|
|
Path.GetInvalidFileNameChars().Contains(character) ? '_' : character));
|
|
|
|
private static string EscapeSetValue(string value)
|
|
=> value.Replace("%", "%%", StringComparison.Ordinal);
|
|
|
|
private static string EscapeEcho(string value)
|
|
=> value.Replace("^", "^^", StringComparison.Ordinal)
|
|
.Replace("&", "^&", StringComparison.Ordinal)
|
|
.Replace("|", "^|", StringComparison.Ordinal)
|
|
.Replace("<", "^<", StringComparison.Ordinal)
|
|
.Replace(">", "^>", StringComparison.Ordinal);
|
|
}
|