Skip to content
Merged
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
188 changes: 90 additions & 98 deletions PinAction/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,14 @@ internal partial class Program
/// </summary>
/// <param name="args">参数</param>
/// <returns>退出代码</returns>
static int Main(string[] args)
private static int Main(string[] args)
{
if (
(args.Length == 0) || // 不提供参数
(
// 单独提供帮助相关参数
(args.Length == 1) &&
(args[0] is "help"
or "--help"
or "-h"
or "/?")
)
args is ["help"
or "--help"
or "-h"
or "/?"]
)
{
Console.WriteLine(Strings.Help);
Expand Down Expand Up @@ -114,115 +110,111 @@ private static bool PinActionHash(string path)

for (int i = 0; i < lines.Length; i++)
{
if (lines[i].Contains("uses:"))
{
// 移除注释
string[] cleanLinePaths = lines[i].Split('#');
if (!lines[i].Contains("uses:")) continue;

// 非注释内容匹配正则 "^\s+uses:\s*([^@]+)@([^@|\s]+)\s*$"
Match match = UsesRegex().Match(cleanLinePaths[0]);
// 移除注释
string[] cleanLinePaths = lines[i].Split('#');

if (match.Success)
{
string repo = match.Groups[1].Value;
string tag = match.Groups[2].Value;
// 非注释内容匹配正则 "^\s+uses:\s*([^@]+)@([^@|\s]+)\s*$"
Match match = UsesRegex().Match(cleanLinePaths[0]);
if (!match.Success) continue;

string repo = match.Groups[1].Value;
string tag = match.Groups[2].Value;

AnsiConsole.MarkupLine($"{Print.MSHead.Information} {string.Format(Strings.FindAction, Path.GetRelativePath(Environment.CurrentDirectory, path), Markup.Escape($"{repo}@{tag}"))}");

// 在这里你可以定义排除哪些项
// 例如排除以 actions/ 开头的项(actions/*@*)
// if (repo.StartsWith("actions/"))
// {
// AnsiConsole.MarkupLine($"{Print.MSHead.Warning} 跳过 {repo}@{tag},因为它是官方工作流");
// continue;
// }

AnsiConsole.MarkupLine($"{Print.MSHead.Information} {string.Format(Strings.FindAction, Path.GetRelativePath(Environment.CurrentDirectory, path), Markup.Escape($"{repo}@{tag}"))}");

// 在这里你可以定义排除哪些项
// 例如排除以 actions/ 开头的项(actions/*@*)
// if (repo.StartsWith("actions/"))
// {
// AnsiConsole.MarkupLine($"{Print.MSHead.Warning} 跳过 {repo}@{tag},因为它是官方工作流");
// continue;
// }
// 操作前检查
// 检查是否已经是哈希值(40个十六进制字符)
if (HashRegex().IsMatch(tag))
{
AnsiConsole.MarkupLine($"{Print.MSHead.Information} {string.Format(Strings.SkippingAlreadyPinnedHashes, Markup.Escape($"{repo}@{tag}"))}");
continue;
}
// 检查仓库是否是 owner/repo 的格式
if (repo.Split('/').Length != 2)
{
AnsiConsole.MarkupLine($"{Print.MSHead.Warning} {Markup.Escape(repo)} 看起来不像是仓库的格式,跳过 {Markup.Escape($"{repo}@{tag}")}");
continue;
}


// 操作前检查
// 检查是否已经是哈希值(40个十六进制字符)
if (HashRegex().IsMatch(tag))
if (!PinedActions.TryGetValue($"{repo}@{tag}", out string? hash))
{
// 尝试 tags/{tag} 和 heads/{tag}
foreach (string refType in new[] { "tags", "heads" })
{
try
{
AnsiConsole.MarkupLine($"{Print.MSHead.Information} {string.Format(Strings.SkippingAlreadyPinnedHashes, Markup.Escape($"{repo}@{tag}"))}");
continue;
// 获取该版本的 git commit hash
Reference reference = GitHubClient.Git.Reference.Get(repo.Split('/')[0], repo.Split('/')[1], $"{refType}/{tag}").Result;
hash = reference.Object.Sha;

PinedActions.TryAdd($"{repo}@{tag}", hash);
break;
}
// 检查仓库是否是 owner/repo 的格式
if (repo.Split('/').Length != 2)
catch (AggregateException ex) when (ex.InnerException != null)
{
AnsiConsole.MarkupLine($"{Print.MSHead.Warning} {Markup.Escape(repo)} 看起来不像是仓库的格式,跳过 {Markup.Escape($"{repo}@{tag}")}");
continue;
}

AnsiConsole.Markup($"{Print.MSHead.Warning} {Strings.ErrorGetHashFailed}");

string? hash;
if (!pinedActions.TryGetValue($"{repo}@{tag}", out hash))
{
// 尝试 tags/{tag} 和 heads/{tag}
foreach (string refType in new[] { "tags", "heads" })
switch (ex.InnerException)
{
try
{
// 获取该版本的 git commit hash
Reference reference = GitHubClient.Git.Reference.Get(repo.Split('/')[0], repo.Split('/')[1], $"{refType}/{tag}").Result;
hash = reference.Object.Sha;

pinedActions.TryAdd($"{repo}@{tag}", hash);
break;
}
catch (AggregateException ex) when (ex.InnerException != null)
{
AnsiConsole.Markup($"{Print.MSHead.Warning} {Strings.ErrorGetHashFailed}");

switch (ex.InnerException)
// 还要再试的用 break;
// 最后一次的用 continue;
// 直接整个程序失败的 return false;
case Octokit.NotFoundException:
if (refType == "tags")
{
AnsiConsole.MarkupLineInterpolated($"[yellow]{string.Format(Strings.ErrorTagNotFound, tag)}[/]");
break;
}
else
{
// 还要再试的用 break;
// 最后一次的用 continue;
// 直接整个程序失败的 return false;
case Octokit.NotFoundException:
if (refType == "tags")
{
AnsiConsole.MarkupLineInterpolated($"[yellow]{string.Format(Strings.ErrorTagNotFound, tag)}[/]");
break;
}
else
{
AnsiConsole.MarkupLineInterpolated($"[red]{string.Format(Strings.ErrorBranchNotFound, tag, $"{repo}@{tag}")}[/]");
continue;
}
case Octokit.RateLimitExceededException:
AnsiConsole.MarkupLine($"[yellow]{Strings.ErrorRateLimitExceeded}[/]");
return false;
default:
AnsiConsole.MarkupLineInterpolated($"[red]{ex.InnerException.Message}[/]");
continue;
AnsiConsole.MarkupLineInterpolated($"[red]{string.Format(Strings.ErrorBranchNotFound, tag, $"{repo}@{tag}")}[/]");
continue;
}
}
case Octokit.RateLimitExceededException:
AnsiConsole.MarkupLine($"[yellow]{Strings.ErrorRateLimitExceeded}[/]");
return false;
default:
AnsiConsole.MarkupLineInterpolated($"[red]{ex.InnerException.Message}[/]");
continue;
}
}
}
}
#if DEBUG
else
{
AnsiConsole.MarkupLine($"{Print.MSHead.Debug} 读取缓存 {Markup.Escape($"{repo}@{hash}")} # {Markup.Escape(tag)}");
}
else
{
AnsiConsole.MarkupLine($"{Print.MSHead.Debug} 读取缓存 {Markup.Escape($"{repo}@{hash}")} # {Markup.Escape(tag)}");
}
#endif

if (hash is null)
{
continue;
}

lines[i] = $"{cleanLinePaths[0].Replace($"{repo}@{tag}", $"{repo}@{hash}")} # {tag}";
if (cleanLinePaths.Length > 1)
{
// 将注释部分重新添加到行末
foreach (string commentPart in cleanLinePaths.Skip(1))
{
lines[i] += commentPart;
}
}
if (hash is null)
{
continue;
}

AnsiConsole.MarkupLine($"{Print.MSHead.Success} {Strings.Pinned} {Markup.Escape($"{repo}@{hash}")} # {Markup.Escape(tag)}");
lines[i] = $"{cleanLinePaths[0].Replace($"{repo}@{tag}", $"{repo}@{hash}")} # {tag}";
if (cleanLinePaths.Length > 1)
{
// 将注释部分重新添加到行末
foreach (string commentPart in cleanLinePaths.Skip(1))
{
lines[i] += commentPart;
}
}

AnsiConsole.MarkupLine($"{Print.MSHead.Success} {Strings.Pinned} {Markup.Escape($"{repo}@{hash}")} # {Markup.Escape(tag)}");
}

// 将修改后的内容写回文件
Expand All @@ -241,7 +233,7 @@ private static bool PinActionHash(string path)
/// <para>缓存已固定哈希值的 Action,第二次遇到时不用再去请求 GitHub API 获取。</para>
/// <para>按 <c>repo@tag</c>, <c>hash</c> 一对存储。</para>
/// </summary>
private static readonly ConcurrentDictionary<string, string> pinedActions = new();
private static readonly ConcurrentDictionary<string, string> PinedActions = new();

[GeneratedRegex(@"^\s*uses:\s*([^@]+)@([^@|\s]+)\s*$")]
private static partial Regex UsesRegex();
Expand Down