Skip to content

Commit 7d5682e

Browse files
authored
Merge pull request #1485 from github/brianaj/external-pr-1464
Add URL validation for organization, repository, and enterprise arguments
2 parents 5a97e6b + 94d4c0a commit 7d5682e

File tree

29 files changed

+813
-3
lines changed

29 files changed

+813
-3
lines changed

RELEASENOTES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
- Added validation to detect and return clear error messages when a URL is provided instead of a name for organization, repository, or enterprise arguments (e.g., `--github-org`, `--github-target-org`, `--source-repo`, `--github-target-enterprise`)
12
- Added `--target-api-url` as an optional arg to the `add-team-to-repo` command

src/Octoshift/Commands/CreateTeam/CreateTeamCommandArgs.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
namespace OctoshiftCLI.Commands.CreateTeam;
1+
using OctoshiftCLI.Extensions;
2+
using OctoshiftCLI.Services;
3+
4+
namespace OctoshiftCLI.Commands.CreateTeam;
25

36
public class CreateTeamCommandArgs : CommandArgs
47
{
@@ -8,4 +11,12 @@ public class CreateTeamCommandArgs : CommandArgs
811
[Secret]
912
public string GithubPat { get; set; }
1013
public string TargetApiUrl { get; set; }
14+
15+
public override void Validate(OctoLogger log)
16+
{
17+
if (GithubOrg.IsUrl())
18+
{
19+
throw new OctoshiftCliException($"The --github-org option expects an organization name, not a URL. Please provide just the organization name (e.g., 'my-org' instead of 'https://github.com/my-org').");
20+
}
21+
}
1122
}

src/Octoshift/Commands/DownloadLogs/DownloadLogsCommandArgs.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-

1+
using OctoshiftCLI.Extensions;
2+
using OctoshiftCLI.Services;
3+
24
namespace OctoshiftCLI.Commands.DownloadLogs;
35

46
public class DownloadLogsCommandArgs : CommandArgs
@@ -11,4 +13,17 @@ public class DownloadLogsCommandArgs : CommandArgs
1113
public string GithubPat { get; set; }
1214
public string MigrationLogFile { get; set; }
1315
public bool Overwrite { get; set; }
16+
17+
public override void Validate(OctoLogger log)
18+
{
19+
if (GithubOrg.IsUrl())
20+
{
21+
throw new OctoshiftCliException("The --github-org option expects an organization name, not a URL. Please provide just the organization name (e.g., 'my-org' instead of 'https://github.com/my-org').");
22+
}
23+
24+
if (GithubRepo.IsUrl())
25+
{
26+
throw new OctoshiftCliException("The --github-repo option expects a repository name, not a URL. Please provide just the repository name (e.g., 'my-repo' instead of 'https://github.com/my-org/my-repo').");
27+
}
28+
}
1429
}

src/Octoshift/Commands/GenerateMannequinCsv/GenerateMannequinCsvCommandArgs.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using System.IO;
2+
using OctoshiftCLI.Extensions;
3+
using OctoshiftCLI.Services;
24

35
namespace OctoshiftCLI.Commands.GenerateMannequinCsv;
46

@@ -10,4 +12,12 @@ public class GenerateMannequinCsvCommandArgs : CommandArgs
1012
[Secret]
1113
public string GithubPat { get; set; }
1214
public string TargetApiUrl { get; set; }
15+
16+
public override void Validate(OctoLogger log)
17+
{
18+
if (GithubOrg.IsUrl())
19+
{
20+
throw new OctoshiftCliException("The --github-org option expects an organization name, not a URL. Please provide just the organization name (e.g., 'my-org' instead of 'https://github.com/my-org').");
21+
}
22+
}
1323
}

src/Octoshift/Commands/GrantMigratorRole/GrantMigratorRoleCommandArgs.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ public class GrantMigratorRoleCommandArgs : CommandArgs
1515

