Skip to content

Commit d5871d0

Browse files
Add --lock-source-repo option. (#589)
<!-- For the checkboxes below you must check each one to indicate that you either did the relevant task, or considered it and decided there was nothing that needed doing --> This change addresses #385 by adding the cli option `--lock-source-repo`. For GHES source, this uses the `lock_repositories` option [as documented here](https://docs.github.com/en/rest/migrations/orgs#start-an-organization-migration). For GHEC source, this uses the new lockSource option in the GraphAPI [mentioned here](github/migration-friction#618 (comment)). - [x] Did you write/update appropriate tests - [x] Release notes updated (if appropriate) - [x] Appropriate logging output - [x] Issue linked - [x] Docs updated (or issue created) #597 <!-- For docs we should review the docs at: https://docs.github.com/en/early-access/github/migrating-with-github-enterprise-importer and the README.md in this repo If a doc update is required based on the changes in this PR, it is sufficient to create an issue and link to it here. The doc update can be made later/separately. The process to update the docs can be found here: https://github.com/github/docs-early-access#opening-prs The markdown files are here: https://github.com/github/docs-early-access/tree/main/content/github/migrating-with-github-enterprise-importer --> Co-authored-by: Dylan Smith <dylan-smith@github.com> Co-authored-by: Dylan Smith <dylanfromwinnipeg@gmail.com>
1 parent 4fb99ee commit d5871d0

File tree

8 files changed

+295
-36
lines changed

8 files changed

+295
-36
lines changed

RELEASENOTES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
- Add `migrate-repo` command to `bbs2gh`.
2+
- Added `--lock-source-repo` option to `gh gei migrate-repo` and `gh gei generate-script` commands. This will make the source repo read-only as part of the migration.

src/Octoshift/GithubApi.cs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ public virtual async Task<string> CreateGhecMigrationSource(string orgId)
223223
return (string)data["data"]["createMigrationSource"]["migrationSource"]["id"];
224224
}
225225

