Skip to content

Commit 3d022df

Browse files
author
josedacosta
committed
refactor: reorganize scripts and add GitHub deployment cleanup tool
- Move check-version-consistency.ts from scripts/version/ to scripts/git/ - Add new cleanup-deployments.ts script for cleaning non-successful GitHub deployments - Script supports dry-run, keep-last, and verbose options - Remove empty scripts/version directory
1 parent e08ffa9 commit 3d022df

File tree

2 files changed

+224
-0
lines changed

2 files changed

+224
-0
lines changed
File renamed without changes.

scripts/git/cleanup-deployments.ts

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
#!/usr/bin/env tsx
2+
3+
/**
4+
* Clean up GitHub deployments that are not successful
5+
* This script removes all deployments with status other than 'success'
6+
*
7+
* Usage: tsx scripts/git/cleanup-deployments.ts [options]
8+
* Options:
9+
* --dry-run Show what would be deleted without actually deleting
10+
* --keep-last Keep the most recent deployment regardless of status
11+
* --verbose Show detailed output
12+
*/
13+
14+
import { execSync } from 'child_process';
15+
16+
interface Deployment {
17+
id: number;
18+
environment: string;
19+
created_at: string;
20+
updated_at: string;
21+
}
22+
23+
interface DeploymentStatus {
24+
state: string;
25+
created_at: string;
26+
}
27+
28+
// Parse command line arguments
29+
const args = process.argv.slice(2);
30+
const options = {
31+
dryRun: args.includes('--dry-run'),
32+
keepLast: args.includes('--keep-last'),
33+
verbose: args.includes('--verbose')
34+
};
35+
36+
// Colors for terminal output
37+
const colors = {
38+
reset: '\x1b[0m',
39+
bright: '\x1b[1m',
40+
red: '\x1b[31m',
41+
green: '\x1b[32m',
42+
yellow: '\x1b[33m',
43+
blue: '\x1b[34m',
44+
cyan: '\x1b[36m',
45+
gray: '\x1b[90m'
46+
};
47+
48+
function log(message: string, color: string = colors.reset) {
49+
console.log(`${color}${message}${colors.reset}`);
50+
}
51+
52+
function verbose(message: string) {
53+
if (options.verbose) {
54+
log(message, colors.gray);
55+
}
56+
}
57+
58+
function executeCommand(command: string): string {
59+
try {
60+
return execSync(command, { encoding: 'utf-8' }).trim();
61+
} catch (error: any) {
62+
if (error.status !== 0) {
63+
throw new Error(`Command failed: ${command}\n${error.message}`);
64+
}
65+
return '';
66+
}
67+
}
68+
69+
async function getDeployments(): Promise<Deployment[]> {
70+
try {
71+
verbose('Fetching deployments...');
72+
const result = executeCommand(
73+
'gh api repos/josedacosta/mcp-jetbrains-code-inspections/deployments --paginate'
74+
);
75+
return JSON.parse(result);
76+
} catch (error) {
77+
log('Error fetching deployments', colors.red);
78+
throw error;
79+
}
80+
}
81+
82+
async function getDeploymentStatus(deploymentId: number): Promise<string> {
83+
try {
84+
const result = executeCommand(
85+
`gh api repos/josedacosta/mcp-jetbrains-code-inspections/deployments/${deploymentId}/statuses`
86+
);
87+
const statuses: DeploymentStatus[] = JSON.parse(result);
88+
89+
// Return the most recent status or 'pending' if no status exists
90+
return statuses.length > 0 ? statuses[0].state : 'pending';
91+
} catch (error) {
92+
verbose(`Could not fetch status for deployment ${deploymentId}, assuming pending`);
93+
return 'pending';
94+
}
95+
}
96+
97+
async function deleteDeployment(deploymentId: number): Promise<boolean> {
98+
if (options.dryRun) {
99+
log(` [DRY RUN] Would delete deployment ${deploymentId}`, colors.cyan);
100+
return true;
101+
}
102+
103+
try {
104+
executeCommand(
105+
`gh api -X DELETE repos/josedacosta/mcp-jetbrains-code-inspections/deployments/${deploymentId}`
106+
);
107+
return true;
108+
} catch (error) {
109+
log(` Failed to delete deployment ${deploymentId}`, colors.red);
110+
verbose(` Error: ${error}`);
111+
return false;
112+
}
113+
}
114+
115+
async function main() {
116+
log('\n🧹 GitHub Deployments Cleanup Script', colors.bright);
117+
log('=' .repeat(40), colors.gray);
118+
119+
if (options.dryRun) {
120+
log('🔍 Running in DRY RUN mode (no changes will be made)', colors.yellow);
121+
}
122+
123+
try {
124+
// Check if gh CLI is installed and authenticated
125+
try {
126+
executeCommand('gh auth status');
127+
} catch (error) {
128+
log('\n❌ GitHub CLI is not authenticated', colors.red);
129+
log(' Run: gh auth login', colors.yellow);
130+
process.exit(1);
131+
}
132+
133+
// Get all deployments
134+
const deployments = await getDeployments();
135+
136+
if (deployments.length === 0) {
137+
log('\n✅ No deployments found', colors.green);
138+
return;
139+
}
140+
141+
log(`\n📊 Found ${deployments.length} deployment(s)`, colors.blue);
142+
143+
// Statistics
144+
let successCount = 0;
145+
let deletedCount = 0;
146+
let failedCount = 0;
147+
let skippedCount = 0;
148+
149+
// Process each deployment
150+
for (let i = 0; i < deployments.length; i++) {
151+
const deployment = deployments[i];
152+
const isLast = i === 0; // Deployments are returned in reverse chronological order
153+
154+
verbose(`\nChecking deployment ${deployment.id}...`);
155+
156+
// Skip the last deployment if keepLast option is set
157+
if (isLast && options.keepLast) {
158+
log(`\n📌 Keeping last deployment ${deployment.id} (--keep-last option)`, colors.yellow);
159+
skippedCount++;
160+
continue;
161+
}
162+
163+
// Get deployment status
164+
const status = await getDeploymentStatus(deployment.id);
165+
166+
// Format deployment info
167+
const deploymentInfo = `Deployment ${deployment.id} [${deployment.environment}] - Status: ${status}`;
168+
169+
if (status === 'success') {
170+
log(`\n✅ ${deploymentInfo}`, colors.green);
171+
successCount++;
172+
} else {
173+
log(`\n❌ ${deploymentInfo}`, colors.yellow);
174+
175+
// Delete non-successful deployment
176+
const deleted = await deleteDeployment(deployment.id);
177+
178+
if (deleted) {
179+
if (!options.dryRun) {
180+
log(` ✓ Deleted successfully`, colors.green);
181+
}
182+
deletedCount++;
183+
} else {
184+
failedCount++;
185+
}
186+
}
187+
}
188+
189+
// Print summary
190+
log('\n' + '=' .repeat(40), colors.gray);
191+
log('📈 Summary:', colors.bright);
192+
log(` • Total deployments: ${deployments.length}`, colors.blue);
193+
log(` • Successful: ${successCount}`, colors.green);
194+
log(` • Deleted: ${deletedCount}`, colors.yellow);
195+
196+
if (failedCount > 0) {
197+
log(` • Failed to delete: ${failedCount}`, colors.red);
198+
}
199+
200+
if (skippedCount > 0) {
201+
log(` • Skipped: ${skippedCount}`, colors.cyan);
202+
}
203+
204+
if (options.dryRun) {
205+
log('\n💡 This was a dry run. Run without --dry-run to actually delete deployments', colors.yellow);
206+
} else if (deletedCount > 0) {
207+
log('\n✨ Cleanup completed successfully!', colors.green);
208+
} else {
209+
log('\n✨ No deployments needed cleanup', colors.green);
210+
}
211+
212+
} catch (error: any) {
213+
log('\n❌ An error occurred:', colors.red);
214+
log(error.message, colors.red);
215+
process.exit(1);
216+
}
217+
}
218+
219+
// Run the script
220+
main().catch((error) => {
221+
log('Unexpected error:', colors.red);
222+
console.error(error);
223+
process.exit(1);
224+
});

0 commit comments

Comments
 (0)