Skip to content

Commit 00b84a0

Browse files
authored
Merge pull request #591 from hickford/gitlab
Add GitLab host provider, with web browser, PAT, and username/password support. This provider currently only supports text-based prompts.
2 parents 7aeded4 + d59c2f5 commit 00b84a0

16 files changed

+869
-0
lines changed

Git-Credential-Manager.sln

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GitHub.UI.Windows", "src\wi
5959
EndProject
6060
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Atlassian.Bitbucket.UI.Windows", "src\windows\Atlassian.Bitbucket.UI.Windows\Atlassian.Bitbucket.UI.Windows.csproj", "{3F015046-DAF2-4D2A-96EC-F9782F169E45}"
6161
EndProject
62+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GitLab", "src\shared\GitLab\GitLab.csproj", "{570897DC-A85C-4598-B793-9A00CF710119}"
63+
EndProject
64+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GitLab.Tests", "src\shared\GitLab.Tests\GitLab.Tests.csproj", "{1AF9F7C5-FA2E-48F1-B216-4D5E9A27F393}"
65+
EndProject
6266
Global
6367
GlobalSection(SolutionConfigurationPlatforms) = preSolution
6468
Debug|Any CPU = Debug|Any CPU
@@ -397,6 +401,38 @@ Global
397401
{3F015046-DAF2-4D2A-96EC-F9782F169E45}.MacRelease|Any CPU.ActiveCfg = Release|Any CPU
398402
{3F015046-DAF2-4D2A-96EC-F9782F169E45}.WindowsRelease|Any CPU.ActiveCfg = Release|Any CPU
399403
{3F015046-DAF2-4D2A-96EC-F9782F169E45}.WindowsRelease|Any CPU.Build.0 = Release|Any CPU
404+
{570897DC-A85C-4598-B793-9A00CF710119}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
405+
{570897DC-A85C-4598-B793-9A00CF710119}.Debug|Any CPU.Build.0 = Debug|Any CPU
406+
{570897DC-A85C-4598-B793-9A00CF710119}.MacDebug|Any CPU.ActiveCfg = Debug|Any CPU
407+
{570897DC-A85C-4598-B793-9A00CF710119}.MacDebug|Any CPU.Build.0 = Debug|Any CPU
408+
{570897DC-A85C-4598-B793-9A00CF710119}.MacRelease|Any CPU.ActiveCfg = Debug|Any CPU
409+
{570897DC-A85C-4598-B793-9A00CF710119}.MacRelease|Any CPU.Build.0 = Debug|Any CPU
410+
{570897DC-A85C-4598-B793-9A00CF710119}.Release|Any CPU.ActiveCfg = Release|Any CPU
411+
{570897DC-A85C-4598-B793-9A00CF710119}.Release|Any CPU.Build.0 = Release|Any CPU
412+
{570897DC-A85C-4598-B793-9A00CF710119}.WindowsDebug|Any CPU.ActiveCfg = Debug|Any CPU
413+
{570897DC-A85C-4598-B793-9A00CF710119}.WindowsDebug|Any CPU.Build.0 = Debug|Any CPU
414+
{570897DC-A85C-4598-B793-9A00CF710119}.WindowsRelease|Any CPU.ActiveCfg = Debug|Any CPU
415+
{570897DC-A85C-4598-B793-9A00CF710119}.WindowsRelease|Any CPU.Build.0 = Debug|Any CPU
416+
{570897DC-A85C-4598-B793-9A00CF710119}.LinuxDebug|Any CPU.ActiveCfg = Debug|Any CPU
417+
{570897DC-A85C-4598-B793-9A00CF710119}.LinuxDebug|Any CPU.Build.0 = Debug|Any CPU
418+
{570897DC-A85C-4598-B793-9A00CF710119}.LinuxRelease|Any CPU.ActiveCfg = Debug|Any CPU
419+
{570897DC-A85C-4598-B793-9A00CF710119}.LinuxRelease|Any CPU.Build.0 = Debug|Any CPU
420+
{1AF9F7C5-FA2E-48F1-B216-4D5E9A27F393}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
421+
{1AF9F7C5-FA2E-48F1-B216-4D5E9A27F393}.Debug|Any CPU.Build.0 = Debug|Any CPU
422+
{1AF9F7C5-FA2E-48F1-B216-4D5E9A27F393}.MacDebug|Any CPU.ActiveCfg = Debug|Any CPU
423+
{1AF9F7C5-FA2E-48F1-B216-4D5E9A27F393}.MacDebug|Any CPU.Build.0 = Debug|Any CPU
424+
{1AF9F7C5-FA2E-48F1-B216-4D5E9A27F393}.MacRelease|Any CPU.ActiveCfg = Debug|Any CPU
425+
{1AF9F7C5-FA2E-48F1-B216-4D5E9A27F393}.MacRelease|Any CPU.Build.0 = Debug|Any CPU
426+
{1AF9F7C5-FA2E-48F1-B216-4D5E9A27F393}.Release|Any CPU.ActiveCfg = Release|Any CPU
427+
{1AF9F7C5-FA2E-48F1-B216-4D5E9A27F393}.Release|Any CPU.Build.0 = Release|Any CPU
428+
{1AF9F7C5-FA2E-48F1-B216-4D5E9A27F393}.WindowsDebug|Any CPU.ActiveCfg = Debug|Any CPU
429+
{1AF9F7C5-FA2E-48F1-B216-4D5E9A27F393}.WindowsDebug|Any CPU.Build.0 = Debug|Any CPU
430+
{1AF9F7C5-FA2E-48F1-B216-4D5E9A27F393}.WindowsRelease|Any CPU.ActiveCfg = Debug|Any CPU
431+
{1AF9F7C5-FA2E-48F1-B216-4D5E9A27F393}.WindowsRelease|Any CPU.Build.0 = Debug|Any CPU
432+
{1AF9F7C5-FA2E-48F1-B216-4D5E9A27F393}.LinuxDebug|Any CPU.ActiveCfg = Debug|Any CPU
433+
{1AF9F7C5-FA2E-48F1-B216-4D5E9A27F393}.LinuxDebug|Any CPU.Build.0 = Debug|Any CPU
434+
{1AF9F7C5-FA2E-48F1-B216-4D5E9A27F393}.LinuxRelease|Any CPU.ActiveCfg = Debug|Any CPU
435+
{1AF9F7C5-FA2E-48F1-B216-4D5E9A27F393}.LinuxRelease|Any CPU.Build.0 = Debug|Any CPU
400436
EndGlobalSection
401437
GlobalSection(SolutionProperties) = preSolution
402438
HideSolutionNode = FALSE
@@ -429,6 +465,8 @@ Global
429465
{714ACBE7-0C69-4D8A-9224-22792CAA8264} = {D5277A0E-997E-453A-8CB9-4EFCC8B16A29}
430466
{0A86ED89-1FC5-42AA-925C-4578FA30607A} = {66722747-1B61-40E4-A89B-1AC8E6D62EA9}
431467
{3F015046-DAF2-4D2A-96EC-F9782F169E45} = {66722747-1B61-40E4-A89B-1AC8E6D62EA9}
468+
{570897DC-A85C-4598-B793-9A00CF710119} = {D5277A0E-997E-453A-8CB9-4EFCC8B16A29}
469+
{1AF9F7C5-FA2E-48F1-B216-4D5E9A27F393} = {D5277A0E-997E-453A-8CB9-4EFCC8B16A29}
432470
EndGlobalSection
433471
GlobalSection(ExtensibilityGlobals) = postSolution
434472
SolutionGuid = {0EF9FC65-E6BA-45D4-A455-262A9EA4366B}

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Secure platform credential storage|&#10003;<br/>[(see more)](docs/credstores.md)
2525
Multi-factor authentication support for Azure DevOps|&#10003;|&#10003;|&#10003;
2626
Two-factor authentication support for GitHub|&#10003;|&#10003;|&#10003;
2727
Two-factor authentication support for Bitbucket|&#10003;|&#10003;|&#10003;
28+
Two-factor authentication support for GitLab|&#10003;|&#10003;|&#10003;
2829
Windows Integrated Authentication (NTLM/Kerberos) support|&#10003;|_N/A_|_N/A_
2930
Basic HTTP authentication support|&#10003;|&#10003;|&#10003;
3031
Proxy support|&#10003;|&#10003;|&#10003;

