OPC # 0001: Extract OPC into standalone repo

This commit is contained in:
amadzarak
2026-04-25 17:26:42 -04:00
commit 42383bdc03
170 changed files with 21365 additions and 0 deletions
+84
View File
@@ -0,0 +1,84 @@
using System.Formats.Tar;
using System.IO.Compression;
namespace ControlPlane.Api.Services;
/// <summary>
/// Creates a gzipped tar stream from a directory, respecting .dockerignore rules.
/// Used to supply the Docker build context to the Docker SDK.
/// </summary>
internal static class TarHelper
{
private static readonly string[] DefaultIgnore =
[
".git", ".vs", ".vscode", "node_modules", "bin", "obj",
"VaultData", "*.user", "*.suo",
];
public static void Pack(string root, Stream destination)
{
var ignorePatterns = LoadDockerIgnore(root);
using var gz = new GZipStream(destination, CompressionLevel.Fastest, leaveOpen: true);
using var tar = new TarWriter(gz, TarEntryFormat.Gnu, leaveOpen: false);
foreach (var file in Directory.EnumerateFiles(root, "*", SearchOption.AllDirectories))
{
var relative = Path.GetRelativePath(root, file).Replace('\\', '/');
if (ShouldIgnore(relative, ignorePatterns))
continue;
var entry = new GnuTarEntry(TarEntryType.RegularFile, relative)
{
DataStream = File.OpenRead(file),
};
tar.WriteEntry(entry);
}
}
private static List<string> LoadDockerIgnore(string root)
{
var path = Path.Combine(root, ".dockerignore");
var patterns = new List<string>(DefaultIgnore);
if (!File.Exists(path)) return patterns;
foreach (var line in File.ReadAllLines(path))
{
var trimmed = line.Trim();
if (!string.IsNullOrEmpty(trimmed) && !trimmed.StartsWith('#'))
patterns.Add(trimmed);
}
return patterns;
}
private static bool ShouldIgnore(string relativePath, List<string> patterns)
{
var segments = relativePath.Split('/');
foreach (var pattern in patterns)
{
var p = pattern.TrimStart('/').TrimEnd('/');
// Glob suffix match (e.g. *.user)
if (p.StartsWith('*'))
{
if (relativePath.EndsWith(p[1..], StringComparison.OrdinalIgnoreCase))
return true;
continue;
}
// Exact full-path match or root-anchored prefix (e.g. .git, .vs)
if (relativePath.Equals(p, StringComparison.OrdinalIgnoreCase))
return true;
if (relativePath.StartsWith(p + "/", StringComparison.OrdinalIgnoreCase))
return true;
// Match any path segment so that nested bin/, obj/, node_modules/ etc. are caught
// regardless of which project subdirectory they live in.
if (segments.Any(seg => seg.Equals(p, StringComparison.OrdinalIgnoreCase)))
return true;
}
return false;
}
}