226-
public virtual async Task<string> StartMigration(string migrationSourceId, string sourceRepoUrl, string orgId, string repo, string sourceToken, string targetToken, string gitArchiveUrl = null, string metadataArchiveUrl = null, bool skipReleases = false)
226+
public virtual async Task<string> StartMigration(string migrationSourceId, string sourceRepoUrl, string orgId, string repo, string sourceToken, string targetToken, string gitArchiveUrl = null, string metadataArchiveUrl = null, bool skipReleases = false, bool lockSource = false)
227227
{
228228
var url = $"{_apiUrl}/graphql";
229229

@@ -238,7 +238,8 @@ mutation startRepositoryMigration(
238238
$metadataArchiveUrl: String,
239239
$accessToken: String!,
240240
$githubPat: String,
241-
$skipReleases: Boolean)";
241+
$skipReleases: Boolean,
242+
$lockSource: Boolean)";
242243
var gql = @"
243244
startRepositoryMigration(
244245
input: {
@@ -251,7 +252,8 @@ mutation startRepositoryMigration(
251252
metadataArchiveUrl: $metadataArchiveUrl,
252253
accessToken: $accessToken,
253254
githubPat: $githubPat,
254-
skipReleases: $skipReleases
255+
skipReleases: $skipReleases,
256+
lockSource: $lockSource
255257
}
256258
) {
257259
repositoryMigration {
@@ -281,7 +283,8 @@ mutation startRepositoryMigration(
281283
metadataArchiveUrl,
282284
accessToken = sourceToken,
283285
githubPat = targetToken,
284-
skipReleases
286+
skipReleases,
287+
lockSource
285288
},
286289
operationName = "startRepositoryMigration"
287290
};
@@ -498,7 +501,7 @@ public virtual async Task<int> StartGitArchiveGeneration(string org, string repo
498501
return (int)data["id"];
499502
}
500503

501-
public virtual async Task<int> StartMetadataArchiveGeneration(string org, string repo, bool skipReleases)
504+
public virtual async Task<int> StartMetadataArchiveGeneration(string org, string repo, bool skipReleases, bool lockSource)
502505
{
503506
var url = $"{_apiUrl}/orgs/{org}/migrations";
504507

@@ -507,6 +510,7 @@ public virtual async Task<int> StartMetadataArchiveGeneration(string org, string
507510
repositories = new[] { repo },
508511
exclude_git_data = true,
509512
exclude_releases = skipReleases,
513+
lock_repositories = lockSource,
510514
exclude_owner_projects = true
511515
};
512516

src/OctoshiftCLI.Tests/GithubApiTests.cs

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,8 @@ mutation startRepositoryMigration(
568568
$metadataArchiveUrl: String,
569569
$accessToken: String!,
570570
$githubPat: String,
571-
$skipReleases: Boolean)";
571+
$skipReleases: Boolean,
572+
$lockSource: Boolean)";
572573
const string gql = @"
573574
startRepositoryMigration(
574575
input: {
@@ -581,7 +582,8 @@ mutation startRepositoryMigration(
581582
metadataArchiveUrl: $metadataArchiveUrl,
582583
accessToken: $accessToken,
583584
githubPat: $githubPat,
584-
skipReleases: $skipReleases
585+
skipReleases: $skipReleases,
586+
lockSource: $lockSource
585587
}
586588
) {
587589
repositoryMigration {
@@ -610,7 +612,8 @@ mutation startRepositoryMigration(
610612
metadataArchiveUrl,
611613
accessToken = sourceToken,
612614
githubPat = targetToken,
613-
skipReleases = false
615+
skipReleases = false,
616+
lockSource = false
614617
},
615618
operationName = "startRepositoryMigration"
616619
};
@@ -670,7 +673,8 @@ mutation startRepositoryMigration(
670673
$metadataArchiveUrl: String,
671674
$accessToken: String!,
672675
$githubPat: String,
673-
$skipReleases: Boolean)";
676+
$skipReleases: Boolean,
677+
$lockSource: Boolean)";
674678
const string gql = @"
675679
startRepositoryMigration(
676680
input: {
@@ -683,7 +687,8 @@ mutation startRepositoryMigration(
683687
metadataArchiveUrl: $metadataArchiveUrl,
684688
accessToken: $accessToken,
685689
githubPat: $githubPat,
686-
skipReleases: $skipReleases
690+
skipReleases: $skipReleases,
691+
lockSource: $lockSource
687692
}
688693
) {
689694
repositoryMigration {
@@ -712,7 +717,8 @@ mutation startRepositoryMigration(
712717
metadataArchiveUrl = unusedMetadataArchiveUrl,
713718
accessToken = unusedSourceToken,
714719
githubPat = targetToken,
715-
skipReleases = false
720+
skipReleases = false,
721+
lockSource = false
716722
},
717723
operationName = "startRepositoryMigration"
718724
};
@@ -1763,6 +1769,7 @@ public async Task StartMetadataArchiveGeneration_Returns_The_Initiated_Migration
17631769
repositories = new[] { GITHUB_REPO },
17641770
exclude_git_data = true,
17651771
exclude_releases = false,
1772+
lock_repositories = false,
17661773
exclude_owner_projects = true
17671774
};
17681775
const int expectedMigrationId = 1;
@@ -1773,7 +1780,7 @@ public async Task StartMetadataArchiveGeneration_Returns_The_Initiated_Migration
17731780
.ReturnsAsync(response.ToJson());
17741781

17751782
// Act
1776-
var actualMigrationId = await _githubApi.StartMetadataArchiveGeneration(GITHUB_ORG, GITHUB_REPO, false);
1783+
var actualMigrationId = await _githubApi.StartMetadataArchiveGeneration(GITHUB_ORG, GITHUB_REPO, false, false);
17771784

17781785
// Assert
17791786
actualMigrationId.Should().Be(expectedMigrationId);
@@ -1789,14 +1796,39 @@ public async Task StartMetadataArchiveGeneration_Excludes_Releases_When_Skip_Rel
17891796
repositories = new[] { GITHUB_REPO },
17901797
exclude_git_data = true,
17911798
exclude_releases = true,
1799+
lock_repositories = false,
17921800
exclude_owner_projects = true
17931801
};
17941802
var response = new { id = 1 };
17951803

17961804
_githubClientMock.Setup(m => m.PostAsync(url, It.IsAny<object>())).ReturnsAsync(response.ToJson());
17971805

17981806
// Act
1799-
await _githubApi.StartMetadataArchiveGeneration(GITHUB_ORG, GITHUB_REPO, true);
1807+
await _githubApi.StartMetadataArchiveGeneration(GITHUB_ORG, GITHUB_REPO, true, false);
1808+
1809+
// Assert
1810+
_githubClientMock.Verify(m => m.PostAsync(url, It.Is<object>(x => x.ToJson() == payload.ToJson())));
1811+
}
1812+
1813+
[Fact]
1814+
public async Task StartMetadataArchiveGeneration_Locks_Repos_When_Lock_Source_Repo_Is_True()
1815+
{
1816+
// Arrange
1817+
const string url = $"https://api.github.com/orgs/{GITHUB_ORG}/migrations";
1818+
var payload = new
1819+
{
1820+
repositories = new[] { GITHUB_REPO },
1821+
exclude_git_data = true,
1822+
exclude_releases = true,
1823+
lock_repositories = true,
1824+
exclude_owner_projects = true
1825+
};
1826+
var response = new { id = 1 };
1827+
1828+
_githubClientMock.Setup(m => m.PostAsync(url, It.IsAny<object>())).ReturnsAsync(response.ToJson());
1829+
1830+
// Act
1831+
await _githubApi.StartMetadataArchiveGeneration(GITHUB_ORG, GITHUB_REPO, true, true);
18001832

18011833
// Assert
18021834
_githubClientMock.Verify(m => m.PostAsync(url, It.Is<object>(x => x.ToJson() == payload.ToJson())));

src/OctoshiftCLI.Tests/ado2gh/Commands/MigrateRepoCommandTests.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ public async Task Happy_Path()
7070
GITHUB_TOKEN,
7171
null,
7272
null,
73+
false,
7374
false).Result)
7475
.Returns(MIGRATION_ID);
7576
_mockGithubApi.Setup(x => x.GetMigration(MIGRATION_ID).Result).Returns((State: RepositoryMigrationStatus.Succeeded, GITHUB_REPO, null));
@@ -103,7 +104,7 @@ public async Task Happy_Path()
103104
// Assert
104105
_mockGithubApi.Verify(m => m.GetOrganizationId(GITHUB_ORG));
105106
_mockGithubApi.Verify(m => m.CreateAdoMigrationSource(GITHUB_ORG_ID, null));
106-
_mockGithubApi.Verify(m => m.StartMigration(MIGRATION_SOURCE_ID, ADO_REPO_URL, GITHUB_ORG_ID, GITHUB_REPO, ADO_TOKEN, GITHUB_TOKEN, null, null, false));
107+
_mockGithubApi.Verify(m => m.StartMigration(MIGRATION_SOURCE_ID, ADO_REPO_URL, GITHUB_ORG_ID, GITHUB_REPO, ADO_TOKEN, GITHUB_TOKEN, null, null, false, false));
107108

108109
_mockOctoLogger.Verify(m => m.LogInformation(It.IsAny<string>()), Times.Exactly(7));
109110
actualLogOutput.Should().Equal(expectedLogOutput);
@@ -128,6 +129,7 @@ public async Task Skip_Migration_If_Target_Repo_Exists()
128129
GITHUB_TOKEN,
129130
null,
130131
null,
132+
false,
131133
false).Result)
132134
.Throws(new OctoshiftCliException($"A repository called {GITHUB_ORG}/{GITHUB_REPO} already exists"));
133135