1616
public override void Validate(OctoLogger log)
1717
{
18+
if (GithubOrg.IsUrl())
19+
{
20+
throw new OctoshiftCliException($"The --github-org option expects an organization name, not a URL. Please provide just the organization name (e.g., 'my-org' instead of 'https://github.com/my-org').");
21+
}
22+
1823
ActorType = ActorType?.ToUpper();
1924

2025
if (ActorType is "TEAM" or "USER")

src/Octoshift/Commands/ReclaimMannequin/ReclaimMannequinCommandArgs.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using OctoshiftCLI.Services;
1+
using OctoshiftCLI.Extensions;
2+
using OctoshiftCLI.Services;
23

34
namespace OctoshiftCLI.Commands.ReclaimMannequin;
45

@@ -17,6 +18,11 @@ public class ReclaimMannequinCommandArgs : CommandArgs
1718
public string TargetApiUrl { get; set; }
1819
public override void Validate(OctoLogger log)
1920
{
21+
if (GithubOrg.IsUrl())
22+
{
23+
throw new OctoshiftCliException("The --github-org option expects an organization name, not a URL. Please provide just the organization name (e.g., 'my-org' instead of 'https://github.com/my-org').");
24+
}
25+
2026
if (string.IsNullOrEmpty(Csv) && (string.IsNullOrEmpty(MannequinUser) || string.IsNullOrEmpty(TargetUser)))
2127
{
2228
throw new OctoshiftCliException($"Either --csv or --mannequin-user and --target-user must be specified");

src/Octoshift/Commands/RevokeMigratorRole/RevokeMigratorRoleCommandArgs.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ public class RevokeMigratorRoleCommandArgs : CommandArgs
1515

1616
public override void Validate(OctoLogger log)
1717
{
18+
if (GithubOrg.IsUrl())
19+
{
20+
throw new OctoshiftCliException($"The --github-org option expects an organization name, not a URL. Please provide just the organization name (e.g., 'my-org' instead of 'https://github.com/my-org').");
21+
}
22+
1823
ActorType = ActorType?.ToUpper();
1924

2025
if (ActorType is "TEAM" or "USER")

src/Octoshift/Extensions/StringExtensions.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,12 @@ public static class StringExtensions
2626
public static string EscapeDataString(this string value) => Uri.EscapeDataString(value);
2727

2828
public static byte[] ToBytes(this string s) => Encoding.UTF8.GetBytes(s);
29+
30+
public static bool IsUrl(this string s)
31+
{
32+
return !s.IsNullOrWhiteSpace()
33+
&& Uri.TryCreate(s, UriKind.Absolute, out var uri)
34+
&& (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps);
35+
}
2936
}
3037
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using FluentAssertions;
2+
using Moq;
3+
using OctoshiftCLI.Commands.CreateTeam;
4+
using OctoshiftCLI.Services;
5+
using Xunit;
6+
7+
namespace OctoshiftCLI.Tests.Octoshift.Commands.CreateTeam;
8+
9+
public class CreateTeamCommandArgsTests
10+
{
11+
private readonly Mock<OctoLogger> _mockOctoLogger = TestHelpers.CreateMock<OctoLogger>();
12+
13+
private const string GITHUB_ORG = "foo-org";
14+
private const string TEAM_NAME = "my-team";
15+
16+
[Fact]
17+
public void Validate_Throws_When_GithubOrg_Is_Url()
18+
{
19+
var args = new CreateTeamCommandArgs
20+
{
21+
GithubOrg = "http://github.com/my-org",
22+
TeamName = TEAM_NAME
23+
};
24+
25+
FluentActions.Invoking(() => args.Validate(_mockOctoLogger.Object))
26+
.Should()
27+
.ThrowExactly<OctoshiftCliException>()
28+
.WithMessage("The --github-org option expects an organization name, not a URL. Please provide just the organization name (e.g., 'my-org' instead of 'https://github.com/my-org').");
29+
}
30+
31+
[Fact]
32+
public void Validate_Succeeds_With_Valid_Name()
33+
{
34+
var args = new CreateTeamCommandArgs
35+
{
36+
GithubOrg = GITHUB_ORG,
37+
TeamName = TEAM_NAME
38+
};
39+
40+
args.Validate(_mockOctoLogger.Object);
41+
42+
args.GithubOrg.Should().Be(GITHUB_ORG);
43+
}
44+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
using FluentAssertions;
2+
using Moq;
3+
using OctoshiftCLI.Commands.DownloadLogs;
4+
using OctoshiftCLI.Services;
5+
using Xunit;
6+
7+
namespace OctoshiftCLI.Tests.Octoshift.Commands.DownloadLogs;
8+
9+
public class DownloadLogsCommandArgsTests
10+
{
11+
private readonly Mock<OctoLogger> _mockOctoLogger = TestHelpers.CreateMock<OctoLogger>();
12+
13+
private const string GITHUB_ORG = "foo-org";
14+
private const string GITHUB_REPO = "foo-repo";
15+
16+
[Fact]
17+
public void Validate_Throws_When_GithubOrg_Is_Url()
18+
{
19+
var args = new DownloadLogsCommandArgs
20+
{
21+
GithubOrg = "https://github.com/my-org",
22+
GithubRepo = GITHUB_REPO
23+
};
24+
25+
FluentActions.Invoking(() => args.Validate(_mockOctoLogger.Object))
26+
.Should()
27+
.ThrowExactly<OctoshiftCliException>()
28+
.WithMessage("The --github-org option expects an organization name, not a URL. Please provide just the organization name (e.g., 'my-org' instead of 'https://github.com/my-org').");
29+
}
30+
31+
[Fact]
32+
public void Validate_Throws_When_GithubRepo_Is_Url()
33+
{
34+
var args = new DownloadLogsCommandArgs
35+
{
36+
GithubOrg = GITHUB_ORG,
37+
GithubRepo = "http://github.com/org/repo"
38+
};
39+
40+
FluentActions.Invoking(() => args.Validate(_mockOctoLogger.Object))
41+
.Should()
42+
.ThrowExactly<OctoshiftCliException>()
43+
.WithMessage("The --github-repo option expects a repository name, not a URL. Please provide just the repository name (e.g., 'my-repo' instead of 'https://github.com/my-org/my-repo').");
44+
}
45+
46+
[Fact]
47+
public void Validate_Succeeds_With_Valid_Names()
48+
{
49+
var args = new DownloadLogsCommandArgs
50+
{
51+
GithubOrg = GITHUB_ORG,
52+
GithubRepo = GITHUB_REPO
53+
};
54+
55+
args.Validate(_mockOctoLogger.Object);
56+
57+
args.GithubOrg.Should().Be(GITHUB_ORG);
58+
args.GithubRepo.Should().Be(GITHUB_REPO);
59+
}
60+
}

0 commit comments

Comments
 (0)