Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/c#/GeneralUpdate.Core/Bootstrap/GeneralClientOSS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

namespace GeneralUpdate.Core;

public sealed class GeneralClientOSS
[Obsolete("Use GeneralUpdateBootstrap with AppType=AppType.OSS instead.")] public sealed class GeneralClientOSS
{
private GeneralClientOSS() { }

Expand Down
1 change: 1 addition & 0 deletions src/c#/GeneralUpdate.Core/Bootstrap/GeneralUpdateOSS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

namespace GeneralUpdate.Core
{
[Obsolete("Use GeneralUpdateBootstrap with AppType=AppType.OSS instead. See UpdateOptions.AppType.")]
public sealed class GeneralUpdateOSS
{
private GeneralUpdateOSS() { }
Expand Down
6 changes: 5 additions & 1 deletion src/c#/GeneralUpdate.Core/Configuration/UpdateOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ public static class UpdateOptions
public static readonly UpdateOption<string?> ProductId = UpdateOption.ValueOf<string?>("PRODUCTID");
public static readonly UpdateOption<string?> PermissionScript = UpdateOption.ValueOf<string?>("PERMISSIONSCRIPT");
public static readonly UpdateOption<string?> Scheme = UpdateOption.ValueOf<string?>("SCHEME");
public static readonly UpdateOption<string?> Token = UpdateOption.ValueOf<string?>("TOKEN");
public static readonly UpdateOption<string?> Token = UpdateOption.ValueOf<string?("TOKEN");

// ═══ OSS ═══
public static readonly UpdateOption<int?> OSSProvider = UpdateOption.ValueOf<int?>("OSSPROVIDER");
public static readonly UpdateOption<string?> OSSBucketRegion = UpdateOption.ValueOf<string?>("OSSBUCKETREGION");
}
}
173 changes: 61 additions & 112 deletions src/c#/GeneralUpdate.Core/FileSystem/BlackListManager.cs
Original file line number Diff line number Diff line change
@@ -1,138 +1,87 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using GeneralUpdate.Core.Configuration;

namespace GeneralUpdate.Core.FileSystem;

public class BlackListManager
/// <summary>Matches files/directories against a blacklist configuration.</summary>
public interface IBlackListMatcher
{
private static readonly object LockObject = new object();
private static BlackListManager? _instance;

private static readonly List<string> _blackFileFormats =
[
".patch",
Format.ZIP,
".rar",
".tar",
".json",
".pdb"
];
bool IsBlacklisted(string relativeFilePath);
bool IsBlacklistedFormat(string extension);
bool ShouldSkipDirectory(string directoryName);
}

/// <summary>
/// Thread-safe blacklist manager. Uses Lazy<T> singleton.
/// Matching is case-insensitive and supports prefix matching for skip directories.
/// </summary>
public class BlackListManager : IBlackListMatcher
{
private static readonly Lazy<BlackListManager> _lazy = new(() => new BlackListManager());
private readonly object _lock = new();

private static readonly List<string> _blackFiles =
private readonly List<string> _blackFiles =
[
"Microsoft.Bcl.AsyncInterfaces.dll",
"System.Collections.Immutable.dll",
"System.IO.Pipelines.dll",
"System.Collections.Immutable.dll",
"System.IO.Pipelines.dll",
"System.Text.Encodings.Web.dll",
"System.Text.Json.dll"
];

private static readonly List<string> _skipDirectorys = ["app-", "fail"];
private readonly List<string> _blackFormats = [".patch", ".pdb", ".rar", ".tar", ".json", Format.ZIP];
private readonly List<string> _skipDirs = ["app-", "fail"];

private BlackListManager() { }

public static BlackListManager? Instance
{
get
{
if (_instance == null)
{
lock (LockObject)
{
if (_instance == null)
{
_instance = new BlackListManager();
}
}
}

return _instance;
}
}

public IReadOnlyList<string> BlackFileFormats => _blackFileFormats.AsReadOnly();
public IReadOnlyList<string> BlackFiles => _blackFiles.AsReadOnly();

public IReadOnlyList<string> SkipDirectorys = _skipDirectorys.AsReadOnly();

public void AddBlackFileFormats(List<string>? formats)
{
if (formats == null)
return;

foreach (var format in formats)
{
AddBlackFileFormat(format);
}
}

public void AddBlackFileFormat(string format)
{
if (string.IsNullOrWhiteSpace(format))
return;

if (!_blackFileFormats.Contains(format))
{
_blackFileFormats.Add(format);
}
}

public void AddBlackFiles(List<string>? fileNames)
{
if (fileNames == null)
return;

foreach (var fileName in fileNames)
{
AddBlackFile(fileName);
}
}

public void AddBlackFile(string fileName)
{
if (string.IsNullOrWhiteSpace(fileName))
return;

if (!_blackFiles.Contains(fileName))
{
_blackFiles.Add(fileName);
}
}

public void AddSkipDirectorys(List<string>? directorys)
{
if (directorys == null)
return;

foreach (var directory in directorys)
{
AddSkipDirectory(directory);
}
}

public void AddSkipDirectory(string directory)
{
if (string.IsNullOrWhiteSpace(directory))
return;

if (!_skipDirectorys.Contains(directory))
{
_skipDirectorys.Add(directory);
}
}

public static BlackListManager Instance => _lazy.Value;

// Read-only accessors
public IReadOnlyList<string> BlackFiles { get { lock (_lock) return _blackFiles.ToList(); } }
public IReadOnlyList<string> BlackFormats { get { lock (_lock) return _blackFormats.ToList(); } }
public IReadOnlyList<string> SkipDirectorys { get { lock (_lock) return _skipDirs.ToList(); } }

// Mutation
public void AddBlackFiles(List<string>? files)
{ if (files == null) return; lock (_lock) { foreach (var f in files) AddBlackFileLocked(f); } }
public void AddBlackFile(string file)
{ if (string.IsNullOrWhiteSpace(file)) return; lock (_lock) AddBlackFileLocked(file); }
private void AddBlackFileLocked(string file)
{ if (!_blackFiles.Contains(file)) _blackFiles.Add(file); }

public void AddBlackFormats(List<string>? formats)
{ if (formats == null) return; lock (_lock) { foreach (var f in formats) AddBlackFormatLocked(f); } }
public void AddBlackFormat(string format)
{ if (string.IsNullOrWhiteSpace(format)) return; lock (_lock) AddBlackFormatLocked(format); }
private void AddBlackFormatLocked(string format)
{ if (!_blackFormats.Contains(format)) _blackFormats.Add(format); }

public void AddSkipDirectorys(List<string>? dirs)
{ if (dirs == null) return; lock (_lock) { foreach (var d in dirs) AddSkipDirectoryLocked(d); } }
public void AddSkipDirectory(string dir)
{ if (string.IsNullOrWhiteSpace(dir)) return; lock (_lock) AddSkipDirectoryLocked(dir); }
private void AddSkipDirectoryLocked(string dir)
{ if (!_skipDirs.Contains(dir)) _skipDirs.Add(dir); }
Comment on lines +43 to +67

// Matching (read operations — no lock needed for immutable reads)
public bool IsBlacklisted(string relativeFilePath)
{
var fileName = Path.GetFileName(relativeFilePath);
var fileExtension = Path.GetExtension(relativeFilePath);
var ext = Path.GetExtension(relativeFilePath);
Comment on lines +69 to +73
lock (_lock)
return _blackFiles.Contains(fileName) || _blackFormats.Contains(ext);
}

return _blackFiles.Contains(fileName) || _blackFileFormats.Contains(fileExtension);
public bool IsBlacklistedFormat(string extension)
{
lock (_lock) return _blackFormats.Contains(extension);
}

public bool IsSkipDirectory(string directory)
public bool ShouldSkipDirectory(string directoryName)
{
return _skipDirectorys.Any(directory.Contains);
lock (_lock) return _skipDirs.Any(d => directoryName.Contains(d));
}
}
}