swadhinbiswas feat: OpenCodeHub v1.1.0 - Enterprise Features & Storage Adapters · 2/15/2026
719a1cb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import { getStorage } from '../src/lib/storage';
/**
* This script identifies and optionally deletes corrupted repository structures in S3.
*
* Corrupted repos have the pattern: repos/swadhinbiswas/test.git/reposswadhinbiswas/...
* instead of: repos/swadhinbiswas/test.git/HEAD, refs/, objects/, etc.
*
* Usage:
* npx ts-node scripts/clean-corrupted-s3-repos.ts [--delete]
*/
async function main() {
const shouldDelete = process.argv.includes('--delete');
console.log('Scanning S3 for corrupted repository structures...\n');
const storage = await getStorage();
// List all repos
const result = await storage.list({ prefix: 'repos/' });
const corruptedRepos: string[] = [];
const seenRepos = new Set<string>();
// Check each prefix/object
if (result.prefixes) {
for (const prefix of result.prefixes) {
// Expected pattern: repos/owner/repo.git/
const parts = prefix.split('/');
if (parts.length === 4 && parts[2].endsWith('.git')) {
const repoPath = parts.slice(0, 3).join('/'); // repos/owner/repo.git
if (seenRepos.has(repoPath)) continue;
seenRepos.add(repoPath);
// Check what's inside this repo
const repoContents = await storage.list({ prefix: repoPath + '/' });
// Check if it has the corrupted nested structure
if (repoContents.prefixes) {
for (const subPrefix of repoContents.prefixes) {
const subParts = subPrefix.split('/');
const lastPart = subParts[subParts.length - 2]; // Get directory name
// If it starts with "repos" it's corrupted
if (lastPart && lastPart.startsWith('repos')) {
console.log(`❌ CORRUPTED: ${repoPath}/`);
console.log(` Found invalid directory: ${lastPart}`);
corruptedRepos.push(repoPath);
break;
}
}
}
// Also check if it has proper Git structure
const hasValidStructure = repoContents.objects?.some(obj =>
obj.key.endsWith('/HEAD') ||
obj.key.includes('/refs/') ||
obj.key.includes('/objects/')
);
if (!hasValidStructure && !corruptedRepos.includes(repoPath)) {
console.log(`⚠️ EMPTY or INCOMPLETE: ${repoPath}/`);
} else if (!corruptedRepos.includes(repoPath)) {
console.log(`✅ VALID: ${repoPath}/`);
}
}
}
}
console.log(`\n\nSummary:`);
console.log(`Found ${corruptedRepos.length} corrupted repositories`);
if (corruptedRepos.length > 0) {
console.log('\nCorrupted repositories:');
corruptedRepos.forEach(repo => console.log(` - ${repo}`));
if (shouldDelete) {
console.log('\n🗑️ Deleting corrupted repositories...');
for (const repoPath of corruptedRepos) {
console.log(`Deleting ${repoPath}...`);
// List ALL objects under this path
const toDelete = await storage.list({ prefix: repoPath + '/' });
if (toDelete.objects) {
for (const obj of toDelete.objects) {
await storage.delete(obj.key);
console.log(` Deleted: ${obj.key}`);
}
}
}
console.log('\n✅ Cleanup complete!');
} else {
console.log('\nTo delete these repositories, run:');
console.log(' npx ts-node scripts/clean-corrupted-s3-repos.ts --delete');
}
}
}
main().catch(console.error);