Skip to content

Commit 324e4ba

Browse files
committed
Rework common functionality in Core, add a simpler gist tool
The simpler gist-specific tool makes some assumptions about the ref being only for gists and about the target temp location. Perhaps the generic runcs could also accomodate gists, but for now this seems more scoped and still useful and intuitive to run using `dnx gist [owner]/[gist]`.
1 parent 7ac3dbf commit 324e4ba

30 files changed

+445
-329
lines changed

.netconfig

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -120,13 +120,13 @@
120120
sha = 032439dbf180fca0539a5bd3a019f18ab3484b76
121121
etag = da7c0104131bd474b52fc9bc9f9bda6470e24ae38d4fb9f5c4f719bc01370ab5
122122
weak
123-
[file "src/runcs/NoGitWrappers.cs"]
124-
url = https://github.com/devlooped/CredentialManager/blob/dev/tests/src/CredentialManager/NoGitWrappers.cs
125-
sha = 78c647b20acddc1f172df20d479ce3d0548d05fd
126-
etag = 2e2cd6992920ff319bdefd5735926cd5bd4ffef39b4214e3786b96946382985b
127-
weak
128123
[file "src/Tests/Attributes.cs"]
129124
url = https://github.com/devlooped/catbag/blob/main/Xunit/Attributes.cs
130125
sha = 40914971d4d6b42d6f8a90923b131136f7e609a5
131126
etag = c77e7b435ce1df06fb60a3b0e15a0833d8e45d4d19f366c6184140ebb4814b1a
132127
weak
128+
[file "src/Core/CommandContext.cs"]
129+
url = https://github.com/devlooped/CredentialManager/blob/main/src/CredentialManager/CommandContext.cs
130+
sha = 8c27185b33465e0f729ea308e077f2287a160de9
131+
etag = 50acc7051f270c94cd087b6b63c223b5290e00d0c05477a3c231de3e4d582065
132+
weak

