-
Notifications
You must be signed in to change notification settings - Fork 132
/
Copy pathrepoclean
executable file
·144 lines (120 loc) · 5.3 KB
/
repoclean
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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#!/usr/bin/env python
# encoding: utf-8
#
# Copyright 2019 Disney Enterprises, Inc. All rights reserved
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
# * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
# Studios" or the names of its contributors may NOT be used to
# endorse or promote products derived from this software without
# specific prior written permission from Walt Disney Pictures.
# Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
# IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
'''A tool to clean abandonded products from a reposado repo'''
from __future__ import absolute_import
from __future__ import print_function
import os
import shutil
import sys
from reposadolib import reposadocommon
# Python 2 and 3 wrapper for raw_input/input
try:
# Python 2
get_input = raw_input # pylint: disable=raw_input-builtin, invalid-name
except NameError:
# Python 3
get_input = input # pylint: disable=input-builtin, invalid-name
def find_all_product_dirs_on_disk():
'''Walks downloads dir and returns a set of all directories that appear to
contain a product'''
root_dir = reposadocommon.pref('UpdatesRootDir')
downloads_dir = os.path.join(root_dir, "content/downloads")
product_dirs = set()
for (path, _, files) in os.walk(downloads_dir):
for a_file in files:
if a_file.endswith((".smd", ".pkm", ".pkg", ".mpkg", ".tar")):
product_dirs.add(path)
break
return product_dirs
def get_product_location(product):
'''Returns local path to replicated product.'''
if not 'CatalogEntry' in product:
# something is wrong with the product entry
return None
catalog_entry = product['CatalogEntry']
product_url = None
if 'ServerMetadataURL' in catalog_entry:
product_url = catalog_entry['ServerMetadataURL']
else:
try:
# get the URL for the first package in the Packages array
product_url = catalog_entry['Packages'][0]['URL']
except (KeyError, IndexError):
return None
filepath = reposadocommon.getLocalPathNameFromURL(product_url)
# return the directory this pkg is in
return os.path.dirname(filepath)
def all_product_dirs_from_productinfo(): # pylint: disable=invalid-name
'''Returns a set of all the product directories for products in our
ProductInformation.plist'''
product_dirs = set()
products = reposadocommon.getProductInfo()
for product in products:
product_location = get_product_location(products[product])
if product_location:
product_dirs.add(product_location)
return product_dirs
def main():
'''Here's the main thing we do!'''
if not reposadocommon.pref('LocalCatalogURLBase'):
print("We're not replicating products, so nothing to clean up!")
return
print("Finding all product directories stored on disk...")
disk_dirs = find_all_product_dirs_on_disk()
print(" Found %s products" % len(disk_dirs))
print("Finding all product directories in our internal database...")
info_dirs = all_product_dirs_from_productinfo()
print(" Found %s products" % len(info_dirs))
missing_disk_dirs = info_dirs - disk_dirs
if missing_disk_dirs:
print("\nFound %s products that might not be on-disk:"
% len(missing_disk_dirs))
print("\n".join(sorted(missing_disk_dirs)))
orphaned_dirs = disk_dirs - info_dirs
if orphaned_dirs:
print("\nFound %s abandoned/orphaned product directories:"
% len(orphaned_dirs))
print("\n".join(sorted(orphaned_dirs)))
print()
answer = get_input(
'Remove abandoned product directories '
'(WARNING: this cannot be undone)? [y/n]: ')
if answer.lower().startswith('y'):
for directory in orphaned_dirs:
print ("Removing %s..." % directory)
try:
shutil.rmtree(directory)
except (OSError, IOError) as err:
print('Error: %s' % err, file=sys.stderr)
else:
print("Can't find anything to clean up!")
if __name__ == '__main__':
main()