docs/configuration.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ ID|Provider
6464
`azure-repos`|Azure Repos
6565
`github`|GitHub
6666
`bitbucket`|Bitbucket
67+
`gitlab`|GitLab<br/>_(supports OAuth in browser, personal access token and Basic Authentication)_
6768
`generic`|Generic (any other provider not listed above)
6869

6970
Automatic provider selection is based on the remote URL.
@@ -94,6 +95,7 @@ Authority|Provider(s)
9495
`msa`, `microsoft`, `microsoftaccount`,<br/>`aad`, `azure`, `azuredirectory`,</br>`live`, `liveconnect`, `liveid`|Azure Repos<br/>_(supports Microsoft Authentication)_
9596
`github`|GitHub<br/>_(supports GitHub Authentication)_
9697
`bitbucket`|Bitbucket.org<br/>_(supports Basic Authentication and OAuth)_<br/>Bitbucket Server<br/>_(supports Basic Authentication)_
98+
`gitlab`|GitLab<br/>_(supports OAuth in browser, personal access token and Basic Authentication)_
9799
`basic`, `integrated`, `windows`, `kerberos`, `ntlm`,<br/>`tfs`, `sso`|Generic<br/>_(supports Basic and Windows Integrated Authentication)_
98100

99101
#### Example
@@ -269,6 +271,31 @@ git config --global credential.gitHubAuthModes "oauth,basic"
269271

