mirror of
https://github.com/actions/runner.git
synced 2025-12-10 12:36:23 +00:00
173 lines
8.4 KiB
C#
173 lines
8.4 KiB
C#
using System;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using GitHub.Runner.Sdk;
|
|
using Pipelines = GitHub.DistributedTask.Pipelines;
|
|
using System.IO;
|
|
using System.Text.RegularExpressions;
|
|
using GitHub.DistributedTask.Pipelines.Expressions;
|
|
using System.Text;
|
|
|
|
namespace GitHub.Runner.Plugins.Repository.v1_0
|
|
{
|
|
public class CheckoutTask : IRunnerActionPlugin
|
|
{
|
|
private readonly Regex _validSha1 = new(@"\b[0-9a-f]{40}\b", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled, TimeSpan.FromSeconds(2));
|
|
|
|
public async Task RunAsync(RunnerActionPluginExecutionContext executionContext, CancellationToken token)
|
|
{
|
|
string runnerWorkspace = executionContext.GetRunnerContext("workspace");
|
|
ArgUtil.Directory(runnerWorkspace, nameof(runnerWorkspace));
|
|
string tempDirectory = executionContext.GetRunnerContext("temp");
|
|
ArgUtil.Directory(tempDirectory, nameof(tempDirectory));
|
|
|
|
var repoFullName = executionContext.GetInput(Pipelines.PipelineConstants.CheckoutTaskInputs.Repository);
|
|
if (string.IsNullOrEmpty(repoFullName))
|
|
{
|
|
repoFullName = executionContext.GetGitHubContext("repository");
|
|
}
|
|
|
|
var repoFullNameSplit = repoFullName.Split("/", StringSplitOptions.RemoveEmptyEntries);
|
|
if (repoFullNameSplit.Length != 2)
|
|
{
|
|
throw new ArgumentOutOfRangeException(repoFullName);
|
|
}
|
|
|
|
string expectRepoPath;
|
|
var path = executionContext.GetInput(Pipelines.PipelineConstants.CheckoutTaskInputs.Path);
|
|
if (!string.IsNullOrEmpty(path))
|
|
{
|
|
expectRepoPath = IOUtil.ResolvePath(runnerWorkspace, path);
|
|
if (!expectRepoPath.StartsWith(runnerWorkspace.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar))
|
|
{
|
|
throw new ArgumentException($"Input path '{path}' should resolve to a directory under '{runnerWorkspace}', current resolved path '{expectRepoPath}'.");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// When repository doesn't has path set, default to sources directory 1/repoName
|
|
expectRepoPath = Path.Combine(runnerWorkspace, repoFullNameSplit[1]);
|
|
}
|
|
|
|
var workspaceRepo = executionContext.GetGitHubContext("repository");
|
|
// for self repository, we need to let the worker knows where it is after checkout.
|
|
if (string.Equals(workspaceRepo, repoFullName, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
var workspaceRepoPath = executionContext.GetGitHubContext("workspace");
|
|
|
|
executionContext.Debug($"Repository requires to be placed at '{expectRepoPath}', current location is '{workspaceRepoPath}'");
|
|
if (!string.Equals(workspaceRepoPath.Trim(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), expectRepoPath.Trim(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), IOUtil.FilePathStringComparison))
|
|
{
|
|
executionContext.Output($"Repository is current at '{workspaceRepoPath}', move to '{expectRepoPath}'.");
|
|
var count = 1;
|
|
var staging = Path.Combine(tempDirectory, $"_{count}");
|
|
while (Directory.Exists(staging))
|
|
{
|
|
count++;
|
|
staging = Path.Combine(tempDirectory, $"_{count}");
|
|
}
|
|
|
|
try
|
|
{
|
|
executionContext.Debug($"Move existing repository '{workspaceRepoPath}' to '{expectRepoPath}' via staging directory '{staging}'.");
|
|
IOUtil.MoveDirectory(workspaceRepoPath, expectRepoPath, staging, CancellationToken.None);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
executionContext.Debug("Catch exception during repository move.");
|
|
executionContext.Debug(ex.ToString());
|
|
executionContext.Warning("Unable move and reuse existing repository to required location.");
|
|
IOUtil.DeleteDirectory(expectRepoPath, CancellationToken.None);
|
|
}
|
|
|
|
executionContext.Output($"Repository will locate at '{expectRepoPath}'.");
|
|
}
|
|
|
|
executionContext.Debug($"Update workspace repository location.");
|
|
executionContext.SetRepositoryPath(repoFullName, expectRepoPath, true);
|
|
}
|
|
|
|
string sourceBranch;
|
|
string sourceVersion;
|
|
string refInput = executionContext.GetInput(Pipelines.PipelineConstants.CheckoutTaskInputs.Ref);
|
|
if (string.IsNullOrEmpty(refInput))
|
|
{
|
|
sourceBranch = executionContext.GetGitHubContext("ref");
|
|
sourceVersion = executionContext.GetGitHubContext("sha");
|
|
}
|
|
else
|
|
{
|
|
sourceBranch = refInput;
|
|
sourceVersion = executionContext.GetInput(Pipelines.PipelineConstants.CheckoutTaskInputs.Version); // version get removed when checkout move to repo in the graph
|
|
if (string.IsNullOrEmpty(sourceVersion) && RegexUtility.IsMatch(sourceBranch, WellKnownRegularExpressions.SHA1))
|
|
{
|
|
sourceVersion = sourceBranch;
|
|
|
|
// If Ref is a SHA and the repo is self, we need to use github.ref as source branch since it might be refs/pull/*
|
|
if (string.Equals(workspaceRepo, repoFullName, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
sourceBranch = executionContext.GetGitHubContext("ref");
|
|
}
|
|
else
|
|
{
|
|
sourceBranch = "refs/heads/master";
|
|
}
|
|
}
|
|
}
|
|
|
|
bool clean = StringUtil.ConvertToBoolean(executionContext.GetInput(Pipelines.PipelineConstants.CheckoutTaskInputs.Clean), true);
|
|
string submoduleInput = executionContext.GetInput(Pipelines.PipelineConstants.CheckoutTaskInputs.Submodules);
|
|
|
|
int fetchDepth = 0;
|
|
if (!int.TryParse(executionContext.GetInput("fetch-depth"), out fetchDepth) || fetchDepth < 0)
|
|
{
|
|
fetchDepth = 0;
|
|
}
|
|
|
|
bool gitLfsSupport = StringUtil.ConvertToBoolean(executionContext.GetInput(Pipelines.PipelineConstants.CheckoutTaskInputs.Lfs));
|
|
string accessToken = executionContext.GetInput(Pipelines.PipelineConstants.CheckoutTaskInputs.Token);
|
|
if (string.IsNullOrEmpty(accessToken))
|
|
{
|
|
accessToken = executionContext.GetGitHubContext("token");
|
|
}
|
|
|
|
// register problem matcher
|
|
string problemMatcher = @"
|
|
{
|
|
""problemMatcher"": [
|
|
{
|
|
""owner"": ""checkout-git"",
|
|
""pattern"": [
|
|
{
|
|
""regexp"": ""^fatal: (.*)$"",
|
|
""message"": 1
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}";
|
|
string matcherFile = Path.Combine(tempDirectory, $"git_{Guid.NewGuid()}.json");
|
|
File.WriteAllText(matcherFile, problemMatcher, new UTF8Encoding(false));
|
|
executionContext.Output($"##[add-matcher]{matcherFile}");
|
|
try
|
|
{
|
|
await new GitHubSourceProvider().GetSourceAsync(executionContext,
|
|
expectRepoPath,
|
|
repoFullName,
|
|
sourceBranch,
|
|
sourceVersion,
|
|
clean,
|
|
submoduleInput,
|
|
fetchDepth,
|
|
gitLfsSupport,
|
|
accessToken,
|
|
token);
|
|
}
|
|
finally
|
|
{
|
|
executionContext.Output("##[remove-matcher owner=checkout-git]");
|
|
}
|
|
}
|
|
}
|
|
}
|