105 lines
3.9 KiB
C#
105 lines
3.9 KiB
C#
|
|
// Copyright (c) The Geekeey Authors
|
||
|
|
// SPDX-License-Identifier: EUPL-1.2
|
||
|
|
|
||
|
|
using System.CommandLine;
|
||
|
|
using System.Text;
|
||
|
|
|
||
|
|
namespace Geekeey.Actions.Core.Commands;
|
||
|
|
|
||
|
|
internal sealed class Checkout : Command
|
||
|
|
{
|
||
|
|
#pragma warning disable format // @formatter:off
|
||
|
|
private static readonly Option<Uri> Repository = new("--repository") { Required = true };
|
||
|
|
private static readonly Option<DirectoryInfo> Destination = new("--path") { Required = true };
|
||
|
|
private static readonly Option<string> Reference = new("--reference") { Required = true };
|
||
|
|
#pragma warning restore format // @formatter:on
|
||
|
|
|
||
|
|
internal Checkout() : base("checkout")
|
||
|
|
{
|
||
|
|
Add(Repository);
|
||
|
|
Add(Destination);
|
||
|
|
Add(Reference);
|
||
|
|
|
||
|
|
SetAction(HandleAsync);
|
||
|
|
}
|
||
|
|
|
||
|
|
private async Task<int> HandleAsync(ParseResult result, CancellationToken cancellationToken)
|
||
|
|
{
|
||
|
|
var server = result.GetRequiredValue(Program.Server);
|
||
|
|
var access = result.GetRequiredValue(Program.Token);
|
||
|
|
|
||
|
|
var workspace = result.GetRequiredValue(Destination);
|
||
|
|
|
||
|
|
await $"git init -q {workspace.FullName}";
|
||
|
|
await $"git -C {workspace.FullName} config set --local protocol.version 2";
|
||
|
|
await $"git -C {workspace.FullName} config set --local gc.auto 0";
|
||
|
|
|
||
|
|
var repository = result.GetRequiredValue(Repository);
|
||
|
|
|
||
|
|
if (!repository.IsAbsoluteUri)
|
||
|
|
{
|
||
|
|
// relative repository urls are resolved against the server url
|
||
|
|
repository = new Uri(server, repository);
|
||
|
|
|
||
|
|
await $"git -C {workspace.FullName} config set --local --append url.{repository}.insteadOf git@{repository.Host}";
|
||
|
|
await $"git -C {workspace.FullName} config set --local --append url.{repository}.insteadOf ssh://git@{repository.Host}";
|
||
|
|
await $"git -C {workspace.FullName} config set --local --append url.{repository}.insteadOf git://{repository.Host}";
|
||
|
|
|
||
|
|
var header = $"Authorization: Basic {Convert.ToBase64String(Encoding.UTF8.GetBytes($"x-access-token::${access}"))}";
|
||
|
|
await $"git -C {workspace.FullName} config set --local http.{repository}.extraheader '{header}'";
|
||
|
|
}
|
||
|
|
|
||
|
|
var origin = "origin";
|
||
|
|
var reference = result.GetRequiredValue(Reference);
|
||
|
|
|
||
|
|
await $"git -C {workspace.FullName} remote add {origin} {repository}";
|
||
|
|
var list = (await $"git -C {workspace.FullName} ls-remote {origin} {reference}").Split('\t');
|
||
|
|
|
||
|
|
if (list.Length < 2)
|
||
|
|
{
|
||
|
|
throw new InvalidOperationException("git ls-remote resolved nothing");
|
||
|
|
}
|
||
|
|
|
||
|
|
var @sha = list[0].Trim();
|
||
|
|
var @ref = list[1].Trim();
|
||
|
|
|
||
|
|
if (@ref.TryCutPrefix("refs/heads/", out var name))
|
||
|
|
{
|
||
|
|
var remote = $"refs/remotes/{origin}/{name}";
|
||
|
|
var branch = name;
|
||
|
|
|
||
|
|
await $"git -C {workspace.FullName} fetch --no-tags --prune --no-recurse-submodules --depth=1 {origin} +{@sha}:{remote}";
|
||
|
|
await $"git -C {workspace.FullName} checkout --force -B {branch} {remote}";
|
||
|
|
}
|
||
|
|
else if (@ref.TryCutPrefix("refs/pull/", out name))
|
||
|
|
{
|
||
|
|
var remote = $"refs/remotes/pull/{name}";
|
||
|
|
|
||
|
|
// Best-effort parity with the Go code's env.BaseRef/env.HeadRef:
|
||
|
|
var baseRef = Environment.GetEnvironmentVariable("GITHUB_BASE_REF");
|
||
|
|
var headRef = Environment.GetEnvironmentVariable("GITHUB_HEAD_REF");
|
||
|
|
|
||
|
|
var branch =
|
||
|
|
!string.IsNullOrEmpty(baseRef) ? baseRef :
|
||
|
|
!string.IsNullOrEmpty(headRef) ? headRef :
|
||
|
|
throw new InvalidOperationException("pull request can not find base ref for branch");
|
||
|
|
|
||
|
|
await $"git -C {workspace.FullName} fetch --no-tags --prune --no-recurse-submodules --depth=1 {origin} +{@sha}:{remote}";
|
||
|
|
await $"git -C {workspace.FullName} checkout --force -B {branch} {branch}";
|
||
|
|
}
|
||
|
|
else if (@ref.TryCutPrefix("refs/tags/", out name))
|
||
|
|
{
|
||
|
|
var remote = $"refs/tags/{name}";
|
||
|
|
|
||
|
|
await $"git -C {workspace.FullName} fetch --no-tags --prune --no-recurse-submodules --depth=1 {origin} +{@sha}:{remote}";
|
||
|
|
await $"git -C {workspace.FullName} checkout --force {remote}";
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
await $"git -C {workspace.FullName} fetch --no-tags --prune --no-recurse-submodules --depth=1 {origin} {@ref}";
|
||
|
|
await $"git -C {workspace.FullName} checkout --force {@ref}";
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
}
|