@@ -169,6 +171,7 @@ public async Task Happy_Path_With_Wait()
169171
GITHUB_TOKEN,
170172
null,
171173
null,
174+
false,
172175
false).Result)
173176
.Returns(MIGRATION_ID);
174177
_mockGithubApi.Setup(x => x.GetMigration(MIGRATION_ID).Result).Returns((State: RepositoryMigrationStatus.Succeeded, GITHUB_REPO, null));

src/OctoshiftCLI.Tests/gei/Commands/GenerateScriptCommandTests.cs

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public void Should_Have_Options()
5454
{
5555
_command.Should().NotBeNull();
5656
_command.Name.Should().Be("generate-script");
57-
_command.Options.Count.Should().Be(16);
57+
_command.Options.Count.Should().Be(17);
5858

5959
TestHelpers.VerifyCommandOption(_command.Options, "github-source-org", false);
6060
TestHelpers.VerifyCommandOption(_command.Options, "ado-server-url", false, true);
@@ -65,6 +65,7 @@ public void Should_Have_Options()
6565
TestHelpers.VerifyCommandOption(_command.Options, "azure-storage-connection-string", false);
6666
TestHelpers.VerifyCommandOption(_command.Options, "no-ssl-verify", false);
6767
TestHelpers.VerifyCommandOption(_command.Options, "skip-releases", false);
68+
TestHelpers.VerifyCommandOption(_command.Options, "lock-source-repo", false);
6869
TestHelpers.VerifyCommandOption(_command.Options, "download-migration-logs", false);
6970
TestHelpers.VerifyCommandOption(_command.Options, "output", false);
7071
TestHelpers.VerifyCommandOption(_command.Options, "ssh", false, true);
@@ -1509,6 +1510,70 @@ public async Task Parallel_It_Adds_Skip_Releases_To_Migrate_Repo_Command_When_Pr
15091510
_script.Should().Be(expected.ToString());
15101511
}
15111512

