自动构建
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
using System.Text;
|
||||
|
||||
namespace Jiaowu.Api.Infrastructure.Configuration;
|
||||
|
||||
public static class EnvironmentFile
|
||||
{
|
||||
public const string PathVariableName = "JIAOWU_ENV_FILE";
|
||||
|
||||
public static string? Load(string? path = null)
|
||||
{
|
||||
var configuredPath = path;
|
||||
if (string.IsNullOrWhiteSpace(configuredPath))
|
||||
{
|
||||
configuredPath = Environment.GetEnvironmentVariable(PathVariableName);
|
||||
}
|
||||
|
||||
var isExplicit = !string.IsNullOrWhiteSpace(configuredPath);
|
||||
var resolvedPath = isExplicit
|
||||
? Path.GetFullPath(configuredPath!, Environment.CurrentDirectory)
|
||||
: Path.Combine(AppContext.BaseDirectory, ".env");
|
||||
|
||||
if (!File.Exists(resolvedPath))
|
||||
{
|
||||
if (isExplicit)
|
||||
{
|
||||
throw new FileNotFoundException(
|
||||
$"环境变量文件不存在:{resolvedPath}",
|
||||
resolvedPath);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
var values = Parse(resolvedPath);
|
||||
foreach (var (key, value) in values)
|
||||
{
|
||||
if (Environment.GetEnvironmentVariable(key) is null)
|
||||
{
|
||||
Environment.SetEnvironmentVariable(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
return resolvedPath;
|
||||
}
|
||||
|
||||
private static Dictionary<string, string> Parse(string path)
|
||||
{
|
||||
var values = new Dictionary<string, string>(StringComparer.Ordinal);
|
||||
var lineNumber = 0;
|
||||
foreach (var sourceLine in File.ReadLines(path, Encoding.UTF8))
|
||||
{
|
||||
lineNumber++;
|
||||
var line = sourceLine.Trim();
|
||||
if (line.Length == 0 || line.StartsWith('#'))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.StartsWith("export ", StringComparison.Ordinal))
|
||||
{
|
||||
line = line[7..].TrimStart();
|
||||
}
|
||||
|
||||
var separatorIndex = line.IndexOf('=');
|
||||
if (separatorIndex <= 0)
|
||||
{
|
||||
throw InvalidLine(path, lineNumber, "缺少 KEY=VALUE 分隔符");
|
||||
}
|
||||
|
||||
var key = line[..separatorIndex].Trim();
|
||||
if (!IsValidKey(key))
|
||||
{
|
||||
throw InvalidLine(path, lineNumber, $"变量名无效:{key}");
|
||||
}
|
||||
|
||||
var rawValue = line[(separatorIndex + 1)..].Trim();
|
||||
values[key] = ParseValue(path, lineNumber, rawValue);
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
private static string ParseValue(string path, int lineNumber, string rawValue)
|
||||
{
|
||||
if (rawValue.Length == 0)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
if (rawValue[0] == '\'')
|
||||
{
|
||||
if (rawValue.Length < 2 || rawValue[^1] != '\'')
|
||||
{
|
||||
throw InvalidLine(path, lineNumber, "单引号值没有正确闭合");
|
||||
}
|
||||
|
||||
return rawValue[1..^1];
|
||||
}
|
||||
|
||||
if (rawValue[0] != '"')
|
||||
{
|
||||
return rawValue;
|
||||
}
|
||||
|
||||
if (rawValue.Length < 2 || rawValue[^1] != '"')
|
||||
{
|
||||
throw InvalidLine(path, lineNumber, "双引号值没有正确闭合");
|
||||
}
|
||||
|
||||
var value = rawValue[1..^1];
|
||||
var result = new StringBuilder(value.Length);
|
||||
for (var index = 0; index < value.Length; index++)
|
||||
{
|
||||
var current = value[index];
|
||||
if (current != '\\' || index == value.Length - 1)
|
||||
{
|
||||
result.Append(current);
|
||||
continue;
|
||||
}
|
||||
|
||||
var escaped = value[++index];
|
||||
switch (escaped)
|
||||
{
|
||||
case 'n':
|
||||
result.Append('\n');
|
||||
break;
|
||||
case 'r':
|
||||
result.Append('\r');
|
||||
break;
|
||||
case 't':
|
||||
result.Append('\t');
|
||||
break;
|
||||
case '\\':
|
||||
result.Append('\\');
|
||||
break;
|
||||
case '"':
|
||||
result.Append('"');
|
||||
break;
|
||||
default:
|
||||
result.Append('\\');
|
||||
result.Append(escaped);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
private static bool IsValidKey(string key)
|
||||
{
|
||||
if (key.Length == 0 || !(char.IsAsciiLetter(key[0]) || key[0] == '_'))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return key.All(character =>
|
||||
char.IsAsciiLetterOrDigit(character) || character == '_');
|
||||
}
|
||||
|
||||
private static FormatException InvalidLine(
|
||||
string path,
|
||||
int lineNumber,
|
||||
string reason) =>
|
||||
new($"{path} 第 {lineNumber} 行无效:{reason}。");
|
||||
}
|
||||
Reference in New Issue
Block a user