runcs.sln

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "runcs", "src\runcs\runcs.cs
77
EndProject
88
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "src\Tests\Tests.csproj", "{C6B47AE9-1575-47B7-8DEC-11B94AF09486}"
99
EndProject
10+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "gist", "src\gist\gist.csproj", "{883AA43F-2445-446C-8C4F-5F60F6B820E6}"
11+
EndProject
12+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core", "src\Core\Core.csproj", "{F6154AD8-2474-4B73-B3FD-C829901DBAA9}"
13+
EndProject
1014
Global
1115
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1216
Debug|Any CPU = Debug|Any CPU
@@ -21,6 +25,14 @@ Global
2125
{C6B47AE9-1575-47B7-8DEC-11B94AF09486}.Debug|Any CPU.Build.0 = Debug|Any CPU
2226
{C6B47AE9-1575-47B7-8DEC-11B94AF09486}.Release|Any CPU.ActiveCfg = Release|Any CPU
2327
{C6B47AE9-1575-47B7-8DEC-11B94AF09486}.Release|Any CPU.Build.0 = Release|Any CPU
28+
{883AA43F-2445-446C-8C4F-5F60F6B820E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29+
{883AA43F-2445-446C-8C4F-5F60F6B820E6}.Debug|Any CPU.Build.0 = Debug|Any CPU
30+
{883AA43F-2445-446C-8C4F-5F60F6B820E6}.Release|Any CPU.ActiveCfg = Release|Any CPU
31+
{883AA43F-2445-446C-8C4F-5F60F6B820E6}.Release|Any CPU.Build.0 = Release|Any CPU
32+
{F6154AD8-2474-4B73-B3FD-C829901DBAA9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33+
{F6154AD8-2474-4B73-B3FD-C829901DBAA9}.Debug|Any CPU.Build.0 = Debug|Any CPU
34+
{F6154AD8-2474-4B73-B3FD-C829901DBAA9}.Release|Any CPU.ActiveCfg = Release|Any CPU
35+
{F6154AD8-2474-4B73-B3FD-C829901DBAA9}.Release|Any CPU.Build.0 = Release|Any CPU
2436
EndGlobalSection
2537
GlobalSection(SolutionProperties) = preSolution
2638
HideSolutionNode = FALSE
Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,20 @@ namespace GitCredentialManager;
1313
/// A wrapper for <see cref="ICommandContext"/> that overrides the namespace for credentials and also
1414
/// allows git-less usage except for the git cache store.
1515
/// </summary>
16-
class CommandContextWrapper : ICommandContext
16+
partial class CommandContextAdapter : ICommandContext
1717
{
1818
readonly CommandContext context;
19-
readonly ICredentialStore? store;
2019
readonly ISettings settings;
2120
readonly IHttpClientFactory clientFactory;
21+
ICredentialStore store;
2222

23-
public CommandContextWrapper(CommandContext context, string? @namespace = default, ICredentialStore? store = default)
23+
public CommandContextAdapter(CommandContext context, string? @namespace = default)
2424
{
2525
this.context = context;
26-
this.store = store;
2726

28-
settings = new SettingsWrapper(
27+
store = new CredentialStore(this);
28+
29+
settings = new SettingsAdapter(
2930
context.Settings is WindowsSettings ?
3031
new NoGitWindowsSettings(context.Environment, context.Git, context.Trace) :
3132
new NoGitSettings(context.Environment, context.Git), @namespace);
@@ -34,10 +35,13 @@ context.Settings is WindowsSettings ?
3435
context.FileSystem, context.Trace, context.Trace2, settings, context.Streams);
3536
}
3637

37-
3838
public ISettings Settings => settings;
3939

40-
public ICredentialStore CredentialStore => store ?? ((ICommandContext)context).CredentialStore;
40+
public ICredentialStore CredentialStore
41+
{
42+
get => store;
43+
set => store = value;
44+
}
4145

4246
public IHttpClientFactory HttpClientFactory => clientFactory;
4347

@@ -138,7 +142,7 @@ public IEnumerable<string> GetRegex(GitConfigurationLevel level, GitConfiguratio
138142

139143
public bool TryGet(GitConfigurationLevel level, GitConfigurationType type, string name, out string value)
140144
{
141-
value = GetAll(level, type, name).FirstOrDefault();
145+
value = GetAll(level, type, name).FirstOrDefault()!;
142146
return value is not null;
143147
}
144148

@@ -152,11 +156,14 @@ public bool TryGet(GitConfigurationLevel level, GitConfigurationType type, strin
152156
}
153157
}
154158

159+
/// <summary>Adapts <see cref="Settings"/> to use <see cref="NoGit"/>.</summary>
155160
class NoGitSettings(IEnvironment environment, IGit git) : Settings(environment, new NoGit(git)) { }
156161

162+
/// <summary>Adapts <see cref="WindowsSettings"/> to use <see cref="NoGit"/>.</summary>
157163
class NoGitWindowsSettings(IEnvironment environment, IGit git, ITrace trace) : WindowsSettings(environment, new NoGit(git), trace) { }
158164

159-
class SettingsWrapper(ISettings settings, string? @namespace) : ISettings
165+
/// <summary>Allows overriding the credential namespace.</summary>
166+
class SettingsAdapter(ISettings settings, string? @namespace) : ISettings
160167
{
161168
public string CredentialNamespace => @namespace ?? settings.CredentialNamespace;
162169

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
extern alias Devlooped;
2+
using GitCredentialManager;
3+
4+
namespace Devlooped;
5+
6+
public static class CommandContextExtensions
7+
{
8+
extension(CommandContext)
9+
{
10+
public static ICommandContext Create(Devlooped::GitCredentialManager.ICredentialStore? store = default, string? @namespace = default)
11+
=> new CommandContextAdapter(new CommandContext(), @namespace)
12+
{
13+
// We need adapting since Devlooped.CredentialManager has its own version of ICredentialStore,
14+
// but since we use the GCM version, we need to adapt it here so we can preserve the functionality.
15+
CredentialStore = new CredentialStoreAdapter(store ?? Devlooped::GitCredentialManager.CredentialManager.Create(@namespace))
16+
};
17+
}
18+
19+
class CredentialStoreAdapter(Devlooped::GitCredentialManager.ICredentialStore store) : ICredentialStore
20+
{
21+
public void AddOrUpdate(string service, string account, string secret) => store.AddOrUpdate(service, account, secret);
22+
public ICredential Get(string service, string account) => store.Get(service, account) is { } creds ? new CredentialAdapter(creds) : default!;
23+
public IList<string> GetAccounts(string service) => store.GetAccounts(service);
24+
public bool Remove(string service, string account) => store.Remove(service, account);
25+
}
26+
27+
class CredentialAdapter(Devlooped::GitCredentialManager.ICredential credential) : ICredential
28+
{
29+
public string Account => credential.Account;
30+
public string Password => credential.Password;
31+
}
32+
}

src/Core/Core.csproj

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net10.0</TargetFramework>
5+
<AssemblyName>Devlooped</AssemblyName>
6+
<RootNamespace>Devlooped</RootNamespace>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="DotNetConfig" Version="1.2.0" />
11+
<PackageReference Include="DotNetConfig.Configuration" Version="1.2.0" />
12+
<PackageReference Include="Devlooped.CredentialManager" Version="42.42.517-main" Aliases="Devlooped" />
13+
<PackageReference Include="git-credential-manager" Version="2.6.1" IncludeAssets="tools" GeneratePathProperty="true" />
14+
<PackageReference Include="ThisAssembly.Project" Version="2.0.14" PrivateAssets="all" />
15+
<PackageReference Include="LibGit2Sharp" Version="0.31.0" />
16+
</ItemGroup>
17+
18+
<ItemGroup Condition="'$(Pkggit-credential-manager)' != ''">
19+
<Reference Include="Atlassian.Bitbucket" HintPath="$(Pkggit-credential-manager)\tools\net8.0\any\Atlassian.Bitbucket.dll" />
20+
<Reference Include="Microsoft.AzureRepos" HintPath="$(Pkggit-credential-manager)\tools\net8.0\any\Microsoft.AzureRepos.dll" />
21+
<Reference Include="GitHub" HintPath="$(Pkggit-credential-manager)\tools\net8.0\any\GitHub.dll" />
22+
<Reference Include="GitLab" HintPath="$(Pkggit-credential-manager)\tools\net8.0\any\GitLab.dll" />
23+
<Reference Include="gcmcore" HintPath="$(Pkggit-credential-manager)\tools\net8.0\any\gcmcore.dll" />
24+
</ItemGroup>
25+
26+
</Project>
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
extern alias Devlooped;
22
using GitCredentialManager;
33

4-
namespace Devlooped.Http;
4+
namespace Core;
55

6-
static class CredentialsExtensions
6+
public static class CredentialsExtensions
77
{
88
public static ICredential? GetCredential(this Devlooped::GitCredentialManager.ICredentialStore store, string url)
99
{

src/Core/DotnetMuxer.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright (c) .NET Foundation and contributors. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System.Runtime.InteropServices;
5+
6+
namespace Devlooped;
7+
8+
public static class DotnetMuxer
9+
{
10+
public static FileInfo? Path { get; }
11+
12+
static DotnetMuxer()
13+
{
14+
var muxerFileName = ExecutableName("dotnet");
15+
var fxDepsFile = GetDataFromAppDomain("FX_DEPS_FILE");
16+
17+
if (string.IsNullOrEmpty(fxDepsFile))
18+
return;
19+
20+
var muxerDir = new FileInfo(fxDepsFile).Directory?.Parent?.Parent?.Parent;
21+
if (muxerDir == null)
22+
return;
23+
24+
var muxerCandidate = new FileInfo(System.IO.Path.Combine(muxerDir.FullName, muxerFileName));
25+
if (muxerCandidate.Exists)
26+
Path = muxerCandidate;
27+
}
28+
29+
public static string? GetDataFromAppDomain(string propertyName)
30+
=> AppContext.GetData(propertyName) as string;
31+
32+
public static string ExecutableName(this string withoutExtension)
33+
=> RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
34+
? withoutExtension + ".exe"
35+
: withoutExtension;
36+
}
Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
using System;
2-
using System.IO;
3-
using System.Runtime.InteropServices;
1+
using System.Runtime.InteropServices;
42

53
namespace Devlooped;
64

7-
class DownloadManager
5+
public static class DownloadManager
86
{
7+
extension(RemoteRef location)
8+
{
9+
public string TempPath => Path.Join(GetTempRoot(), location.Owner, location.Repo, location.Ref ?? "main");
10+
}
11+
912
/// <summary>
1013
/// Obtains the temporary directory root, e.g., <c>/tmp/dotnet/runcs/</c>.
1114
/// </summary>
Lines changed: 8 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,8 @@
11
extern alias Devlooped;
2-
3-
using System;
4-
using System.Collections.Generic;
52
using System.Diagnostics;
6-
using System.IO;
73
using System.Net;
8-
using System.Net.Http;
9-
using System.Threading;
10-
using System.Threading.Tasks;
114
using Atlassian.Bitbucket;
5+
using Core;
126
using Devlooped.Http;
137
using GitCredentialManager;
148
using LibGit2Sharp;
@@ -27,7 +21,7 @@ public abstract class DownloadProvider
2721
public abstract Task<HttpResponseMessage> GetAsync(RemoteRef location);
2822
}
2923

30-
public class GitHubDownloadProvider : DownloadProvider
24+
public class GitHubDownloadProvider(bool gist = false) : DownloadProvider
3125
{
3226
static readonly HttpClient http = new(new GitHubAuthHandler(
3327
new RedirectingHttpHandler(
@@ -49,40 +43,14 @@ public override async Task<HttpResponseMessage> GetAsync(RemoteRef location)
4943
HttpCompletionOption.ResponseHeadersRead);
5044
}
5145

52-
var request = new HttpRequestMessage(HttpMethod.Get, GetBranchUri(location)).WithTag(location.ETag);
53-
var response = await http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
54-
// Cascading attempt for branch, tag, sha
55-
if (response.StatusCode == HttpStatusCode.NotFound && !string.IsNullOrEmpty(location.Ref))
56-
{
57-
response = await http.SendAsync(response.Retry(GetTagUri(location)), HttpCompletionOption.ResponseHeadersRead);
58-
if (response.StatusCode == HttpStatusCode.NotFound)
59-
{
60-
response = await http.SendAsync(response.Retry(GetShaUri(location)), HttpCompletionOption.ResponseHeadersRead);
61-
}
62-
}
46+
var subdomain = gist ? "gist." : "";
47+
var request = new HttpRequestMessage(HttpMethod.Get,
48+
// Direct archive link works for branch, tag, sha
49+
new Uri($"https://{subdomain}github.com/{location.Owner}/{location.Repo}/archive/{(location.Ref ?? "main")}.zip"))
50+
.WithTag(location.ETag);
6351

64-
return response;
52+
return await http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
6553
}
66-
67-
static Uri GetBranchUri(RemoteRef location)
68-
{
69-
var url = $"https://github.com/{location.Owner}/{location.Repo}/archive";
70-
71-
if (!string.IsNullOrEmpty(location.Ref))
72-
url += "/refs/heads/" + location.Ref;
73-
else
74-
url += "/refs/heads/main"; // TODO: get default branch for repo
75-
76-
url += ".zip";
77-
78-
return new Uri(url);
79-
}
80-
81-
static Uri GetTagUri(RemoteRef location)
82-
=> new($"https://github.com/{location.Owner}/{location.Repo}/archive/refs/tags/{location.Ref}.zip");
83-
84-
static Uri GetShaUri(RemoteRef location)
85-
=> new($"https://github.com/{location.Owner}/{location.Repo}/archive/{location.Ref}.zip");
8654
}
8755

8856
public class GitLabDownloadProvider : DownloadProvider
Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.Net.Http;
4-
using System.Text;
5-
using System.Threading;
6-
using System.Threading.Tasks;
1+
using System.Text;
72
using GitCredentialManager;
83
using Microsoft.AzureRepos;
94

105
namespace Devlooped.Http;
116

12-
class AzureRepoAuthHandler(HttpMessageHandler inner) : AuthHandler(inner)
7+
public class AzureRepoAuthHandler(HttpMessageHandler inner) : DelegatingHandler(inner)
138
{
149
ICredential? credential;
1510

0 commit comments

Comments
 (0)