1513+
[Fact]
1514+
public async Task It_Adds_Lock_Source_Repo_To_Migrate_Repo_Command_When_Provided_In_Sequential_Script()
1515+
{
1516+
// Arrange
1517+
_mockGithubApi
1518+
.Setup(m => m.GetRepos(SOURCE_ORG))
1519+
.ReturnsAsync(new[] { REPO });
1520+
1521+
_mockSourceGithubApiFactory
1522+
.Setup(m => m.Create(It.IsAny<string>(), It.IsAny<string>()))
1523+
.Returns(_mockGithubApi.Object);
1524+
1525+
var expected = $"Exec {{ gh gei migrate-repo --github-source-org \"{SOURCE_ORG}\" --source-repo \"{REPO}\" --github-target-org \"{TARGET_ORG}\" --target-repo \"{REPO}\" --wait --lock-source-repo }}";
1526+
1527+
// Act
1528+
var args = new GenerateScriptCommandArgs
1529+
{
1530+
GithubSourceOrg = SOURCE_ORG,
1531+
GithubTargetOrg = TARGET_ORG,
1532+
Output = new FileInfo("unit-test-output"),
1533+
Sequential = true,
1534+
LockSourceRepo = true
1535+
};
1536+
await _command.Invoke(args);
1537+
1538+
_script = TrimNonExecutableLines(_script);
1539+
1540+
// Assert
1541+
_script.Should().Be(expected);
1542+
}
1543+
1544+
[Fact]
1545+
public async Task Parallel_It_Adds_Lock_Source_Repo_To_Migrate_Repo_Command_When_Provided_In_Parallel_Script()
1546+
{
1547+
// Arrange
1548+
_mockGithubApi
1549+
.Setup(m => m.GetRepos(SOURCE_ORG))
1550+
.ReturnsAsync(new[] { REPO });
1551+
1552+
_mockSourceGithubApiFactory
1553+
.Setup(m => m.Create(It.IsAny<string>(), It.IsAny<string>()))
1554+
.Returns(_mockGithubApi.Object);
1555+
1556+
var expected = new StringBuilder();
1557+
expected.AppendLine($"$MigrationID = ExecAndGetMigrationID {{ gh gei migrate-repo --github-source-org \"{SOURCE_ORG}\" --source-repo \"{REPO}\" --github-target-org \"{TARGET_ORG}\" --target-repo \"{REPO}\" --lock-source-repo }}");
1558+
expected.AppendLine($"$RepoMigrations[\"{REPO}\"] = $MigrationID");
1559+
expected.Append($"gh gei wait-for-migration --migration-id $RepoMigrations[\"{REPO}\"]");
1560+
1561+
// Act
1562+
var args = new GenerateScriptCommandArgs
1563+
{
1564+
GithubSourceOrg = SOURCE_ORG,
1565+
GithubTargetOrg = TARGET_ORG,
1566+
Output = new FileInfo("unit-test-output"),
1567+
LockSourceRepo = true
1568+
};
1569+
await _command.Invoke(args);
1570+
1571+
_script = TrimNonExecutableLines(_script, 22, 7);
1572+
1573+
// Assert
1574+
_script.Should().Be(expected.ToString());
1575+
}
1576+
15121577
[Fact]
15131578
public async Task Sequential_Github_Contains_Cli_Version()
15141579
{

0 commit comments

Comments
 (0)