Skip to content

Commit 8842307

Browse files
authored
feat: add migration-test repository for testing transfer automation (#17)
* feat: add migration-test repository for testing transfer automation Add test repository to validate the migration workflow before migrating real repositories like worlddriven core. Origin: TooAngel/worlddriven-migration-test * feat: implement repository transfer API Add transferRepository() function that calls GitHub's transfer API to move repositories from external owners to the worlddriven org. After transfer completes, standard settings and topics are applied. Transfer errors are handled gracefully - if post-transfer config fails, the transfer itself is still considered successful. Closes #9 * fix: check worlddrivenbot's permission instead of worlddriven org The permission check was looking for 'worlddriven' org as a collaborator, but we invite 'worlddrivenbot' user. Changed to use the repo endpoint which returns permissions for the authenticated user (worlddrivenbot).
1 parent 74992cd commit 8842307

File tree

3 files changed

+66
-13
lines changed

3 files changed

+66
-13
lines changed

REPOSITORIES.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,8 @@ Track implementation progress in GitHub issue #9.
116116
## webapp
117117
- Description: Web application interface for worlddriven
118118
- Topics: webapp, web, frontend, worlddriven
119+
120+
## migration-test
121+
- Description: Test repository for migration automation
122+
- Topics: test, migration, automation
123+
- Origin: TooAngel/worlddriven-migration-test

scripts/check-transfer-permissions.js

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
#!/usr/bin/env node
22

33
/**
4-
* Check if worlddriven organization has admin permission on a repository
4+
* Check if worlddrivenbot has admin permission on a repository
55
* Required for repository transfer automation
66
*/
77

88
const GITHUB_API_BASE = 'https://api.github.com';
99
const ORG_NAME = 'worlddriven';
1010

1111
/**
12-
* Check if worlddriven org has admin permission on the origin repository
12+
* Check if the authenticated user (worlddrivenbot) has admin permission on the origin repository
1313
*
1414
* @param {string} token - GitHub token (WORLDDRIVEN_GITHUB_TOKEN)
1515
* @param {string} originRepo - Repository in format "owner/repo-name"
@@ -31,8 +31,9 @@ export async function checkTransferPermission(token, originRepo) {
3131
}
3232

3333
try {
34-
// Check if worlddriven org has admin permission on the origin repository
35-
const url = `${GITHUB_API_BASE}/repos/${owner}/${repo}/collaborators/${ORG_NAME}/permission`;
34+
// Check the authenticated user's permission on the origin repository
35+
// The repo endpoint returns permissions for the authenticated user
36+
const url = `${GITHUB_API_BASE}/repos/${owner}/${repo}`;
3637

3738
const response = await fetch(url, {
3839
headers: {
@@ -44,11 +45,11 @@ export async function checkTransferPermission(token, originRepo) {
4445

4546
// Handle different response scenarios
4647
if (response.status === 404) {
47-
// Repository doesn't exist or worlddriven doesn't have any permission
48+
// Repository doesn't exist or user doesn't have any access
4849
return {
4950
hasPermission: false,
5051
permissionLevel: 'none',
51-
details: `Repository ${originRepo} not found or worlddriven has no access`,
52+
details: `Repository ${originRepo} not found or no access`,
5253
};
5354
}
5455

@@ -64,16 +65,19 @@ export async function checkTransferPermission(token, originRepo) {
6465

6566
const data = await response.json();
6667

67-
// GitHub returns permission level: "admin", "write", "read", or "none"
68-
const permissionLevel = data.permission || 'none';
69-
const hasPermission = permissionLevel === 'admin';
68+
// The repo response includes permissions object for the authenticated user
69+
const permissions = data.permissions || {};
70+
const hasPermission = permissions.admin === true;
71+
const permissionLevel = hasPermission ? 'admin' :
72+
permissions.push ? 'write' :
73+
permissions.pull ? 'read' : 'none';
7074

7175
return {
7276
hasPermission,
7377
permissionLevel,
7478
details: hasPermission
75-
? `✅ ${ORG_NAME} has admin access to ${originRepo}`
76-
: `❌ ${ORG_NAME} has "${permissionLevel}" access to ${originRepo} (admin required)`,
79+
? `✅ worlddrivenbot has admin access to ${originRepo}`
80+
: `❌ worlddrivenbot has "${permissionLevel}" access to ${originRepo} (admin required)`,
7781
};
7882

7983
} catch (error) {

scripts/sync-repositories.js

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,39 @@ async function updateRepositoryTopics(token, repoName, topics) {
232232
return await response.json();
233233
}
234234

235+
/**
236+
* Transfer a repository to the worlddriven organization
237+
* @param {string} token - GitHub token with admin access
238+
* @param {string} originRepo - Source repository in format "owner/repo"
239+
* @param {string} newName - Name for the repository in worlddriven org
240+
* @returns {Promise<Object>} Transfer result
241+
*/
242+
async function transferRepository(token, originRepo, newName) {
243+
const [owner, repo] = originRepo.split('/');
244+
const url = `${GITHUB_API_BASE}/repos/${owner}/${repo}/transfer`;
245+
246+
const response = await fetch(url, {
247+
method: 'POST',
248+
headers: {
249+
'Authorization': `Bearer ${token}`,
250+
'Accept': 'application/vnd.github+json',
251+
'X-GitHub-Api-Version': '2022-11-28',
252+
'Content-Type': 'application/json',
253+
},
254+
body: JSON.stringify({
255+
new_owner: ORG_NAME,
256+
new_name: newName,
257+
}),
258+
});
259+
260+
if (!response.ok) {
261+
const error = await response.text();
262+
throw new Error(`Transfer failed (${response.status}): ${error}`);
263+
}
264+
265+
return await response.json();
266+
}
267+
235268
/**
236269
* Delete a repository from the GitHub organization
237270
*/
@@ -540,8 +573,19 @@ async function executeSyncPlan(token, plan, dryRun) {
540573
break;
541574

542575
case 'transfer':
543-
// Transfer API not yet implemented
544-
throw new Error('Repository transfer is not yet implemented. See issue #9 for progress.');
576+
result = await transferRepository(token, action.origin, action.repo);
577+
// Apply standard settings after transfer completes
578+
// Note: Transfer is async on GitHub's side, settings may need retry
579+
try {
580+
await ensureStandardConfiguration(token, action.repo);
581+
if (action.data.topics && action.data.topics.length > 0) {
582+
await updateRepositoryTopics(token, action.repo, action.data.topics);
583+
}
584+
} catch (configError) {
585+
// Transfer succeeded but config failed - log but don't fail
586+
console.error(`Warning: Transfer succeeded but post-transfer config failed: ${configError.message}`);
587+
}
588+
break;
545589

546590
default:
547591
throw new Error(`Unknown action type: ${action.type}`);

0 commit comments

Comments
 (0)