Skip to content

Commit df4d9b3

Browse files
committed
chore(stacks): add clean stack function
1 parent 7ec025a commit df4d9b3

4 files changed

Lines changed: 183 additions & 48 deletions

File tree

scripts/checker.py

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -158,18 +158,6 @@
158158
"owner": "kubernetes",
159159
"match": "^kubernetes-[0-9]{1,}\.[0-9]{1,}\.[0-9]{1,}$",
160160
},
161-
"mc": {
162-
"name": "mc",
163-
"type": "github",
164-
"owner": "minio",
165-
"match": "^RELEASE\.[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}-[0-9]{2}-[0-9]{2}Z$",
166-
},
167-
"minio": {
168-
"name": "minio",
169-
"type": "github",
170-
"owner": "minio",
171-
"match": "^RELEASE\.[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}-[0-9]{2}-[0-9]{2}Z$",
172-
},
173161
"nginx": {
174162
"name": "nginx",
175163
"type": "github",

scripts/cleaner.py

Lines changed: 183 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,26 @@
11
#!/usr/bin/env python3
22
"""
3-
Git Repository Cleaner
3+
Git Repository Cleaner and OSS Stack Cleaner
44
5-
This script provides various cleanup operations for git repositories and GitHub issues.
5+
This script provides various cleanup operations for git repositories, GitHub issues,
6+
and OSS stack files.
67
78
Usage:
89
python cleaner.py tags -n 3 [--dry-run] [--confirm]
910
python cleaner.py issues --max-issues 100 [--dry-run]
11+
python cleaner.py oss-stacks -n 3 [--dry-run]
1012
"""
1113

1214
import argparse
1315
import subprocess
1416
import sys
1517
import os
1618
import requests
19+
import oss2
20+
import re
1721
from collections import defaultdict
1822
from typing import List, Dict, Tuple
23+
from packaging import version
1924

2025

2126
def run_command(command: List[str], dry_run: bool = False) -> str:
@@ -286,6 +291,164 @@ def _close_issue(issue_number: int, issue_title: str, headers: Dict[str, str]) -
286291
print(f"Error closing issue #{issue_number}: {e}")
287292

288293

294+
def parse_oss_filename(filename: str) -> Tuple[str, str, str]:
295+
s0, s1 = filename.split("/")[-1].split("-linux-")
296+
def parse_version(full_name):
297+
parts = full_name.split('-')
298+
for i in range(len(parts)):
299+
potential_version = '-'.join(parts[i:])
300+
try:
301+
version.parse(potential_version)
302+
software_name = '-'.join(parts[:i]) if i > 0 else None
303+
return software_name, potential_version
304+
except version.InvalidVersion:
305+
continue
306+
raise ValueError(f"Could not parse version from {full_name}")
307+
os_name = s1.rstrip('.tar.gz')
308+
stack_name, stack_version = parse_version(s0)
309+
return stack_name, stack_version, os_name
310+
311+
312+
def clean_oss_stacks(args):
313+
"""Clean up old OSS stack files, keeping only the latest n versions per stack per OS."""
314+
# Initialize OSS bucket
315+
try:
316+
bucket = oss2.Bucket(
317+
oss2.Auth(
318+
os.environ.get("OSS_ACCESS_KEY_ID"),
319+
os.environ.get("OSS_ACCESS_KEY_SECRET"),
320+
),
321+
os.environ.get("OSS_ENDPOINT", "http://oss-accelerate.aliyuncs.com"),
322+
'drycc'
323+
)
324+
except Exception as e:
325+
print(f"Error initializing OSS bucket: {e}")
326+
sys.exit(1)
327+
328+
# List all objects in the stacks directory
329+
print("Listing OSS objects...")
330+
object_keys = []
331+
try:
332+
for obj in oss2.ObjectIterator(bucket, prefix='stacks/'):
333+
if obj.key.endswith('.tar.gz'):
334+
object_keys.append(obj.key)
335+
except Exception as e:
336+
print(f"Error listing OSS objects: {e}")
337+
sys.exit(1)
338+
339+
if not object_keys:
340+
print("No stack files found in OSS")
341+
return
342+
343+
print(f"Found {len(object_keys)} stack files")
344+
345+
# Handle suffix-based deletion
346+
if hasattr(args, 'subfix') and args.subfix is not None:
347+
print(f"OSS Stack Cleaner - Deleting files with suffix: {args.subfix}")
348+
349+
# Find files matching the suffix
350+
files_to_delete = []
351+
for obj_key in object_keys:
352+
# Check if the filename ends with the specified suffix
353+
filename = obj_key.split('/')[-1] # Get just the filename part
354+
if filename.endswith(args.subfix):
355+
files_to_delete.append(obj_key)
356+
357+
if not files_to_delete:
358+
print(f"No files found matching suffix: {args.subfix}")
359+
return
360+
361+
print(f"\nFound {len(files_to_delete)} files to delete:")
362+
for obj_key in sorted(files_to_delete):
363+
print(f" - {obj_key}")
364+
365+
# Confirm deletion
366+
if not args.dry_run:
367+
response = input(f"\nDelete these {len(files_to_delete)} files? (y/N): ")
368+
if response.lower() != 'y':
369+
print("Aborted by user")
370+
return
371+
372+
# Delete files
373+
success_count = 0
374+
for obj_key in files_to_delete:
375+
try:
376+
if args.dry_run:
377+
print(f"[DRY RUN] Would delete: {obj_key}")
378+
else:
379+
bucket.delete_object(obj_key)
380+
print(f"Deleted: {obj_key}")
381+
success_count += 1
382+
except Exception as e:
383+
print(f"Error deleting {obj_key}: {e}", file=sys.stderr)
384+
385+
print(f"\nCompleted: {success_count}/{len(files_to_delete)} files processed")
386+
return
387+
388+
# Handle keep-count based deletion (existing logic)
389+
if hasattr(args, 'keep_count') and args.keep_count is not None:
390+
if args.keep_count < 1:
391+
print("Error: keep-count must be at least 1", file=sys.stderr)
392+
sys.exit(1)
393+
print(f"OSS Stack Cleaner - Keeping {args.keep_count} latest versions per stack per OS")
394+
395+
# Parse and group files by stack and OS
396+
stack_os_files = defaultdict(list)
397+
398+
for obj_key in object_keys:
399+
try:
400+
stack_name, stack_version, os_name = parse_oss_filename(obj_key)
401+
package_version = version.parse(stack_version)
402+
main_version = f"{package_version.major}.{package_version.micro}"
403+
stack_os_files[(stack_name, os_name, main_version)].append((obj_key, stack_version))
404+
except ValueError as e:
405+
print(f"Warning: Skipping invalid filename {obj_key}: {e}")
406+
continue
407+
408+
# Sort versions for each stack-OS combination (newest first)
409+
for key in stack_os_files:
410+
stack_os_files[key].sort(key=lambda x: version.parse(x[1]), reverse=True)
411+
412+
# Determine which files to delete
413+
files_to_delete = []
414+
415+
for (stack_name, os_name, _), files in stack_os_files.items():
416+
if len(files) > args.keep_count:
417+
# Keep the first n files (newest), delete the rest
418+
files_to_delete.extend([file_info[0] for file_info in files[args.keep_count:]])
419+
print(f"Stack '{stack_name}' OS '{os_name}': keeping {args.keep_count} versions, deleting {len(files) - args.keep_count}")
420+
421+
if not files_to_delete:
422+
print("No files to delete - all stacks have <= {} versions per OS".format(args.keep_count))
423+
return
424+
425+
print(f"\nFound {len(files_to_delete)} files to delete:")
426+
for obj_key in sorted(files_to_delete):
427+
print(f" - {obj_key}")
428+
429+
# Confirm deletion
430+
if not args.dry_run:
431+
response = input(f"\nDelete these {len(files_to_delete)} files? (y/N): ")
432+
if response.lower() != 'y':
433+
print("Aborted by user")
434+
return
435+
436+
# Delete files
437+
success_count = 0
438+
for obj_key in files_to_delete:
439+
try:
440+
if args.dry_run:
441+
print(f"[DRY RUN] Would delete: {obj_key}")
442+
else:
443+
bucket.delete_object(obj_key)
444+
print(f"Deleted: {obj_key}")
445+
success_count += 1
446+
except Exception as e:
447+
print(f"Error deleting {obj_key}: {e}", file=sys.stderr)
448+
449+
print(f"\nCompleted: {success_count}/{len(files_to_delete)} files processed")
450+
451+
289452
def main():
290453
parser = argparse.ArgumentParser(
291454
description='Git repository cleaner with various cleanup operations',
@@ -297,6 +460,10 @@ def main():
297460
python cleaner.py tags -n 2 --confirm # Skip confirmation prompt
298461
python cleaner.py issues --max-issues 50 # Keep 50 most recent issues
299462
python cleaner.py issues --dry-run # Preview issue cleanup
463+
python cleaner.py oss-stacks -n 3 # Keep 3 latest versions per stack per OS
464+
python cleaner.py oss-stacks -n 2 --dry-run # Preview OSS stack cleanup
465+
python cleaner.py oss-stacks -s linux-arm64-debian-12.tar.gz # Delete files with specific suffix
466+
python cleaner.py oss-stacks -s linux-amd64-debian-12.tar.gz --dry-run # Preview suffix-based deletion
300467
301468
Note: Issues cleanup requires admin permissions to delete issues.
302469
If deletion fails, issues will be closed instead.
@@ -321,6 +488,18 @@ def main():
321488
issues_parser.add_argument('--dry-run', action='store_true',
322489
help='Only print commands without executing them')
323490

491+
# OSS stacks cleanup parser
492+
oss_parser = subparsers.add_parser('oss-stacks', help='Clean up old OSS stack files')
493+
oss_parser.add_argument('--dry-run', action='store_true',
494+
help='Only print commands without executing them')
495+
496+
# Make -n and -s mutually exclusive
497+
oss_group = oss_parser.add_mutually_exclusive_group(required=True)
498+
oss_group.add_argument('-n', '--keep-count', type=int,
499+
help='Number of latest versions to keep for each stack per OS')
500+
oss_group.add_argument('-s', '--subfix', type=str,
501+
help='Suffix pattern to match files for deletion (e.g., linux-arm64-debian-12.tar.gz)')
502+
324503
args = parser.parse_args()
325504

326505
if not args.action:
@@ -331,6 +510,8 @@ def main():
331510
clean_tags(args)
332511
elif args.action == 'issues':
333512
clean_github_issues(args)
513+
elif args.action == 'oss-stacks':
514+
clean_oss_stacks(args)
334515
else:
335516
parser.error(f"Unknown action: {args.action}")
336517

stacks/mc/build.sh

Lines changed: 0 additions & 17 deletions
This file was deleted.

stacks/minio/build.sh

Lines changed: 0 additions & 17 deletions
This file was deleted.

0 commit comments

Comments
 (0)