Skip to content

Commit a7477b6

Browse files
itshawiAutomation Bot
andauthored
Align ELM migration enums and skip-validation policies with server rename (#1495)
Co-authored-by: Automation Bot <[email protected]>
1 parent 10baa80 commit a7477b6

5 files changed

Lines changed: 87 additions & 20 deletions

File tree

azure-devops/azext_devops/dev/migration/arguments.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def load_migration_arguments(self, _):
3939
help='Agent pool name to use for migration work.')
4040
context.argument('skip_validation', options_list='--skip-validation',
4141
help='Validation policies to skip. Accepts either a comma-separated list of '
42-
'policy names (for example, AgentPoolExists,MaxRepoSize) or a non-negative '
42+
'policy names (for example, AgentPoolExists,MaxFileSize) or a non-negative '
4343
'integer bitmask.')
4444
context.argument('service_endpoint_id', options_list='--service-endpoint-id',
4545
help='Service endpoint ID (GUID) for the GitHub Enterprise Server connection. '

azure-devops/azext_devops/dev/migration/migration.py

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,10 @@
4646
'maxpullrequestsize': 16,
4747
'maxpushpacksize': 32,
4848
'maxreferencenamelength': 64,
49-
'maxreposize': 128,
5049
'targetrepositorydoesnotexist': 256,
50+
'sourcerepositorycontainslfsobjects': 512,
51+
'sourcerepositorynotreadonly': 1024,
52+
'boardsgithubconnectionprovisioning': 2048,
5153
'all': 2147483647,
5254
}
5355
_SUCCESS_TERMINAL_STATES = {
@@ -58,14 +60,22 @@
5860
'completed',
5961
'succeeded',
6062
'failed',
61-
'suspended'
63+
'suspended',
64+
'paused'
6265
}
6366
_ACTIVE_STAGES = {
6467
'queued',
6568
'validation',
6669
'synchronization',
6770
'cutover'
6871
}
72+
73+
_MIGRATION_STATUS_VALUES = {
74+
'active': 0,
75+
'completed': 1,
76+
'failed': 2,
77+
'paused': 3,
78+
}
6979
_URL_PATTERN = re.compile(r'^https?://[^\s]+$', re.IGNORECASE)
7080

7181

@@ -114,7 +124,7 @@ def _parse_skip_validation(skip_validation):
114124
policies = [policy.strip() for policy in normalized.split(',')]
115125
if any(not policy for policy in policies):
116126
raise CLIError('--skip-validation contains an empty policy name. Provide a comma-separated '
117-
'list such as "AgentPoolExists,MaxRepoSize".')
127+
'list such as "AgentPoolExists,MaxFileSize".')
118128

119129
mask = 0
120130
invalid_policies = []
@@ -141,8 +151,10 @@ def _parse_skip_validation(skip_validation):
141151
'MaxPullRequestSize',
142152
'MaxPushPackSize',
143153
'MaxReferenceNameLength',
144-
'MaxRepoSize',
145154
'TargetRepositoryDoesNotExist',
155+
'SourceRepositoryContainsLfsObjects',
156+
'SourceRepositoryNotReadOnly',
157+
'BoardsGitHubConnectionProvisioning',
146158
'All'
147159
])))
148160

@@ -397,7 +409,8 @@ def _validate_target_repository(target_repository):
397409

398410

399411
def pause_migration(repository_id=None, organization=None, detect=None):
400-
result = _update_migration(repository_id, organization, detect, status_requested='suspended')
412+
result = _update_migration(repository_id, organization, detect,
413+
status_requested=_MIGRATION_STATUS_VALUES['paused'])
401414
if not result:
402415
return {'message': 'Migration paused successfully.'}
403416
return result
@@ -449,7 +462,8 @@ def resume_migration(repository_id=None, validate_only=False, migration=False, o
449462
validate_only_value = False
450463

451464
return _update_migration(repository_id, organization, None,
452-
validate_only=validate_only_value, status_requested='active')
465+
validate_only=validate_only_value,
466+
status_requested=_MIGRATION_STATUS_VALUES['active'])
453467

454468

455469
def schedule_cutover(repository_id=None, cutover_date=None, organization=None, detect=None):
@@ -619,7 +633,8 @@ def _is_validate_only_succeeded(migration):
619633
def _promote_to_full_migration(migration_data, repository_id, organization):
620634
del migration_data
621635
return _update_migration(repository_id, organization, detect=None,
622-
validate_only=False, status_requested='active')
636+
validate_only=False,
637+
status_requested=_MIGRATION_STATUS_VALUES['active'])
623638

624639

625640
def _resolve_org_for_auth(organization, detect):

azure-devops/azext_devops/tests/latest/migration/test_migration.py

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -709,7 +709,7 @@ def test_create_migration_skip_validation_rejects_empty_policy_name(self):
709709
repository_id='00000000-0000-0000-0000-000000000000',
710710
target_repository='https://example.ghe.com/OrgName/RepoName',
711711
target_owner_user_id='TestOwner',
712-
skip_validation='AgentPoolExists,,MaxRepoSize',
712+
skip_validation='AgentPoolExists,,MaxFileSize',
713713
organization=self._TEST_ORG,
714714
detect=False
715715
)
@@ -1065,6 +1065,22 @@ def test_pause_returns_migration_data_when_service_responds(self):
10651065

