def read_file_contents(filename): try: f = open(filename, 'r') return f.read() except OSError, e: log.printerr('Unable to read file \'%s\'' % filename) return None
def init(self): if self.itdb_exists(): print 'Already initialized issue database in branch \'%s\'.' % \ it.ITDB_BRANCH return # else, initialize the new .it database alongside the .git repo gitrepo = repo.find_git_repo() if not gitrepo: log.printerr('Not a valid Git repository.') else: parent, _ = os.path.split(gitrepo) ticket_dir = os.path.join(parent, it.TICKET_DIR) hold_file = os.path.join(ticket_dir, it.HOLD_FILE) misc.mkdirs(ticket_dir) misc.write_file_contents(hold_file, \ 'This is merely a placeholder file for git-it that prevents ' + \ 'this directory from\nbeing pruned by Git.') # Commit the new itdb to the repo curr_branch = git.current_branch() git.change_head_branch('git-it') git.command_lines('add', [hold_file]) msg = 'Initialized empty ticket database.' git.command_lines('commit', ['-m', msg, hold_file]) os.remove(hold_file) os.rmdir(ticket_dir) git.change_head_branch(curr_branch) abs_ticket_dir = os.path.join(repo.find_root(), it.TICKET_DIR) git.command_lines('reset', ['HEAD', abs_ticket_dir]) misc.rmdirs(abs_ticket_dir) print 'Initialized empty ticket database.'
def create_interactive(): # First, do some checks to error early fullname = os.popen('git config user.name').read().strip() email = os.popen('git config user.email').read().strip() if not fullname: log.printerr('author name not set. use "git config [--global] user.name \'John Smith\'" to set the fullname') return if not fullname: log.printerr('author name not set. use "git config [--global] user.name \'John Smith\'" to set the fullname') return i = Ticket() i.title = ask_for_pattern('Title: ', not_empty) type_dict = { 'i': 'issue', 't': 'task', 'f': 'feature', 'b': 'bug' } type_string = ask_for_pattern('Type [(b)ug, (f)eature, (i)ssue, (t)ask]: ', lambda x: not_empty(x) and x.strip() in 'bfit') i.type = type_dict[type_string] prio_string = ask_for_pattern('Priority [(1)high, (2)medium, (3)low]: ', lambda x: x.strip() in '123') if prio_string == '': i.prio = 2 else: i.prio = int(prio_string) i.release = ask_for_pattern('Release: ').strip() if i.release == '': i.release = 'uncategorized' #i.body = ask_for_multiline_pattern('Describe the ticket:\n') i.status = 'open' i.date = datetime.datetime.now() i.issuer = '%s <%s>' % (fullname, email) return i
def edit(self, sha): i, rel, fullsha, match = self.get_ticket(sha) sha7 = misc.chop(fullsha, 7) # Save the contents of this ticket to a file, so it can be edited i.save(it.EDIT_TMP_FILE) timestamp1 = os.path.getmtime(it.EDIT_TMP_FILE) success = os.system('vim "%s"' % it.EDIT_TMP_FILE) == 0 timestamp2 = os.path.getmtime(it.EDIT_TMP_FILE) if success: if timestamp1 < timestamp2: i = ticket.create_from_file(it.EDIT_TMP_FILE, fullsha, rel) # Now, when the edit has succesfully taken place, switch branches, commit, # and switch back curr_branch = git.current_branch() git.change_head_branch('git-it') msg = 'ticket \'%s\' edited' % sha7 i.save() git.command_lines('commit', ['-m', msg, i.filename()]) git.change_head_branch(curr_branch) abs_ticket_dir = os.path.join(repo.find_root(), it.TICKET_DIR) git.command_lines('reset', ['HEAD', abs_ticket_dir]) misc.rmdirs(abs_ticket_dir) print 'ticket \'%s\' edited succesfully' % sha7 print '' self.list() else: print 'editing of ticket \'%s\' cancelled' % sha7 else: log.printerr('editing of ticket \'%s\' failed' % sha7) # Remove the temporary file os.remove(it.EDIT_TMP_FILE)
def read_file_contents(filename): try: f = open(filename, 'r') return f.read() except OSError as e: log.printerr("Unable to read file '%s'" % filename) return None
def require_itdb(self): """ This method asserts that the itdb is initialized, or errors if not. """ if not self.itdb_exists(): log.printerr('itdb not yet initialized. run \'it init\' first to ' + \ 'create a new itdb.') sys.exit(1)
def require_itdb(self): """ This method asserts that the itdb is initialized, or errors if not. """ if not self.itdb_exists(): log.printerr("The itdb is not yet initialized.\n" \ + "Run 'it init' first to create a new itdb.") sys.exit(1)
def create_interactive(git_cfg): # First, do some checks to error early try: fullname = git_cfg.get('user', 'name') except Exception as e: log.printerr(""" Author name not set. Use git config [--global] user.name "John Smith" to set the fullname. """) return try: email = git_cfg.get('user', 'email') except Exception as e: log.printerr(""" Email address not set. Use git config [--global] user.email "*****@*****.**" to set the email address. """) return i = Ticket() i.title = ask_for_pattern('Title: ', not_empty) type_dict = { 'i': 'issue', 't': 'task', 'f': 'feature', 'b': 'bug' } type_string = ask_for_pattern( 'Type [(b)ug, (f)eature, (i)ssue, (t)ask]: ', lambda x: not_empty(x) and x.strip() in 'bfit', default = 'i' ) i.type = type_dict[type_string] prio_string = ask_for_pattern( 'Priority [(1)high, (2)medium, (3)low]: ', lambda x: x.strip() in '123', default='2' ) i.prio = int(prio_string) i.weight = int(ask_for_pattern( 'Weight [1-27] (1=small, 3=minor, 9=major, 27=super): ', lambda x: is_int(x) and 1 <= int(x) <= 27, default='3' )) i.release = ask_for_pattern('Release: ', default=it.UNCATEGORIZED) #FIXME: add ticket description as body #i.body = ask_for_multiline_pattern('Describe the ticket:\n') i.status = 'open' i.date = datetime.datetime.now() i.issuer = '%s <%s>' % (fullname, email) return i
def init(self): """ Initializes a ITDB if it does not exists. Otherwise search for a remote ITDB an branch from it. """ # check wheter it is already initialzed if it.ITDB_BRANCH in [b.name for b in self.repo.branches]: # check for the hold file abs_hold_file = os.path.join(it.TICKET_DIR, it.HOLD_FILE) if abs_hold_file in [ x.path for x in self.itdb_tree.list_traverse(depth=1) ]: print("Issue database already initialized.") return # search for a ITDB on a remote branch for r in self.repo.remotes: for ref in r.refs: if ref.name.endswith(it.ITDB_BRANCH): print("Initialize ticket database from %s." % ref.name) self.repo.create_head('refs/heads/%s' % it.ITDB_BRANCH, ref.name) return # else, initialize the new .it database alongside the .git repo gitrepo = self.repo.git_dir if not gitrepo: log.printerr("%s: Not a valid Git repository." % gitrepo) return #FIXME: use working_dir directly instead of assuming 'git_repo' # is work_dir/.git parent, _ = os.path.split(gitrepo) ticket_dir = os.path.join(parent, it.TICKET_DIR) hold_file = os.path.join(ticket_dir, it.HOLD_FILE) curr_branch = self.repo.active_branch.name msg = "Initialized empty ticket database." abs_ticket_dir = os.path.join(self.repo.working_dir, it.TICKET_DIR) try: misc.mkdirs(ticket_dir) misc.write_file_contents(hold_file, \ 'This is merely a placeholder file for git-it that prevents ' + \ 'this directory from\nbeing pruned by Git.') # Commit the new itdb to the repo self.repo.git.symbolic_ref( ['HEAD', 'refs/heads/' + it.ITDB_BRANCH]) self.repo.git.add([hold_file]) self.repo.git.commit(['-m', msg, hold_file]) print("Initialized empty ticket database.") except Exception: log.printerr("Error initialising ticket database.") finally: os.remove(hold_file) os.rmdir(ticket_dir) self.repo.git.symbolic_ref(['HEAD', 'refs/heads/' + curr_branch]) self.repo.git.reset(['HEAD', '--', abs_ticket_dir]) misc.rmdirs(abs_ticket_dir)
def write_file_contents(filename, contents): try: f = open(filename, 'w') f.write(contents) f.close() return True except OSError as e: log.printerr("Unable to write file '%s'" % filename) return False
def write_file_contents(filename, contents): try: f = open(filename, 'w') f.write(contents) f.close() return True except OSError, e: log.printerr('Unable to write file \'%s\'' % filename) return False
def create_interactive(git_cfg): # First, do some checks to error early try: fullname = git_cfg.get('user', 'name') except Exception as e: log.printerr(""" Author name not set. Use git config [--global] user.name "John Smith" to set the fullname. """) return try: email = git_cfg.get('user', 'email') except Exception as e: log.printerr(""" Email address not set. Use git config [--global] user.email "*****@*****.**" to set the email address. """) return i = Ticket() i.title = ask_for_pattern('Title: ', not_empty) type_dict = {'i': 'issue', 't': 'task', 'f': 'feature', 'b': 'bug'} type_string = ask_for_pattern( 'Type [(b)ug, (f)eature, (i)ssue, (t)ask]: ', lambda x: not_empty(x) and x.strip() in 'bfit', default='i') i.type = type_dict[type_string] prio_string = ask_for_pattern('Priority [(1)high, (2)medium, (3)low]: ', lambda x: x.strip() in '123', default='2') i.prio = int(prio_string) i.weight = int( ask_for_pattern( 'Weight [1-27] (1=small, 3=minor, 9=major, 27=super): ', lambda x: is_int(x) and 1 <= int(x) <= 27, default='3')) i.release = ask_for_pattern('Release: ', default=it.UNCATEGORIZED) #FIXME: add ticket description as body #i.body = ask_for_multiline_pattern('Describe the ticket:\n') i.status = 'open' i.date = datetime.datetime.now() i.issuer = '%s <%s>' % (fullname, email) return i
def create_from_file(filename, overwrite_id = None, overwrite_release = None): if (overwrite_id and not overwrite_release) or (overwrite_release and not overwrite_id): log.printerr('program error: specify both an alternative ID and alternative release or neither') return if overwrite_id: id = overwrite_id else: dir, id = os.path.split(filename) if overwrite_release: release = overwrite_release else: _, release = os.path.split(dir) content = misc.read_file_contents(filename) if not content: return None else: return create_from_string(content, id, release)
def finish_ticket(self, sha, new_status): i, _, fullsha, match = self.get_ticket(sha) sha7 = misc.chop(fullsha, 7) if i.status not in ['open', 'test']: log.printerr('ticket \'%s\' already %s' % (sha7, i.status)) sys.exit(1) # Now, when the edit has succesfully taken place, switch branches, commit, # and switch back curr_branch = git.current_branch() git.change_head_branch('git-it') i.status = new_status msg = '%s ticket \'%s\'' % (i.status, sha7) i.save() git.command_lines('commit', ['-m', msg, match], from_root=True) git.change_head_branch(curr_branch) abs_ticket_dir = os.path.join(repo.find_root(), it.TICKET_DIR) git.command_lines('reset', ['HEAD', abs_ticket_dir]) misc.rmdirs(abs_ticket_dir) print 'ticket \'%s\' %s' % (sha7, new_status)
def reopen_ticket(self, sha): i, _, fullsha, match = self.get_ticket(sha) sha7 = misc.chop(sha, 7) if i.status == 'open': log.printerr('ticket \'%s\' already open' % sha7) sys.exit(1) # Now, when the edit has succesfully taken place, switch branches, commit, # and switch back curr_branch = git.current_branch() git.change_head_branch('git-it') msg = 'ticket \'%s\' reopened' % sha7 i.status = 'open' i.save() git.command_lines('commit', ['-m', msg, match], from_root=True) git.change_head_branch(curr_branch) abs_ticket_dir = os.path.join(repo.find_root(), it.TICKET_DIR) git.command_lines('reset', ['HEAD', abs_ticket_dir]) misc.rmdirs(abs_ticket_dir) print 'ticket \'%s\' reopened' % sha7
def reject_ticket(self, sha, message): i, _, fullsha, match = self.get_ticket(sha) sha7 = misc.chop(sha, 7) if 'rejected' == i.status: log.printerr('ticket \'%s\' already rejected' % sha7) sys.exit(1) if message is None or message == '': message = ticket.ask_for_pattern('Reason for rejection: ', ticket.not_empty) curr_branch = git.current_branch() git.change_head_branch('git-it') i.status = 'rejected' i.body = 'REJECTED: %s\n\n%s' % (message, i.body) msg = '%s ticket \'%s\'' % (i.status, sha7) i.save() git.command_lines('commit', ['-m', msg, match], from_root=True) git.change_head_branch(curr_branch) abs_ticket_dir = os.path.join(repo.find_root(), it.TICKET_DIR) git.command_lines('reset', ['HEAD', abs_ticket_dir]) misc.rmdirs(abs_ticket_dir) print 'ticket \'%s\' rejected' % sha7
def reject_ticket(self, sha, message): i, _, fullsha, match = self.get_ticket(sha) sha7 = misc.chop(sha, 7) if 'rejected' == i.status: log.printerr('ticket \'%s\' already rejected' % sha7) sys.exit(1) if message is None or message == '': message = ticket.ask_for_pattern( 'Reason for rejection: ', ticket.not_empty) curr_branch = git.current_branch() git.change_head_branch('git-it') i.status = 'rejected' i.body = 'REJECTED: %s\n\n%s' % (message, i.body) msg = '%s ticket \'%s\'' % (i.status, sha7) i.save() git.command_lines('commit', ['-m', msg, match], from_root=True) git.change_head_branch(curr_branch) abs_ticket_dir = os.path.join(repo.find_root(), it.TICKET_DIR) git.command_lines('reset', ['HEAD', abs_ticket_dir]) misc.rmdirs(abs_ticket_dir) print 'ticket \'%s\' rejected' % sha7
def rmdirs(dir): if not os.path.exists(dir): return True if not os.path.isdir(dir): log.printerr("'%s': not a directory" % dir) return False # First, remove all children of dir ls = dircache.listdir(dir) ok = True for file in ls: full = os.path.join(dir, file) if os.path.isdir(full): if not rmdirs(full): ok = False else: try: os.remove(full) except OSError as e: log.printerr("Could not remove file '%s'" % full) ok = False # Finally, remove the empty dir itself if ok: try: os.rmdir(dir) except OSError as e: log.printerr("Could not remove directory '%s'" % dir) ok = False return ok
def rmdirs(dir): if not os.path.exists(dir): return True if not os.path.isdir(dir): log.printerr('\'%s\': not a directory' % dir) return False # First, remove all children of dir ls = dircache.listdir(dir) ok = True for file in ls: full = os.path.join(dir, file) if os.path.isdir(full): if not rmdirs(full): ok = False else: try: os.remove(full) except OSError, e: log.printerr('could not remove file \'%s\'' % full) ok = False
def match_or_error(self, sha): self.require_itdb() files = git.full_tree(it.ITDB_BRANCH + ':' + it.TICKET_DIR) matches = [] for _, _, _, path in files: _, file = os.path.split(path) if file.startswith(sha): matches.append(path) if len(matches) == 0: log.printerr('no such ticket') sys.exit(1) elif len(matches) > 1: log.printerr('ambiguous match critiria. the following tickets match:') for match in matches: _, id = os.path.split(match) log.printerr(id) sys.exit(1) else: return os.path.join(it.TICKET_DIR, matches[0])
def mv(self, sha, to_rel): self.require_itdb() i, rel, fullsha, src_path = self.get_ticket(sha) sha7 = misc.chop(fullsha, 7) src_dir = os.path.join(repo.find_root(), it.TICKET_DIR, rel) target_dir = os.path.join(repo.find_root(), it.TICKET_DIR, to_rel) target_path = os.path.join(target_dir, fullsha) if src_dir == target_dir: log.printerr('ticket \'%s\' already in \'%s\'' % (sha7, to_rel)) return # Create the target dir, if neccessary if not os.path.isdir(target_dir): misc.mkdirs(target_dir) # Try to move the file into it try: # Commit the new itdb to the repo curr_branch = git.current_branch() git.change_head_branch('git-it') i.save(target_path) if os.path.isfile(src_path): os.remove(src_path) msg = 'moved ticket \'%s\' (%s --> %s)' % (sha7, rel, to_rel) git.command_lines('add', [target_path]) git.command_lines('commit', ['-m', msg, src_path, target_path]) git.change_head_branch(curr_branch) abs_ticket_dir = os.path.join(repo.find_root(), it.TICKET_DIR) git.command_lines('reset', ['HEAD', abs_ticket_dir]) misc.rmdirs(abs_ticket_dir) print 'ticket \'%s\' moved to release \'%s\'' % (sha7, to_rel) print '' self.list() except OSError, e: log.printerr('could not move ticket \'%s\' to \'%s\':' % (sha7, to_rel)) log.printerr(e)
def match_or_error(self, sha): self.require_itdb() files = git.full_tree(it.ITDB_BRANCH + ':' + it.TICKET_DIR) matches = [] for _, _, _, path in files: _, file = os.path.split(path) if file.startswith(sha): matches.append(path) if len(matches) == 0: log.printerr('no such ticket') sys.exit(1) elif len(matches) > 1: log.printerr( 'ambiguous match critiria. the following tickets match:') for match in matches: _, id = os.path.split(match) log.printerr(id) sys.exit(1) else: return os.path.join(it.TICKET_DIR, matches[0])
if os.path.isdir(full): if not rmdirs(full): ok = False else: try: os.remove(full) except OSError, e: log.printerr('could not remove file \'%s\'' % full) ok = False # Finally, remove the empty dir itself if ok: try: os.rmdir(dir) except OSError, e: log.printerr('could not remove directory \'%s\'' % dir) ok = False return ok def read_file_contents(filename): try: f = open(filename, 'r') return f.read() except OSError, e: log.printerr('Unable to read file \'%s\'' % filename) return None def write_file_contents(filename, contents): try:
# Now, when the edit has succesfully taken place, switch branches, commit, # and switch back curr_branch = git.current_branch() git.change_head_branch('git-it') msg = 'ticket \'%s\' edited' % sha7 i.save() git.command_lines('commit', ['-m', msg, i.filename()]) git.change_head_branch(curr_branch) abs_ticket_dir = os.path.join(repo.find_root(), it.TICKET_DIR) git.command_lines('reset', ['HEAD', abs_ticket_dir]) misc.rmdirs(abs_ticket_dir) print 'ticket \'%s\' edited succesfully' % sha7 else: print 'editing of ticket \'%s\' cancelled' % sha7 else: log.printerr('editing of ticket \'%s\' failed' % sha7) # Remove the temporary file os.remove(it.EDIT_TMP_FILE) def mv(self, sha, to_rel): self.require_itdb() i, rel, fullsha, src_path = self.get_ticket(sha) sha7 = misc.chop(fullsha, 7) src_dir = os.path.join(repo.find_root(), it.TICKET_DIR, rel) target_dir = os.path.join(repo.find_root(), it.TICKET_DIR, to_rel) target_path = os.path.join(target_dir, fullsha) if src_dir == target_dir: log.printerr('ticket \'%s\' already in \'%s\'' % (sha7, to_rel)) return
if os.path.isdir(full): if not rmdirs(full): ok = False else: try: os.remove(full) except OSError, e: log.printerr('could not remove file \'%s\'' % full) ok = False # Finally, remove the empty dir itself if ok: try: os.rmdir(dir) except OSError, e: log.printerr('could not remove directory \'%s\'' % dir) ok = False return ok def read_file_contents(filename): try: f = open(filename, 'r') return f.read() except OSError, e: log.printerr('Unable to read file \'%s\'' % filename) return None def write_file_contents(filename, contents): try: f = open(filename, 'w') f.write(contents)