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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
|
#!/usr/bin/python
import getopt, sys, os
import settings
#__doc__="Usage: "+sys.argv[0]+" <local repository directory> <action> [<action arguments>...]"
def verbose_system(command):
print command
return os.system(command)
def list_configured_drivers():
#all .cfg files which end in .cfg as they should
return [x[:-len('.cfg')] for x in os.listdir(settings.GLOBAL_CONF_DIR) if x[-len('.cfg'):]=='.cfg']
#read key=value file to dict
def read_config(conf_file,defaults={}):
conffile=open(conf_file)
conf=defaults.copy()
for line in conffile:
if len(line):
value=line[line.find("=")+1:]
if value[-1]=='\n':
value=value[:-1]
conf[line[:line.find("=")]]=value
return conf
#returns dict of key=value config file
def read_driver_config(driver_name):
conffile=os.path.join(settings.GLOBAL_CONF_DIR,settings.DRIVER_DIR,driver_name+'.cfg')
return read_config(conffile,{
#'name':None,
#'executable':None,
})
return configs #dict
#read g-common config for a repo
def read_repo_config(repo_location):
hidden_conffile=os.path.join(repo_location,settings.MYDIR,'repo.cfg')
return read_config(hidden_conffile)
#sync a local repository's PACKAGES file
def action_sync(repo_location,driver,remote_uri):
if driver==None:
repo_conf=read_repo_config(repo_location)
driver=repo_conf['driver']
driver_conf=read_driver_config(driver)
if remote_uri is None:
remote_uri=repo_conf['uri']
if os.path.exists(repo_location):
try:
os.makedirs(os.path.join(repo_location,settings.MYDIR))
except:
pass
cfg_file=open(os.path.join(repo_location,settings.MYDIR,"repo.cfg"),"w")
cfg_file.write('driver='+driver+'\n')
cfg_file.write('uri='+remote_uri+'\n')
cfg_file.close()
return os.system(driver_conf['exec']+" "+repo_location+" sync "+remote_uri)
#list categories in this repositorie
def list_categories(repo_location):
repo_conf=read_repo_config(repo_location)
driver_conf=read_driver_config(repo_conf['driver'])
return os.system(driver_conf['exec']+" "+repo_location+" list-categories")
#idem ditto
def list_packages(repo_location):
repo_conf=read_repo_config(repo_location)
driver_conf=read_driver_config(repo_conf['driver'])
return os.system(driver_conf['exec']+" "+repo_location+" list-packages")
def parse_manifest(manifest_file):
manifest=open(manifest_file)
digests={}
for line in manifest:
parts=line.split(' ')
type=parts[0]
file=parts[1]
size=parts[2]
hashes={}
for i in range((len(parts)-3)/2):
hashes[parts[3+i*2]]=parts[4+i*2]
digests[file]=(type,file,size,hashes)
manifest.close()
return digests
def generate_digest(dir,entry,type):
import hashlib
full_name=os.path.join(dir,entry)
return (type,os.path.relpath(full_name,dir),os.path.getsize(full_name),{'SHA1':hashlib.sha1(open(full_name).read()).hexdigest()})
def write_manifest(manifest,filename):
manifest_file=open(filename,'w')
for file, data in manifest.items():
hashes=[key+' '+value for key,value in data[3].items()]
manifest_file.write("%s %s %d %s\n" % (data[0], data[1], int(data[2]), ' '.join(hashes)))
manifest_file.close()
def populate_manifest(manifest_file,dir):
import hashlib
ebuild_file=settings.COMMON_EBUILD_FILE #get from settings
ebuild_digest=hashlib.sha1(open(ebuild_file).read()).hexdigest()
if os.path.exists(manifest_file):
manifest=parse_manifest(manifest_file)
else:
manifest={}
for entry in os.listdir(dir):
full_entry=os.path.join(dir,entry)
if not os.path.isfile(full_entry): #we don't deal with dirs and stuff
continue
elif entry[0]=='.': #hidden file, skip
continue
elif entry=='Manifest': #eh, don't process this
continue
elif entry[-len('.ebuild'):]=='.ebuild': #ends in .ebuild
manifest[entry]=('EBUILD',entry,os.path.getsize(full_entry),{'SHA1':ebuild_digest})
elif entry=='ChangeLog' or entry=='metadata.xml':
manifest[entry]=generate_digest(dir,entry,'MISC')
else:
raise NotImplementedError #can't deduce file type
files_dir=os.path.join(dir,'files')
if os.path.exists(files_dir):
for entry in os.listdir(files_dir):
full_entry=os.path.join(files_dir,entry)
if not os.path.isfile(full_entry): #don't process dirs
continue
else:
digest=generate_digest(files_dir,entry,'AUX')
manifest[digest[1]]=digest
write_manifest(manifest,manifest_file)
#generate a tree of ebuilds... note that we only link ebuild files, instead of generating them
#we will, however, generate metadata.xml and Manifest files
def generate_tree(repo_location,generate_manifest,generate_metadata):
import hashlib, subprocess
repo_conf=read_repo_config(repo_location)
driver_conf=read_driver_config(repo_conf['driver'])
#clean repo
visible_files=[x for x in os.listdir(repo_location) if x[0]!='.']
import shutil
for dir in visible_files:
try:
shutil.rmtree(os.path.join(repo_location,dir))
except:
pass
#create directory structure
packages_list_pipe=subprocess.Popen(driver_conf['exec']+' '+repo_location+' list-packages',shell=True,stdout=subprocess.PIPE)
packages=[]
for line in iter(packages_list_pipe.stdout.readline,''):
if line=='':
continue
category=line[:line.find("/")]
package=line[line.find("/")+1:line.find(" ")]
version=line[line.find(" ")+1:-1]
ebuild_dir=os.path.join(repo_location,category,package)
packages.append(line)
if not os.path.exists(ebuild_dir): #obvious race condition, but whatever
os.makedirs(ebuild_dir)
os.waitpid(packages_list_pipe.pid,0)
if packages_list_pipe.returncode:
return returncode
os.makedirs(os.path.join(repo_location,'profiles'))
#call driver generate-metadata to give it a chance to fill up the repo
returncode=os.system(driver_conf['exec']+" "+repo_location+" generate-metadata")
if returncode:
return returncode
ebuild_file=settings.COMMON_EBUILD_FILE #get from settings
#write symlinks
for line in packages:
category=line[:line.find("/")]
package=line[line.find("/")+1:line.find(" ")]
version=line[line.find(" ")+1:-1]
ebuild_dir=os.path.join(repo_location,category,package)
package_file=package+'-'+version+'.ebuild'
os.symlink(ebuild_file,os.path.join(ebuild_dir,package_file))
if generate_metadata:
metadata_file=open(os.path.join(ebuild_dir,'metadata.xml'),'w')
metadata_file.close()
if generate_manifest:
populate_manifest(os.path.join(ebuild_dir,'Manifest'),ebuild_dir)
#write repo metadata
if not os.path.exists(os.path.join(repo_location,'profiles','repo_name')):
import string
#make up a name
#as a lucky guess, use the last part of the repo_location directory location
repo_location_parts=os.path.split(repo_location)
if len(repo_location_parts): #repo might be locaten in /... unlikely, but i was in a good mood while typing this
#name chars must be in [a-zA-Z_-] and must not start with a -
raw_name=repo_location_parts[-1]
name=''.join([x for x in raw_name if x in string.ascii_letters+string.digits or x=='_' or x=='-'])
if name[0]=='-': #name may not begin with a hyphen
name=name[1:]
else:
name='common-repo' #fallback
repo_name_file=open(os.path.join(repo_location,'profiles','repo_name'),'w')
repo_name_file.write(name)
repo_name_file.close()
if not os.path.exists(os.path.join(repo_location,'profiles','categories')):
categories_file=open(os.path.join(repo_location,'profiles','categories'),'w')
categories_file.write('\n'.join(list(set([package[:package.find('/')] for package in packages])))) #now isn't that a readable oneliner
categories_file.close()
return 0
#list package details, in PMS's format
def action_package(repo_location,package_name,version):
repo_conf=read_repo_config(repo_location)
driver_conf=read_driver_config(repo_conf['driver'])
version_append=''
if version:
version_append=' '+version
return os.system(driver_conf['exec']+" "+repo_location+" package "+package_name+version_append)
#do one of the ebuild phases
def exec_phase(repo_location,phase):
repo_conf=read_repo_config(repo_location)
driver_conf=read_driver_config(repo_conf['driver'])
env=os.environ
return os.system(driver_conf['exec']+" "+repo_location+" "+phase)
def usage():
print __doc__
return 0
def main():
arguments=sys.argv[1:]
#print options, arguments
if len(arguments)<2: #we need at least a local repository location and an action
usage()
sys.exit(0)
action=arguments[1]
repo_location=os.path.abspath(arguments[0])
if action=='sync':
if len(arguments)<2 or 'help' in arguments:
print "The 'sync' action takes the following parameters:"
print " * [driver]"
print " * [remote_repository_uri]"
sys.exit(1)
driver=None
remote_repo=None
if len(arguments)>2:
driver=arguments[2]
if len(arguments)>3:
remote_repo=arguments[3]
return action_sync(repo_location,driver,remote_repo)
elif action=='list-categories':
return list_categories(repo_location)
elif action=='list-packages':
return list_packages(repo_location)
elif action=='generate-tree':
if '--without-manifest' in arguments:
manifest=False
else:
manifest=True
if '--without-metadata' in arguments:
metadata=False
else:
metadata=True
return generate_tree(repo_location,manifest,metadata)
elif action=='package':
if len(arguments)<3 or 'help' in arguments:
print "The 'package' action takes the following parameters:"
print " * category/package_name"
print " * [version]"
sys.exit(1)
package_name=arguments[2]
version=None
if len(arguments)>3: #version
version=arguments[3]
return action_package(repo_location,package_name,version)
elif action=='usage' or action=='help':
return usage()
elif action in settings.PMS_PHASES:
return exec_phase(repo_location,action)
else:
return usage()
if __name__ == "__main__":
sys.exit(main())
|