10661066
self.assertEqual(result, migration_response)
10671067

1068+
def test_pause_returns_migration_data_when_service_responds_paused(self):
1069+
migration_response = {'repositoryId': '00000000-0000-0000-0000-000000000000', 'status': 'paused'}
1070+
with patch('azext_devops.dev.migration.migration.resolve_instance') as mock_resolve, \
1071+
patch('azext_devops.dev.migration.migration._get_service_client') as mock_client, \
1072+
patch('azext_devops.dev.migration.migration._send_request') as mock_send:
1073+
mock_send.return_value = migration_response
1074+
mock_resolve.return_value = self._TEST_ORG
1075+
1076+
result = pause_migration(
1077+
repository_id='00000000-0000-0000-0000-000000000000',
1078+
organization=self._TEST_ORG,
1079+
detect=False
1080+
)
1081+
1082+
self.assertEqual(result, migration_response)
1083+
10681084
def test_list_migrations_warns_when_empty(self):
10691085
with patch('azext_devops.dev.migration.migration.resolve_instance') as mock_resolve, \
10701086
patch('azext_devops.dev.migration.migration._get_service_client') as mock_client, \
@@ -1160,7 +1176,7 @@ def test_resume_sets_validate_only(self):
11601176

11611177
payload = mock_send.call_args[0][3]
11621178
self.assertTrue(payload['validateOnly'])
1163-
self.assertEqual(payload['statusRequested'], 'active')
1179+
self.assertEqual(payload['statusRequested'], migration_module._MIGRATION_STATUS_VALUES['active'])
11641180

11651181
def test_resume_sets_migration(self):
11661182
with patch('azext_devops.dev.migration.migration.get_migration') as mock_get, \
@@ -1177,7 +1193,41 @@ def test_resume_sets_migration(self):
11771193

11781194
payload = mock_send.call_args[0][3]
11791195
self.assertFalse(payload['validateOnly'])
1180-
self.assertEqual(payload['statusRequested'], 'active')
1196+
self.assertEqual(payload['statusRequested'], migration_module._MIGRATION_STATUS_VALUES['active'])
1197+
1198+
def test_resume_sets_validate_only_when_status_paused(self):
1199+
with patch('azext_devops.dev.migration.migration.get_migration') as mock_get, \
1200+
patch('azext_devops.dev.migration.migration.resolve_instance') as mock_resolve, \
1201+
patch('azext_devops.dev.migration.migration._get_service_client') as mock_client, \
1202+
patch('azext_devops.dev.migration.migration._send_request') as mock_send:
1203+
mock_send.return_value = {}
1204+
mock_get.return_value = {'status': 'paused'}
1205+
mock_resolve.return_value = self._TEST_ORG
1206+
1207+
resume_migration(repository_id='00000000-0000-0000-0000-000000000000',
1208+
validate_only=True,
1209+
organization=self._TEST_ORG, detect=False)
1210+
1211+
payload = mock_send.call_args[0][3]
1212+
self.assertTrue(payload['validateOnly'])
1213+
self.assertEqual(payload['statusRequested'], migration_module._MIGRATION_STATUS_VALUES['active'])
1214+
1215+
def test_resume_sets_migration_when_status_paused(self):
1216+
with patch('azext_devops.dev.migration.migration.get_migration') as mock_get, \
1217+
patch('azext_devops.dev.migration.migration.resolve_instance') as mock_resolve, \
1218+
patch('azext_devops.dev.migration.migration._get_service_client') as mock_client, \
1219+
patch('azext_devops.dev.migration.migration._send_request') as mock_send:
1220+
mock_send.return_value = {}
1221+
mock_get.return_value = {'status': 'paused'}
1222+
mock_resolve.return_value = self._TEST_ORG
1223+
1224+
resume_migration(repository_id='00000000-0000-0000-0000-000000000000',
1225+
migration=True,
1226+
organization=self._TEST_ORG, detect=False)
1227+
1228+
payload = mock_send.call_args[0][3]
1229+
self.assertFalse(payload['validateOnly'])
1230+
self.assertEqual(payload['statusRequested'], migration_module._MIGRATION_STATUS_VALUES['active'])
11811231

11821232
def test_resume_without_flags_preserves_mode(self):
11831233
with patch('azext_devops.dev.migration.migration.get_migration') as mock_get, \
@@ -1193,7 +1243,7 @@ def test_resume_without_flags_preserves_mode(self):
11931243

11941244
payload = mock_send.call_args[0][3]
11951245
self.assertNotIn('validateOnly', payload)
1196-
self.assertEqual(payload['statusRequested'], 'active')
1246+
self.assertEqual(payload['statusRequested'], migration_module._MIGRATION_STATUS_VALUES['active'])
11971247