270272
---
271273

274+
### credential.gitLabAuthModes
275+
276+
Override the available authentication modes presented during GitLab authentication.
277+
If this option is not set, then the available authentication modes will be automatically detected.
278+
279+
**Note:** This setting supports multiple values separated by commas.
280+
281+
Value|Authentication Mode
282+
-|-
283+
_(unset)_|Automatically detect modes
284+
`browser`|OAuth authentication via a web browser _(requires a GUI)_
285+
`basic`|Basic authentication using username and password
286+
`pat`|Personal Access Token (pat)-based authentication
287+
288+
#### Example
289+
290+
```shell
291+
git config --global credential.gitLabAuthModes "browser"
292+
```
293+
294+
**Also see: [GCM_GITLAB_AUTHMODES](environment.md#GCM_GITLAB_AUTHMODES)**
295+
296+
---
297+
298+
272299
### credential.namespace
273300

274301
Use a custom namespace prefix for credentials read and written in the OS credential store.

docs/environment.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ ID|Provider
169169
`auto` _(default)_|_\[automatic\]_ ([learn more](autodetect.md))
170170
`azure-repos`|Azure Repos
171171
`github`|GitHub
172+
`gitlab`|GitLab<br/>_(supports OAuth in browser, personal access token and Basic Authentication)_
172173
`generic`|Generic (any other provider not listed above)
173174

174175
Automatic provider selection is based on the remote URL.
@@ -206,6 +207,7 @@ Authority|Provider(s)
206207
`auto` _(default)_|_\[automatic\]_
207208
`msa`, `microsoft`, `microsoftaccount`,<br/>`aad`, `azure`, `azuredirectory`,</br>`live`, `liveconnect`, `liveid`|Azure Repos<br/>_(supports Microsoft Authentication)_
208209
`github`|GitHub<br/>_(supports GitHub Authentication)_
210+
`gitlab`|GitLab<br/>_(supports OAuth in browser, personal access token and Basic Authentication)_
209211
`basic`, `integrated`, `windows`, `kerberos`, `ntlm`,<br/>`tfs`, `sso`|Generic<br/>_(supports Basic and Windows Integrated Authentication)_
210212

211213
#### Example
@@ -436,6 +438,36 @@ export GCM_GITHUB_AUTHMODES="oauth,basic"
436438

437439
---
438440

441+
### GCM_GITLAB_AUTHMODES
442+
443+
Override the available authentication modes presented during GitLab authentication.
444+
If this option is not set, then the available authentication modes will be automatically detected.
445+
446+
**Note:** This setting supports multiple values separated by commas.
447+
448+
Value|Authentication Mode
449+
-|-
450+
_(unset)_|Automatically detect modes
451+
`browser`|OAuth authentication via a web browser _(requires a GUI)_
452+
`basic`|Basic authentication using username and password
453+
`pat`|Personal Access Token (pat)-based authentication
454+
455+
##### Windows
456+
457+
```batch
458+
SET GCM_GITLAB_AUTHMODES="browser"
459+
```
460+
461+
##### macOS/Linux
462+
463+
```bash
464+
export GCM_GITLAB_AUTHMODES="browser"
465+
```
466+
467+
**Also see: [credential.gitLabAuthModes](configuration.md#credentialgitLabAuthModes)**
468+
469+
---
470+
439471
### GCM_NAMESPACE
440472

441473
Use a custom namespace prefix for credentials read and written in the OS credential store.

docs/gitlab.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# GitLab support
2+
3+
Git Credential Manager supports [gitlab.com](https://gitlab.com) out the box.
4+
5+
## Using on a another instance
6+
7+
To use on another instance, eg. `https://gitlab.example.com` requires setup and configuration:
8+
9+
1. [Create an OAuth application](https://docs.gitlab.com/ee/integration/oauth_provider.html). This can be at the user, group or instance level. Specify a name and use a redirect URI of `http://127.0.0.1/`. _Unselect_ the 'Confidential' option, and ensure the 'Expire access tokens' option is selected. Set the scope to 'write_repository'.
10+
2. Copy the application ID and configure `git config --global credential.https://gitlab.example.com.GitLabDevClientId <APPLICATION_ID>`
11+
3. Copy the application secret and configure `git config --global credential.https://gitlab.example.com.GitLabDevClientSecret <APPLICATION_SECRET>`
12+
4. Configure authentication modes to include 'browser' `git config --global credential.https://gitlab.example.com.gitLabAuthModes browser`
13+
5. For good measure, configure `git config --global credential.https://gitlab.example.com.provider gitlab`
14+
6. Verify the config is as expected `git config --global --get-urlmatch credential https://gitlab.example.com`
15+
16+
### Clearing config
17+
18+
```
19+
git config --global --unset-all credential.https://gitlab.example.com.GitLabDevClientId
20+
git config --global --unset-all credential.https://gitlab.example.com.GitLabDevClientSecret
21+
git config --global --unset-all credential.https://gitlab.example.com.provider
22+
```
23+
24+
## Preferences
25+
26+
```
27+
Select an authentication method for 'https://gitlab.com/':
28+
1. Web browser (default)
29+
2. Personal access token
30+
3. Username/password
31+
option (enter for default):
32+
```
33+
34+
If you have a preferred authentication mode, you can specify [credential.gitLabAuthModes](configuration.md#credential.gitLabAuthModes):
35+
36+
`git config --global credential.gitlabauthmodes browser`
37+
38+
## Caveats
39+
40+
Improved support requires changes in GitLab. Please vote for these issues if they affect you:
41+
42+
1. No support for OAuth device authorization (necessary for machines without web browser) https://gitlab.com/gitlab-org/gitlab/-/issues/332682
43+
2. Only domains with prefix `gitlab.` are recognised as GitLab remotes https://gitlab.com/gitlab-org/gitlab/-/issues/349464
44+
3. Username/password authentication is suggested even if disabled on server https://gitlab.com/gitlab-org/gitlab/-/issues/349463

src/shared/Git-Credential-Manager/Git-Credential-Manager.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
<ItemGroup>
1818
<ProjectReference Include="..\Atlassian.Bitbucket\Atlassian.Bitbucket.csproj" />
1919
<ProjectReference Include="..\GitHub\GitHub.csproj" />
20+
<ProjectReference Include="..\GitLab\GitLab.csproj" />
2021
<ProjectReference Include="..\Microsoft.AzureRepos\Microsoft.AzureRepos.csproj" />
2122
<ProjectReference Include="..\Core\Core.csproj" />
2223
</ItemGroup>

src/shared/Git-Credential-Manager/Program.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using Atlassian.Bitbucket;
33
using GitHub;
4+
using GitLab;
45
using Microsoft.AzureRepos;
56
using GitCredentialManager.Authentication;
67

@@ -35,6 +36,7 @@ public static void Main(string[] args)
3536
app.RegisterProvider(new AzureReposHostProvider(context), HostProviderPriority.Normal);
3637
app.RegisterProvider(new BitbucketHostProvider(context), HostProviderPriority.Normal);
3738
app.RegisterProvider(new GitHubHostProvider(context), HostProviderPriority.Normal);
39+
app.RegisterProvider(new GitLabHostProvider(context), HostProviderPriority.Normal);
3840
app.RegisterProvider(new GenericHostProvider(context), HostProviderPriority.Low);
3941

4042
int exitCode = app.RunAsync(args)
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>net5.0</TargetFramework>
5+
<IsPackable>false</IsPackable>
6+
<IsTestProject>true</IsTestProject>
7+
<LangVersion>latest</LangVersion>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="coverlet.collector" Version="3.1.0">
12+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
13+
<PrivateAssets>all</PrivateAssets>
14+
</PackageReference>
15+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.0" />
16+
<PackageReference Include="ReportGenerator" Version="4.8.13" />
17+
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
18+
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
19+
</ItemGroup>
20+
21+
<ItemGroup>
22+
<ProjectReference Include="..\GitLab\GitLab.csproj" />
23+
<ProjectReference Include="..\TestInfrastructure\TestInfrastructure.csproj" />
24+
</ItemGroup>
25+
26+
</Project>
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Threading.Tasks;
5+
using GitCredentialManager.Tests.Objects;
6+
using Moq;
7+
using Moq.Protected;
8+
using Xunit;
9+
10+
namespace GitLab.Tests
11+
{
12+
public class GitLabAuthenticationTests
13+
{
14+
[Fact]
15+
public void GitLabAuthentication_GetAuthenticationAsync_AuthenticationModesNone_ThrowsException()
16+
{
17+
var context = new TestCommandContext();
18+
var auth = new GitLabAuthentication(context);
19+
Assert.Throws<ArgumentException>("modes",
20+
() => auth.GetAuthentication(null, null, AuthenticationModes.None)
21+
);
22+
}
23+
24+
[Theory]
25+
[InlineData(AuthenticationModes.Browser)]
26+
public void GitLabAuthentication_GetAuthenticationAsync_SingleChoice_TerminalAndInteractionNotRequired(GitLab.AuthenticationModes modes)
27+
{
28+
var context = new TestCommandContext();
29+
context.Settings.IsTerminalPromptsEnabled = false;
30+
context.Settings.IsInteractionAllowed = false;
31+
context.SessionManager.IsDesktopSession = true; // necessary for browser
32+
var auth = new GitLabAuthentication(context);
33+
var result = auth.GetAuthentication(null, null, modes);
34+
Assert.Equal(modes, result.AuthenticationMode);
35+
}
36+
37+
[Fact]
38+
public void GitLabAuthentication_GetAuthenticationAsync_TerminalPromptsDisabled_Throws()
39+
{
40+
var context = new TestCommandContext();
41+
context.Settings.IsTerminalPromptsEnabled = false;
42+
var auth = new GitLabAuthentication(context);
43+
var exception = Assert.Throws<InvalidOperationException>(
44+
() => auth.GetAuthentication(null, null, AuthenticationModes.All)
45+
);
46+
Assert.Equal("Cannot prompt because terminal prompts have been disabled.", exception.Message);
47+
}
48+
49+
[Fact]
50+
public void GitLabAuthentication_GetAuthenticationAsync_Terminal()
51+
{
52+
var context = new TestCommandContext();
53+
var auth = new GitLabAuthentication(context);
54+
context.SessionManager.IsDesktopSession = true;
55+
context.Terminal.Prompts["option (enter for default)"] = "";
56+
var result = auth.GetAuthentication(null, null, AuthenticationModes.All);
57+
Assert.Equal(AuthenticationModes.Browser, result.AuthenticationMode);
58+
}
59+
60+
[Fact]
61+
public void GitLabAuthentication_GetAuthenticationAsync_ChoosePat()
62+
{
63+
var context = new TestCommandContext();
64+
var auth = new GitLabAuthentication(context);
65+
context.Terminal.Prompts["option (enter for default)"] = "";
66+
context.Terminal.Prompts["Username"] = "username";
67+
context.Terminal.SecretPrompts["Personal access token"] = "token";
68+
var result = auth.GetAuthentication(null, null, AuthenticationModes.All);
69+
Assert.Equal(AuthenticationModes.Pat, result.AuthenticationMode);
70+
Assert.Equal("username", result.Credential.Account);
71+
Assert.Equal("token", result.Credential.Password);
72+
}
73+
74+
[Fact]
75+
public void GitLabAuthentication_GetAuthenticationAsync_ChooseBasic()
76+
{
77+
var context = new TestCommandContext();
78+
var auth = new GitLabAuthentication(context);
79+
context.Terminal.Prompts["option (enter for default)"] = "2";
80+
context.Terminal.Prompts["Username"] = "username";
81+
context.Terminal.SecretPrompts["Password"] = "password";
82+
var result = auth.GetAuthentication(null, null, AuthenticationModes.All);
83+
Assert.Equal(AuthenticationModes.Basic, result.AuthenticationMode);
84+
Assert.Equal("username", result.Credential.Account);
85+
Assert.Equal("password", result.Credential.Password);
86+
}
87+
88+
[Fact]
89+
public void GitLabAuthentication_GetAuthenticationAsync_AuthenticationModesAll_RequiresInteraction()
90+
{
91+
var context = new TestCommandContext();
92+
context.Settings.IsInteractionAllowed = false;
93+
var auth = new GitLabAuthentication(context);
94+
var exception = Assert.Throws<InvalidOperationException>(
95+
() => auth.GetAuthentication(new Uri("https://GitLab.com"), null, AuthenticationModes.All)
96+
);
97+
Assert.Equal("Cannot prompt because user interactivity has been disabled.", exception.Message);
98+
}
99+
}
100+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Threading.Tasks;
4+
using GitCredentialManager;
5+
using GitCredentialManager.Authentication.OAuth;
6+
using GitCredentialManager.Tests.Objects;
7+
using Moq;
8+
using Xunit;
9+
10+
namespace GitLab.Tests
11+
{
12+
public class GitLabHostProviderTests
13+
{
14+
[Theory]
15+
[InlineData("https", "gitlab.com", true)]
16+
[InlineData("http", "gitlab.com", true)]
17+
[InlineData("https", "gitlab.example.com", true)]
18+
[InlineData("https", "github.com", false)]
19+
[InlineData("https", "github.example.com", false)]
20+
public void GitLabHostProvider_IsSupported(string protocol, string host, bool expected)
21+
{
22+
var input = new InputArguments(new Dictionary<string, string>
23+
{
24+
["protocol"] = protocol,
25+
["host"] = host,
26+
});
27+
28+
var provider = new GitLabHostProvider(new TestCommandContext());
29+
Assert.Equal(expected, provider.IsSupported(input));
30+
}
31+
}
32+
}

0 commit comments

Comments
 (0)