mirror of
https://github.com/openSUSE/osc.git
synced 2025-01-27 15:06:15 +01:00
0.3
status: - use new file metadata which provides checksum, size and mtime - don't list unmodified files - fix some conditions where status was too stupid so far update: - much faster now, since only new or changed files are downloaded - print revision number checkout: - mark project directory as such (to be used later) diff: - faster, since 'status' is faster - work against local copy checkin: - update package metadata from specfile - fix argument handling add: - ignore .osc and other files other changes: - sanitize argument handling, so most commands can work with arbitrary files/directories as arguments (work is always done per package) - add history command (doesn't seem to be working yet) - on first usage, ask for username password on first usage, and store them in .oscrc (.netrc can still be used)
This commit is contained in:
parent
0871a8ce3f
commit
4b83218cf6
46
README
46
README
@ -10,6 +10,10 @@ https://forgesvn1.novell.com/svn/opensuse/trunk/buildservice/src/clientlib/pytho
|
||||
|
||||
INSTALLATION:
|
||||
|
||||
RPM packages are here (yum repository):
|
||||
http://repos.opensuse.org/opensuse/repositories/openSUSE:Tools/
|
||||
|
||||
To install from svn, do
|
||||
python setup.py build
|
||||
python setup.py install
|
||||
# create a symlink 'osc' in your path pointing to osc.py.
|
||||
@ -28,10 +32,8 @@ on SUSE anyway):
|
||||
|
||||
CONFIGURATION:
|
||||
|
||||
For authentication, put your account data into your ~/.netrc file, like this
|
||||
line:
|
||||
machine api.opensuse.org login $login password $pass
|
||||
|
||||
osc uses authentication data if it finds it in .netrc, otherwise it will ask
|
||||
for username/password once, and store it in ~/.oscrc.
|
||||
|
||||
|
||||
|
||||
@ -49,6 +51,7 @@ osc co Apache subversion foo # single file
|
||||
# update working copy
|
||||
osc up
|
||||
osc up <directory>
|
||||
osc up * # from within a project dir, update all packages
|
||||
|
||||
# check in
|
||||
osc ci # current dir
|
||||
@ -58,20 +61,22 @@ osc ci file1 file2 ...
|
||||
# show status
|
||||
osc st
|
||||
osc st <directory>
|
||||
osc st file1 file2 ...
|
||||
|
||||
# initialize a source directory to be a
|
||||
# working copy of project <prj> package <pac>
|
||||
osc init <prj> <pac>
|
||||
|
||||
# schedule file foo to be added / deleted
|
||||
osc add foo
|
||||
osc rm foo
|
||||
# schedule files foo to be added / deleted
|
||||
osc add file1 file2 ...
|
||||
osc rm file1 file2 ...
|
||||
|
||||
# add all unknown files and remove all missing files
|
||||
# add all unknown files AND remove all missing files
|
||||
osc addremove
|
||||
|
||||
# show diff
|
||||
osc diff [file]
|
||||
osc diff # current dir
|
||||
osc diff file1 file2 ...
|
||||
|
||||
# show build results (xml)
|
||||
osc results
|
||||
@ -90,3 +95,26 @@ osc meta Apache
|
||||
osc meta Apache subversion
|
||||
osc id username
|
||||
|
||||
|
||||
|
||||
|
||||
HINT FOR W3M USERS
|
||||
|
||||
Putting the following in the file ~/.w3m/passwd will make
|
||||
w3m know the credentials for the buildservice servers:
|
||||
|
||||
"""
|
||||
host api.opensuse.org
|
||||
port 80
|
||||
realm Authentication required
|
||||
login foo
|
||||
password bar
|
||||
|
||||
host build.opensuse.org
|
||||
port 80
|
||||
realm openSUSE Build Service
|
||||
login foo
|
||||
password bar
|
||||
"""
|
||||
|
||||
chmod 0600 ~/.w3m/passwd
|
||||
|
17
TODO
17
TODO
@ -1,14 +1,27 @@
|
||||
- implement 'info' command
|
||||
- implement 'mv' command
|
||||
- implement 'trigger-rebuild'
|
||||
|
||||
update: handle local modifications
|
||||
|
||||
checkin:
|
||||
- fix argument handling
|
||||
- aggregate files to check in per directory
|
||||
- handle error if PUT fails, so the change is not committed to
|
||||
localmeta
|
||||
|
||||
diff: use rev numbers in diff header lines
|
||||
|
||||
results: aggregate results of all packages in a project
|
||||
|
||||
|
||||
|
||||
|
||||
split functionality that needs prj/pac as commandline arguments into a seperate tool (oscremote)
|
||||
|
||||
implement a package search / indexing
|
||||
|
||||
edit user data
|
||||
<watchlist>
|
||||
<project name="ASCIIParadize"/>
|
||||
<project name="crashtest"/>
|
||||
</watchlist>
|
||||
|
||||
|
@ -1,4 +1,8 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# this wrapper exists so it can be put into /usr/bin, but still allows the
|
||||
# python module to be called within the source directory during development
|
||||
|
||||
from osc import commandline
|
||||
from osc.core import init_basicauth
|
||||
|
||||
|
@ -7,7 +7,6 @@
|
||||
|
||||
from core import *
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
cmd = sys.argv[1]
|
||||
@ -47,17 +46,21 @@ def main():
|
||||
print ''.join(show_project_meta(project))
|
||||
|
||||
elif cmd == 'diff':
|
||||
wd = os.curdir
|
||||
package = store_read_package(wd)
|
||||
project = store_read_project(wd)
|
||||
if len(sys.argv) > 2:
|
||||
filename = sys.argv[2]
|
||||
if filename:
|
||||
print get_source_file_diff(project, package, filename)
|
||||
else:
|
||||
args = parseargs()
|
||||
pacs = findpacs(args)
|
||||
|
||||
for p in pacs:
|
||||
if p.todo == []:
|
||||
for i in p.filenamelist:
|
||||
if p.status(i) == 'M':
|
||||
p.todo.append(i)
|
||||
|
||||
d = []
|
||||
for filename in meta_get_filelist(project, package):
|
||||
d.append(get_source_file_diff(project, package, filename))
|
||||
for filename in p.todo:
|
||||
d.append('Index: %s\n' % filename)
|
||||
d.append('===================================================================\n')
|
||||
d.append(get_source_file_diff(p.dir, filename, p.rev))
|
||||
if d:
|
||||
print ''.join(d)
|
||||
|
||||
|
||||
@ -85,31 +88,20 @@ def main():
|
||||
elif cmd == 'st' or cmd == 'status':
|
||||
|
||||
args = parseargs()
|
||||
pacs = findpacs(args)
|
||||
|
||||
for arg in args:
|
||||
for p in pacs:
|
||||
|
||||
if os.path.isfile(arg):
|
||||
wd = os.path.dirname(arg)
|
||||
filenames = [ os.path.basename(arg) ]
|
||||
elif os.path.isdir(arg):
|
||||
wd = arg
|
||||
package = store_read_package(wd)
|
||||
project = store_read_project(wd)
|
||||
filenames = meta_get_filelist(project, package)
|
||||
# no files given as argument? Take all files in current dir
|
||||
if not p.todo:
|
||||
p.todo = p.filenamelist + p.filenamelist_unvers
|
||||
|
||||
# add files which are not listed in _meta
|
||||
for i in os.listdir(arg):
|
||||
if i not in filenames and i not in exclude_stuff:
|
||||
filenames.insert(0, i)
|
||||
|
||||
os.chdir(wd)
|
||||
check_store_version()
|
||||
|
||||
filelist = localmeta_get_filelist()
|
||||
for filename in filenames:
|
||||
s = get_file_status(project, package, filename, filelist=filelist)
|
||||
if not s.startswith(' '):
|
||||
print s
|
||||
for filename in p.todo:
|
||||
s = p.status(filename)
|
||||
if s == 'F':
|
||||
print statfrmt('!', filename)
|
||||
elif s != ' ':
|
||||
print statfrmt(s, filename)
|
||||
|
||||
|
||||
elif cmd == 'add':
|
||||
@ -123,155 +115,122 @@ def main():
|
||||
if not os.path.exists(filename):
|
||||
print "file '%s' does not exist" % filename
|
||||
sys.exit(1)
|
||||
for filename in filenames:
|
||||
localmeta_addfile(filename)
|
||||
print 'A ', filename
|
||||
|
||||
pacs = findpacs(filenames)
|
||||
|
||||
for pac in pacs:
|
||||
for filename in pac.todo:
|
||||
if filename in exclude_stuff:
|
||||
continue
|
||||
|
||||
pac.addfile(filename)
|
||||
print statfrmt('A', filename)
|
||||
|
||||
|
||||
elif cmd == 'addremove':
|
||||
args = parseargs()
|
||||
pacs = findpacs(args)
|
||||
for p in pacs:
|
||||
|
||||
for arg in args:
|
||||
p.todo = p.filenamelist + p.filenamelist_unvers
|
||||
|
||||
if os.path.isfile(arg):
|
||||
wd = os.path.dirname(arg)
|
||||
filenames = [ os.path.basename(arg) ]
|
||||
elif os.path.isdir(arg):
|
||||
wd = arg
|
||||
package = store_read_package(wd)
|
||||
project = store_read_project(wd)
|
||||
filenames = meta_get_filelist(project, package)
|
||||
for filename in p.todo:
|
||||
if filename in exclude_stuff:
|
||||
continue
|
||||
state = p.status(filename)
|
||||
if state == '?':
|
||||
p.addfile(filename)
|
||||
print statfrmt('A', filename)
|
||||
elif state == '!':
|
||||
p.put_on_deletelist(filename)
|
||||
p.write_deletelist()
|
||||
os.unlink(os.path.join(p.storedir, filename))
|
||||
print statfrmt('D', filename)
|
||||
|
||||
# add files which are not listed in _meta
|
||||
for i in os.listdir(arg):
|
||||
if i not in filenames and i not in exclude_stuff:
|
||||
filenames.insert(0, i)
|
||||
|
||||
|
||||
|
||||
filelist = localmeta_get_filelist()
|
||||
for filename in filenames:
|
||||
st = get_file_status(project, package, filename, filelist=filelist)
|
||||
if st.startswith('?'):
|
||||
localmeta_addfile(filename)
|
||||
print 'A ', filename
|
||||
elif st.startswith('!'):
|
||||
print 'D ', filename
|
||||
localmeta_removefile(filename)
|
||||
|
||||
|
||||
|
||||
elif cmd == 'ci' or cmd == 'checkin':
|
||||
init_basicauth()
|
||||
|
||||
args = parseargs()
|
||||
|
||||
for arg in args:
|
||||
pacs = findpacs(args)
|
||||
|
||||
if os.path.isfile(arg):
|
||||
wd = os.path.dirname(arg)
|
||||
filenames = [ os.path.basename(arg) ]
|
||||
elif os.path.isdir(arg):
|
||||
wd = arg
|
||||
package = store_read_package(wd)
|
||||
project = store_read_project(wd)
|
||||
filenames = meta_get_filelist(project, package)
|
||||
for p in pacs:
|
||||
p.todo = p.filenamelist_unvers + p.filenamelist
|
||||
|
||||
# add files which are not listed in _meta
|
||||
for i in os.listdir(arg):
|
||||
if i not in filenames and i not in exclude_stuff:
|
||||
filenames.insert(0, i)
|
||||
|
||||
os.chdir(wd)
|
||||
|
||||
files_to_send = []
|
||||
files_to_delete = []
|
||||
|
||||
filelist = localmeta_get_filelist()
|
||||
for filename in filenames:
|
||||
st = get_file_status(project, package, filename, filelist=filelist)
|
||||
if st.startswith('A') or st.startswith('M'):
|
||||
files_to_send.append(filename)
|
||||
for filename in p.todo:
|
||||
st = p.status(filename)
|
||||
if st == 'A' or st == 'M':
|
||||
p.todo_send.append(filename)
|
||||
print 'Sending %s' % filename
|
||||
elif st.startswith('D'):
|
||||
files_to_delete.append(filename)
|
||||
elif st == 'D':
|
||||
p.todo_delete.append(filename)
|
||||
print 'Deleting %s' % filename
|
||||
|
||||
if not files_to_send and not files_to_delete:
|
||||
print 'nothing to do'
|
||||
sys.exit(0)
|
||||
if not p.todo_send and not p.todo_delete:
|
||||
print 'nothing to do for package %s' % p.name
|
||||
continue
|
||||
|
||||
print 'Transmitting file data ',
|
||||
for filename in files_to_send:
|
||||
put_source_file(project, package, filename)
|
||||
copy_file(filename, os.path.join(store, filename))
|
||||
for filename in files_to_delete:
|
||||
del_source_file(project, package, filename)
|
||||
for filename in p.todo_send:
|
||||
put_source_file(p.prjname, p.name, os.path.join(p.dir, filename))
|
||||
#copy_file(filename, os.path.join(store, filename))
|
||||
for filename in p.todo_delete:
|
||||
del_source_file(p.prjname, p.name, filename)
|
||||
p.to_be_deleted.remove(filename)
|
||||
|
||||
p.update_filesmeta()
|
||||
p.write_deletelist()
|
||||
print
|
||||
|
||||
|
||||
elif cmd == 'up' or cmd == 'update':
|
||||
|
||||
args = parseargs()
|
||||
pacs = findpacs(args)
|
||||
|
||||
for arg in args:
|
||||
|
||||
if os.path.isfile(arg):
|
||||
wd = os.path.dirname(arg)
|
||||
filenames = [ os.path.basename(arg) ]
|
||||
elif os.path.isdir(arg):
|
||||
wd = arg
|
||||
package = store_read_package(wd)
|
||||
project = store_read_project(wd)
|
||||
|
||||
## add files which are not listed in _meta
|
||||
#for i in os.listdir(arg):
|
||||
# if i not in filenames and i not in exclude_stuff:
|
||||
# filenames.insert(0, i)
|
||||
|
||||
olddir = os.getcwd()
|
||||
os.chdir(wd)
|
||||
check_store_version()
|
||||
for p in pacs:
|
||||
|
||||
# save filelist before replacing the meta file
|
||||
filenames = localmeta_get_filelist()
|
||||
os.chdir(store)
|
||||
# update filelist
|
||||
f = open('_files', 'w')
|
||||
f.write(''.join(show_files_meta(project, package)))
|
||||
f.close()
|
||||
saved_filenames = p.filenamelist
|
||||
p.update_filesmeta()
|
||||
p = Package(p.dir)
|
||||
|
||||
# which files do no longer exist upstream?
|
||||
disappeared = []
|
||||
upstream_files = meta_get_filelist(project, package)
|
||||
for filename in filenames:
|
||||
if filename not in upstream_files:
|
||||
for filename in saved_filenames:
|
||||
if filename not in p.filenamelist:
|
||||
disappeared.append(filename)
|
||||
|
||||
|
||||
for filename in filenames:
|
||||
for filename in saved_filenames:
|
||||
if filename in disappeared:
|
||||
print 'D %s' % filename
|
||||
os.unlink(filename)
|
||||
print statfrmt('D', filename)
|
||||
p.delfile(filename)
|
||||
continue
|
||||
|
||||
get_source_file(project, package, filename)
|
||||
wcfilename = os.path.join(os.pardir, os.path.basename(filename))
|
||||
for filename in p.filenamelist:
|
||||
|
||||
if not os.path.exists(wcfilename):
|
||||
print 'A %s' % filename
|
||||
copy_file(filename, wcfilename)
|
||||
|
||||
elif dgst(wcfilename) != dgst(filename):
|
||||
print 'U %s' % filename
|
||||
copy_file(filename, wcfilename)
|
||||
|
||||
else:
|
||||
state = p.status(filename)
|
||||
if state == 'M':
|
||||
print 'file %s is locally modified... fixme' % filename
|
||||
p.updatefile(filename)
|
||||
print statfrmt('U', filename)
|
||||
elif state == '!':
|
||||
p.updatefile(filename)
|
||||
print 'Restored \'%s\'' % filename
|
||||
elif state == 'F':
|
||||
p.updatefile(filename)
|
||||
print statfrmt('A', filename)
|
||||
elif state == ' ':
|
||||
pass
|
||||
|
||||
# get current meta file
|
||||
f = open('_meta', 'w')
|
||||
f.write(''.join(show_package_meta(project, package)))
|
||||
f.close()
|
||||
|
||||
os.chdir(olddir)
|
||||
p.update_pacmeta()
|
||||
|
||||
print 'At revision %s.' % p.rev
|
||||
|
||||
|
||||
|
||||
@ -283,25 +242,26 @@ def main():
|
||||
sys.exit(1)
|
||||
|
||||
args = parseargs()
|
||||
pacs = findpacs(args)
|
||||
|
||||
for arg in args:
|
||||
for p in pacs:
|
||||
|
||||
olddir = os.getcwd()
|
||||
for filename in p.todo:
|
||||
p.put_on_deletelist(filename)
|
||||
p.write_deletelist()
|
||||
try:
|
||||
os.unlink(os.path.join(p.dir, filename))
|
||||
os.unlink(os.path.join(p.storedir, filename))
|
||||
except:
|
||||
pass
|
||||
print statfrmt('D', filename)
|
||||
|
||||
wd = os.path.dirname(arg) or os.curdir
|
||||
filename = arg
|
||||
|
||||
os.chdir(wd)
|
||||
|
||||
localmeta_removefile(filename)
|
||||
print 'D %s' % filename
|
||||
|
||||
os.chdir(olddir)
|
||||
|
||||
|
||||
elif cmd == 'id':
|
||||
print ''.join(get_user_id(sys.argv[2]))
|
||||
|
||||
|
||||
elif cmd == 'platforms':
|
||||
if len(sys.argv) > 2:
|
||||
project = sys.argv[2]
|
||||
@ -310,7 +270,6 @@ def main():
|
||||
print '\n'.join(get_platforms())
|
||||
|
||||
|
||||
|
||||
elif cmd == 'results_meta':
|
||||
wd = os.curdir
|
||||
package = store_read_package(wd)
|
||||
@ -322,6 +281,7 @@ def main():
|
||||
for platform in get_platforms_of_project(project):
|
||||
print ''.join(show_results_meta(project, package, platform))
|
||||
|
||||
|
||||
elif cmd == 'results':
|
||||
if len(sys.argv) > 3:
|
||||
print 'getting results for more than one package is not supported'
|
||||
@ -347,9 +307,19 @@ def main():
|
||||
arch = sys.argv[3]
|
||||
print ''.join(get_log(project, package, platform, arch))
|
||||
|
||||
|
||||
elif cmd == 'hist' or cmd == 'history':
|
||||
args = parseargs()
|
||||
pacs = findpacs(args)
|
||||
|
||||
for p in pacs:
|
||||
print ''.join(get_history(p.prjname, p.name))
|
||||
|
||||
|
||||
else:
|
||||
print "unknown command '%s'" % cmd
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
init_basicauth()
|
||||
main()
|
||||
|
477
osc/core.py
477
osc/core.py
@ -5,20 +5,15 @@
|
||||
# and distributed under the terms of the GNU General Public Licence,
|
||||
# either version 2, or (at your option) any later version.
|
||||
|
||||
__version__ = '0.2'
|
||||
__version__ = '0.3'
|
||||
|
||||
import os
|
||||
import sys
|
||||
import urllib2
|
||||
import netrc
|
||||
from urlparse import urlunsplit
|
||||
import cElementTree as ET
|
||||
from cStringIO import StringIO
|
||||
|
||||
# the needed entry in .netrc looks like this:
|
||||
# machine api.opensuse.org login your_login password your_pass
|
||||
info = netrc.netrc()
|
||||
username, account, password = info.authenticators("api.opensuse.org")
|
||||
|
||||
from xml.dom.ext.reader import Sax2
|
||||
from xml.dom.ext import PrettyPrint
|
||||
@ -31,6 +26,222 @@ store = '.osc'
|
||||
exclude_stuff = [store, '.svn', 'CVS']
|
||||
|
||||
|
||||
class File:
|
||||
"""represent a file, including its metadata"""
|
||||
def __init__(self, name, md5, size, mtime):
|
||||
self.name = name
|
||||
self.md5 = md5
|
||||
self.size = size
|
||||
self.mtime = mtime
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class Package:
|
||||
"""represent a package (its directory) and read/keep/write its metadata"""
|
||||
def __init__(self, workingdir):
|
||||
self.dir = workingdir
|
||||
self.storedir = os.path.join(self.dir, store)
|
||||
|
||||
check_store_version(self.dir)
|
||||
|
||||
self.prjname = store_read_project(self.dir)
|
||||
self.name = store_read_package(self.dir)
|
||||
|
||||
files_tree = read_filemeta(self.dir)
|
||||
files_tree_root = files_tree.getroot()
|
||||
|
||||
self.rev = files_tree_root.get('rev')
|
||||
|
||||
self.filenamelist = []
|
||||
self.filelist = []
|
||||
for node in files_tree_root.findall('entry'):
|
||||
f = File(node.get('name'),
|
||||
node.get('md5'),
|
||||
node.get('size'),
|
||||
node.get('mtime'))
|
||||
self.filelist.append(f)
|
||||
self.filenamelist.append(f.name)
|
||||
|
||||
self.to_be_deleted = read_tobedeleted(self.dir)
|
||||
|
||||
self.todo = []
|
||||
self.todo_send = []
|
||||
self.todo_delete = []
|
||||
|
||||
# gather unversioned files (the ones not listed in _meta)
|
||||
self.filenamelist_unvers = []
|
||||
for i in os.listdir(self.dir):
|
||||
if i in exclude_stuff:
|
||||
continue
|
||||
if not i in self.filenamelist:
|
||||
self.filenamelist_unvers.append(i)
|
||||
|
||||
def addfile(self, n):
|
||||
st = os.stat(os.path.join(self.dir, n))
|
||||
f = File(n, dgst(os.path.join(self.dir, n)), st[6], st[8])
|
||||
self.filelist.append(f)
|
||||
self.filenamelist.append(n)
|
||||
self.filenamelist_unvers.remove(n)
|
||||
copy_file(os.path.join(self.dir, n), os.path.join(self.storedir, n))
|
||||
|
||||
def delfile(self, n):
|
||||
os.unlink(os.path.join(self.dir, n))
|
||||
os.unlink(os.path.join(self.storedir, n))
|
||||
|
||||
def put_on_deletelist(self, n):
|
||||
if n not in self.to_be_deleted:
|
||||
self.to_be_deleted.append(n)
|
||||
|
||||
def write_deletelist(self):
|
||||
fname = os.path.join(self.storedir, '_to_be_deleted')
|
||||
f = open(fname, 'w')
|
||||
f.write('\n'.join(self.to_be_deleted))
|
||||
f.write('\n')
|
||||
f.close()
|
||||
|
||||
def updatefile(self, n):
|
||||
filename = os.path.join(self.dir, n)
|
||||
storefilename = os.path.join(self.storedir, n)
|
||||
mtime = int(self.findfilebyname(n).mtime)
|
||||
|
||||
get_source_file(self.prjname, self.name, n, targetfilename=filename)
|
||||
os.utime(filename, (-1, mtime))
|
||||
|
||||
copy_file(filename, storefilename)
|
||||
os.utime(storefilename, (-1, mtime))
|
||||
|
||||
def update_filesmeta(self):
|
||||
meta = ''.join(show_files_meta(self.prjname, self.name))
|
||||
f = open(os.path.join(self.storedir, '_files'), 'w')
|
||||
f.write(meta)
|
||||
f.close()
|
||||
|
||||
def update_pacmeta(self):
|
||||
meta = ''.join(show_package_meta(self.prjname, self.name))
|
||||
f = open(os.path.join(self.storedir, '_meta'), 'w')
|
||||
f.write(meta)
|
||||
f.close()
|
||||
|
||||
def findfilebyname(self, n):
|
||||
for i in self.filelist:
|
||||
if i.name == n:
|
||||
return i
|
||||
|
||||
def status(self, n):
|
||||
"""
|
||||
status can be:
|
||||
|
||||
file storefile file present STATUS
|
||||
exists exists in _files
|
||||
|
||||
x x - 'A'
|
||||
x x x 'M', if digest differs, else ' '
|
||||
x - - '?'
|
||||
x - x 'D' and listed in _to_be_deleted
|
||||
- x x '!'
|
||||
- x - 'D' (when file in working copy is already deleted)
|
||||
- - x 'F' (new in repo, but not yet in working copy)
|
||||
- - - NOT DEFINED
|
||||
|
||||
"""
|
||||
|
||||
known_by_meta = False
|
||||
exists = False
|
||||
exists_in_store = False
|
||||
if n in self.filenamelist:
|
||||
known_by_meta = True
|
||||
if os.path.exists(os.path.join(self.dir, n)):
|
||||
exists = True
|
||||
if os.path.exists(os.path.join(self.storedir, n)):
|
||||
exists_in_store = True
|
||||
|
||||
|
||||
if exists and not exists_in_store and known_by_meta:
|
||||
state = 'D'
|
||||
elif n in self.to_be_deleted:
|
||||
state = 'D'
|
||||
elif exists and exists_in_store and known_by_meta:
|
||||
#print self.findfilebyname(n)
|
||||
if dgst(os.path.join(self.dir, n)) != self.findfilebyname(n).md5:
|
||||
state = 'M'
|
||||
else:
|
||||
state = ' '
|
||||
elif exists and not exists_in_store and not known_by_meta:
|
||||
state = '?'
|
||||
elif exists and exists_in_store and not known_by_meta:
|
||||
state = 'A'
|
||||
elif not exists and exists_in_store and known_by_meta:
|
||||
state = '!'
|
||||
elif not exists and not exists_in_store and known_by_meta:
|
||||
state = 'F'
|
||||
elif not exists and exists_in_store and not known_by_meta:
|
||||
state = 'D'
|
||||
elif not exists and not exists_in_store and not known_by_meta:
|
||||
print '%s: not exists and not exists_in_store and not nown_by_meta' % n
|
||||
print 'this code path should never be reached!'
|
||||
sys.exit(1)
|
||||
|
||||
return state
|
||||
|
||||
|
||||
def merge(self, otherpac):
|
||||
self.todo += otherpac.todo
|
||||
|
||||
def __str__(self):
|
||||
r = """
|
||||
name: %s
|
||||
prjname: %s
|
||||
workingdir: %s
|
||||
localfilelist: %s
|
||||
rev: %s
|
||||
'todo' files: %s
|
||||
""" % (self.name,
|
||||
self.prjname,
|
||||
self.dir,
|
||||
'\n '.join(self.filenamelist),
|
||||
self.rev,
|
||||
self.todo)
|
||||
|
||||
return r
|
||||
|
||||
|
||||
|
||||
def findpacs(files):
|
||||
pacs = []
|
||||
for f in files:
|
||||
if f in exclude_stuff:
|
||||
break
|
||||
|
||||
p = filedir_to_pac(f)
|
||||
known = None
|
||||
for i in pacs:
|
||||
if i.name == p.name:
|
||||
known = i
|
||||
break
|
||||
if known:
|
||||
i.merge(p)
|
||||
else:
|
||||
pacs.append(p)
|
||||
return pacs
|
||||
|
||||
|
||||
def read_filemeta(dir):
|
||||
return ET.parse(os.path.join(dir, store, '_files'))
|
||||
|
||||
|
||||
def read_tobedeleted(dir):
|
||||
r = []
|
||||
fname = os.path.join(dir, store, '_to_be_deleted')
|
||||
|
||||
if os.path.exists(fname):
|
||||
|
||||
for i in open(fname, 'r').readlines():
|
||||
r.append(i.strip())
|
||||
|
||||
return r
|
||||
|
||||
|
||||
def parseargs():
|
||||
if len(sys.argv) > 2:
|
||||
args = sys.argv[2:]
|
||||
@ -39,12 +250,46 @@ def parseargs():
|
||||
return args
|
||||
|
||||
|
||||
def filedir_to_pac(f):
|
||||
|
||||
if os.path.isdir(f):
|
||||
wd = f
|
||||
p = Package(wd)
|
||||
|
||||
elif os.path.isfile(f):
|
||||
wd = os.path.dirname(f)
|
||||
if wd == '':
|
||||
wd = os.curdir
|
||||
p = Package(wd)
|
||||
p.todo = [ os.path.basename(f) ]
|
||||
|
||||
else:
|
||||
wd = os.path.dirname(f)
|
||||
if wd == '':
|
||||
wd = os.curdir
|
||||
p = Package(wd)
|
||||
p.todo = [ os.path.basename(f) ]
|
||||
|
||||
|
||||
#else:
|
||||
# print
|
||||
# print 'error: %s is neither a valid file or directory' % f
|
||||
# sys.exit(1)
|
||||
|
||||
return p
|
||||
|
||||
|
||||
def statfrmt(statusletter, filename):
|
||||
return '%s %s' % (statusletter, filename)
|
||||
|
||||
|
||||
def makeurl(l):
|
||||
"""given a list of path compoments, construct a complete URL"""
|
||||
return urlunsplit((scheme, netloc, '/'.join(l), '', ''))
|
||||
|
||||
|
||||
def copy_file(src, dst):
|
||||
# fixme: preserve mtime by default?
|
||||
s = open(src)
|
||||
d = open(dst, 'w')
|
||||
while 1:
|
||||
@ -55,8 +300,65 @@ def copy_file(src, dst):
|
||||
d.close()
|
||||
|
||||
|
||||
def readauth():
|
||||
"""look for the credentials. If there aren't any, ask and store them"""
|
||||
|
||||
#
|
||||
# try .netrc first
|
||||
#
|
||||
|
||||
# the needed entry in .netrc looks like this:
|
||||
# machine api.opensuse.org login your_login password your_pass
|
||||
# but it is not able for credentials containing spaces
|
||||
import netrc
|
||||
global username, password
|
||||
|
||||
try:
|
||||
info = netrc.netrc()
|
||||
username, account, password = info.authenticators(netloc)
|
||||
return username, password
|
||||
|
||||
except (IOError, TypeError):
|
||||
pass
|
||||
|
||||
#
|
||||
# try .oscrc next
|
||||
#
|
||||
import ConfigParser
|
||||
conffile = os.path.expanduser('~/.oscrc')
|
||||
if os.path.exists(conffile):
|
||||
config = ConfigParser.ConfigParser()
|
||||
config.read(conffile)
|
||||
username = config.get(netloc, 'user')
|
||||
password = config.get(netloc, 'pass')
|
||||
return username, password
|
||||
|
||||
#
|
||||
# create .oscrc
|
||||
#
|
||||
import getpass
|
||||
print >>sys.stderr, \
|
||||
"""your user account / password are not configured yet.
|
||||
You will be asked for them below, and they will be stored in
|
||||
%s for later use.
|
||||
""" % conffile
|
||||
|
||||
username = raw_input('Username: ')
|
||||
password = getpass.getpass()
|
||||
|
||||
fd = open(conffile, 'w')
|
||||
os.chmod(conffile, 0600)
|
||||
print >>fd, '[%s]\nuser: %s\npass: %s' % (netloc, username, password)
|
||||
fd.close()
|
||||
|
||||
return username, password
|
||||
|
||||
|
||||
|
||||
def init_basicauth():
|
||||
|
||||
username, password = readauth()
|
||||
|
||||
passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
|
||||
# this creates a password manager
|
||||
passmgr.add_password(None, netloc, username, password)
|
||||
@ -98,23 +400,32 @@ def init_package_dir(project, package, dir):
|
||||
return
|
||||
|
||||
|
||||
def check_store_version():
|
||||
def check_store_version(dir):
|
||||
versionfile = os.path.join(dir, store, '_osclib_version')
|
||||
try:
|
||||
v = open(os.path.join(store, '_osclib_version')).read().strip()
|
||||
v = open(versionfile).read().strip()
|
||||
except:
|
||||
v = ''
|
||||
|
||||
if v == '':
|
||||
print 'error: "%s" is not an osc working copy' % dir
|
||||
sys.exit(1)
|
||||
|
||||
if v != __version__:
|
||||
if v == '0.2':
|
||||
# 0.2 is fine, no migration needed
|
||||
f = open(versionfile, 'w')
|
||||
f.write(__version__ + '\n')
|
||||
f.close()
|
||||
return
|
||||
print
|
||||
print 'the osc metadata of your working copy'
|
||||
print ' %s' % os.getcwd()
|
||||
print 'the osc metadata of your working copy "%s"' % dir
|
||||
print 'has the wrong version (%s), should be %s' % (v, __version__)
|
||||
print 'please do a fresh checkout'
|
||||
print
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
|
||||
def meta_get_packagelist(prj):
|
||||
|
||||
u = makeurl(['source', prj, '_meta'])
|
||||
@ -159,6 +470,7 @@ def localmeta_addfile(filename):
|
||||
PrettyPrint(doc, stream=o)
|
||||
o.close()
|
||||
|
||||
|
||||
def localmeta_removefile(filename):
|
||||
|
||||
reader = Sax2.Reader()
|
||||
@ -195,6 +507,7 @@ def get_slash_source():
|
||||
r.sort()
|
||||
return r
|
||||
|
||||
|
||||
def show_project_meta(prj):
|
||||
f = urllib2.urlopen(makeurl(['source', prj, '_meta']))
|
||||
return f.readlines()
|
||||
@ -204,22 +517,49 @@ def show_package_meta(prj, pac):
|
||||
f = urllib2.urlopen(makeurl(['source', prj, pac, '_meta']))
|
||||
return f.readlines()
|
||||
|
||||
|
||||
def show_files_meta(prj, pac):
|
||||
f = urllib2.urlopen(makeurl(['source', prj, pac]))
|
||||
return f.readlines()
|
||||
|
||||
|
||||
def read_meta_from_spec(specfile):
|
||||
"""read Name, Summary and %description from spec file"""
|
||||
in_descr = False
|
||||
descr = []
|
||||
|
||||
if not os.path.isfile(specfile):
|
||||
print 'file \'%s\' is not a readable file' % specfile
|
||||
return None
|
||||
|
||||
for line in open(specfile, 'r'):
|
||||
if line.startswith('Name:'):
|
||||
name = line.split(':')[1].strip()
|
||||
if line.startswith('Summary:'):
|
||||
summary = line.split(':')[1].strip()
|
||||
if line.startswith('%description'):
|
||||
in_descr = True
|
||||
continue
|
||||
if in_descr and line.startswith('%'):
|
||||
break
|
||||
if in_descr:
|
||||
descr.append(line)
|
||||
|
||||
return name, summary, descr
|
||||
|
||||
|
||||
def get_user_id(user):
|
||||
u = makeurl(['person', user])
|
||||
f = urllib2.urlopen(u)
|
||||
return f.readlines()
|
||||
|
||||
|
||||
def get_source_file(prj, package, filename):
|
||||
def get_source_file(prj, package, filename, targetfilename=None):
|
||||
u = makeurl(['source', prj, package, filename])
|
||||
#print 'checking out', u
|
||||
f = urllib2.urlopen(u)
|
||||
|
||||
o = open(filename, 'w')
|
||||
o = open(targetfilename or filename, 'w')
|
||||
while 1:
|
||||
buf = f.read(BUFSIZE)
|
||||
if not buf: break
|
||||
@ -227,84 +567,22 @@ def get_source_file(prj, package, filename):
|
||||
o.close()
|
||||
|
||||
|
||||
|
||||
def dgst(file):
|
||||
|
||||
if not os.path.exists(file):
|
||||
return None
|
||||
#if not os.path.exists(file):
|
||||
#return None
|
||||
|
||||
import sha
|
||||
s = sha.new()
|
||||
import md5
|
||||
s = md5.new()
|
||||
f = open(file, 'r')
|
||||
while 1:
|
||||
buf = f.read(BUFSIZE)
|
||||
if not buf: break
|
||||
s.update(buf)
|
||||
return s.digest()
|
||||
return s.hexdigest()
|
||||
|
||||
|
||||
def get_file_status(prj, package, filename, filelist=None):
|
||||
"""
|
||||
status can be:
|
||||
|
||||
file storefile file present STATUS
|
||||
exists exists in _files
|
||||
|
||||
x x - 'D'
|
||||
x x x 'M', if digest differs, else ' '
|
||||
x - - '?'
|
||||
x - x 'A'
|
||||
- x x '!'
|
||||
- x - 'D' (when file in working copy is already deleted)
|
||||
- - x NOT DEFINED
|
||||
- - - NEVER REACHED
|
||||
|
||||
"""
|
||||
known_by_meta = False
|
||||
exists = False
|
||||
exists_in_store = False
|
||||
|
||||
if not filelist:
|
||||
filelist = localmeta_get_filelist()
|
||||
|
||||
if filename in filelist:
|
||||
known_by_meta = True
|
||||
|
||||
if os.path.exists(filename):
|
||||
exists = True
|
||||
|
||||
if os.path.exists(os.path.join(store, filename)):
|
||||
exists_in_store = True
|
||||
|
||||
if exists and exists_in_store and not known_by_meta:
|
||||
state = 'D'
|
||||
elif exists and exists_in_store and known_by_meta:
|
||||
if dgst(filename) != dgst(os.path.join(store, filename)):
|
||||
state = 'M'
|
||||
else:
|
||||
state = ' '
|
||||
elif exists and not exists_in_store and not known_by_meta:
|
||||
state = '?'
|
||||
elif exists and not exists_in_store and known_by_meta:
|
||||
state = 'A'
|
||||
elif not exists and exists_in_store and known_by_meta:
|
||||
state = '!'
|
||||
elif not exists and not exists_in_store and known_by_meta:
|
||||
print '%s: not exists and not exists_in_store and known_by_meta' % filename
|
||||
print 'this state is undefined!'
|
||||
sys.exit(1)
|
||||
elif not exists and exists_in_store and not known_by_meta:
|
||||
state = 'D'
|
||||
elif not exists and not exists_in_store and not known_by_meta:
|
||||
print '%s: not exists and not exists_in_store and not nown_by_meta' % filename
|
||||
print 'this code path should never be reached!'
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
return '%s %s' % (state, filename)
|
||||
|
||||
|
||||
def get_source_file_diff(prj, package, filename):
|
||||
def get_source_file_diff_upstream(prj, package, filename):
|
||||
url = makeurl(['source', prj, package, filename])
|
||||
f = urllib2.urlopen(url)
|
||||
|
||||
@ -319,6 +597,26 @@ def get_source_file_diff(prj, package, filename):
|
||||
return ''.join(d)
|
||||
|
||||
|
||||
def get_source_file_diff(dir, filename, rev):
|
||||
import difflib
|
||||
|
||||
file1 = os.path.join(dir, filename)
|
||||
file2 = os.path.join(dir, store, filename)
|
||||
|
||||
f1 = open(file1, 'r')
|
||||
f2 = open(file2, 'r')
|
||||
|
||||
d = difflib.unified_diff(\
|
||||
f1.readlines(), \
|
||||
f2.readlines(), \
|
||||
fromfile = '%s (revision %s)' % (filename, rev), \
|
||||
tofile = '%s (working copy)' % filename)
|
||||
|
||||
f1.close()
|
||||
f2.close()
|
||||
|
||||
return ''.join(d)
|
||||
|
||||
|
||||
def put_source_file(prj, package, filename):
|
||||
import othermethods
|
||||
@ -341,13 +639,17 @@ def del_source_file(prj, package, filename):
|
||||
|
||||
def make_dir(project, package):
|
||||
#print "creating directory '%s'" % project
|
||||
print 'A %s' % project
|
||||
print statfrmt('A', project)
|
||||
if not os.path.exists(project):
|
||||
os.mkdir(project)
|
||||
os.mkdir(os.path.join(project, store))
|
||||
|
||||
f = open(os.path.join(project, store, '_project'), 'w')
|
||||
f.write(project + '\n')
|
||||
f.close()
|
||||
|
||||
#print "creating directory '%s/%s'" % (project, package)
|
||||
print 'A %s/%s' % (project, package)
|
||||
print statfrmt('A', '%s/%s' % (project, package))
|
||||
if not os.path.exists(os.path.join(project, package)):
|
||||
os.mkdir(os.path.join(project, package))
|
||||
os.mkdir(os.path.join(project, package, store))
|
||||
@ -435,6 +737,15 @@ def get_log(prj, package, platform, arch):
|
||||
return f.readlines()
|
||||
|
||||
|
||||
def get_history(prj, package):
|
||||
# http://api.opensuse.org/rpm/Apache/factory/i586/apache2/history ?
|
||||
# http://api.opensuse.org/package/Apache/apache2/history ?
|
||||
u = makeurl(['package', prj, package, 'history'])
|
||||
print u
|
||||
f = urllib2.urlopen(u)
|
||||
return f.readlines()
|
||||
|
||||
|
||||
def store_read_project(dir):
|
||||
p = open(os.path.join(dir, store, '_project')).readlines()[0].strip()
|
||||
return p
|
||||
|
Loading…
Reference in New Issue
Block a user