11981248
def test_resume_migration_promotes_validate_only_succeeded(self):
11991249
with patch('azext_devops.dev.migration.migration.get_migration') as mock_get, \
@@ -1215,7 +1265,7 @@ def test_resume_migration_promotes_validate_only_succeeded(self):
12151265
self.assertEqual(args[1], 'PUT')
12161266
payload = args[3]
12171267
self.assertFalse(payload['validateOnly'])
1218-
self.assertEqual(payload['statusRequested'], 'active')
1268+
self.assertEqual(payload['statusRequested'], migration_module._MIGRATION_STATUS_VALUES['active'])
12191269

12201270
def test_resume_migration_promotes_validate_only_completed(self):
12211271
with patch('azext_devops.dev.migration.migration.get_migration') as mock_get, \
@@ -1237,7 +1287,7 @@ def test_resume_migration_promotes_validate_only_completed(self):
12371287
self.assertEqual(args[1], 'PUT')
12381288
payload = args[3]
12391289
self.assertFalse(payload['validateOnly'])
1240-
self.assertEqual(payload['statusRequested'], 'active')
1290+
self.assertEqual(payload['statusRequested'], migration_module._MIGRATION_STATUS_VALUES['active'])
12411291

12421292
def test_resume_migration_promote_uses_only_state_transition_fields(self):
12431293
with patch('azext_devops.dev.migration.migration.get_migration') as mock_get, \
@@ -1262,7 +1312,7 @@ def test_resume_migration_promote_uses_only_state_transition_fields(self):
12621312

12631313
payload = mock_send.call_args[0][3]
12641314
self.assertEqual(payload['validateOnly'], False)
1265-
self.assertEqual(payload['statusRequested'], 'active')
1315+
self.assertEqual(payload['statusRequested'], migration_module._MIGRATION_STATUS_VALUES['active'])
12661316
self.assertEqual(set(payload.keys()), {'validateOnly', 'statusRequested'})
12671317
self.assertNotIn('agentPoolName', payload)
12681318
self.assertNotIn('scheduledCutoverDate', payload)

doc/elm_migrations_tsg.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ az devops migrations resume --detect false --repository-id <GUID> --validate-onl
386386
Recommended form using policy names:
387387

388388
```powershell
389-
az devops migrations create --detect false --repository-id <GUID> --target-repository <TARGET_URL> --target-owner-user-id <OWNER> --skip-validation AgentPoolExists,MaxRepoSize
389+
az devops migrations create --detect false --repository-id <GUID> --target-repository <TARGET_URL> --target-owner-user-id <OWNER> --skip-validation AgentPoolExists,MaxFileSize
390390
```
391391

392392
Advanced form using integer bitmask:
@@ -398,7 +398,7 @@ az devops migrations create --detect false --repository-id <GUID> --target-repos
398398
Token/PAT-authenticated examples:
399399

400400
```powershell
401-
az devops migrations create --detect false --repository-id <GUID> --target-repository <TARGET_URL> --github-token <TOKEN_OR_PAT> --skip-validation AgentPoolExists,MaxRepoSize
401+
az devops migrations create --detect false --repository-id <GUID> --target-repository <TARGET_URL> --github-token <TOKEN_OR_PAT> --skip-validation AgentPoolExists,MaxFileSize
402402
az devops migrations create --detect false --repository-id <GUID> --target-repository <TARGET_URL> --github-token <TOKEN_OR_PAT> --skip-validation 132
403403
```
404404

@@ -412,8 +412,10 @@ Supported policy names:
412412
- `MaxPullRequestSize`
413413
- `MaxPushPackSize`
414414
- `MaxReferenceNameLength`
415-
- `MaxRepoSize`
416415
- `TargetRepositoryDoesNotExist`
416+
- `SourceRepositoryContainsLfsObjects`
417+
- `SourceRepositoryNotReadOnly`
418+
- `BoardsGitHubConnectionProvisioning`
417419
- `All`
418420

419421
### Promote validate-only does not start

doc/migrations.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ Recommended form using policy names:
150150
az devops migrations create --org https://dev.azure.com/myorg \
151151
--repository-id 00000000-0000-0000-0000-000000000000 \
152152
--target-repository https://example.ghe.com/OrgName/RepoName \
153-
--skip-validation AgentPoolExists,MaxRepoSize
153+
--skip-validation AgentPoolExists,MaxFileSize
154154
```
155155

156156
Advanced form using integer bitmask:
@@ -234,7 +234,7 @@ az devops migrations pause --org https://dev.azure.com/myorg \
234234
Pass `--github-token`, set `ELM_GITHUB_TOKEN`, or complete the interactive GitHub device-flow prompt shown by CLI.
235235

236236
- Error: `--skip-validation` contains unsupported policy names.
237-
Use supported names such as `AgentPoolExists`, `MaxRepoSize`, or pass a non-negative integer bitmask.
237+
Use supported names such as `AgentPoolExists`, `MaxFileSize`, or pass a non-negative integer bitmask.
238238

239239
- Error: requests are sent to the wrong org.
240240
Use `--org <url> --detect false`, and verify defaults via `az devops configure -l`.

0 commit comments

Comments
 (0)