def fetch_data_authors(ctx): # data is a dictionnary mapping an author name to the data for # this author user = ctx.user() email = util.email(user) name = person(user) yield name
def fixup_user(user,authors): user=user.strip("\"") if "<<>>" in user: user = user.replace("<<>>", "") if "<<" in user: user = user.replace("<<", "<") if authors!=None: # if we have an authors table, try to get mapping # by defaulting to the current value of 'user' user=authors.get(user,user) name,mail,m='','',user_re.match(user) if m==None: # if we don't have 'Name <mail>' syntax, extract name # and mail from hg helpers. this seems to work pretty well. # if email doesn't contain @, replace it with devnull@localhost name=templatefilters.person(user) mail='<%s>' % util.email(user) if '@' not in mail: mail = '<devnull@localhost>' else: # if we have 'Name <mail>' syntax, everything is fine :) name,mail=m.group(1),m.group(2) # remove any silly quoting from username m2=user_clean_re.match(name) if m2!=None: name=m2.group(1) return '%s %s' % (name,mail)
def fetch_data_authors(ctx, stat_maker, options): localstat = stat_maker(ctx, options) # data is dictionnary mapping an author name to the data for # this author email = util.email(ctx.user()) email = options.amap.get(email, email) # alias remap yield email, localstat
def update(self, bugid, newstate, ctx): '''update bugzilla bug with reference to changeset.''' def webroot(root): '''strip leading prefix of repo root and turn into url-safe path.''' count = int(self.ui.config('bugzilla', 'strip', 0)) root = util.pconvert(root) while count > 0: c = root.find('/') if c == -1: break root = root[c + 1:] count -= 1 return root mapfile = self.ui.config('bugzilla', 'style') tmpl = self.ui.config('bugzilla', 'template') t = cmdutil.changeset_templater(self.ui, self.repo, False, None, mapfile, False) if not mapfile and not tmpl: tmpl = _('changeset {node|short} in repo {root} refers ' 'to bug {bug}.\ndetails:\n\t{desc|tabindent}') if tmpl: tmpl = templater.parsestring(tmpl, quoted=False) t.use_template(tmpl) self.ui.pushbuffer() t.show(ctx, changes=ctx.changeset(), bug=str(bugid), hgweb=self.ui.config('web', 'baseurl'), root=self.repo.root, webroot=webroot(self.repo.root)) data = self.ui.popbuffer() self.updatebug(bugid, newstate, data, util.email(ctx.user()))
def check(self, ctx): if self.is_uplift: return True # Ignore merge changesets if len(ctx.parents()) > 1: return True # Ignore backouts if is_backout(ctx.description()): return True # Ignore changes that don't touch sync-messages.ini ipc_files = [f for f in ctx.files() if f == 'ipc/ipdl/sync-messages.ini'] if not ipc_files: return True # Allow patches authored by peers if self._is_peer_email(util.email(ctx.user())): return True # Allow if reviewed by any peer requal = list(parse_requal_reviewers(ctx.description())) if any(self._is_peer_nick(nick) for nick in requal): return True # Reject print_banner(self.ui, 'error', MISSING_REVIEW % short(ctx.node())) return False
def update(self, bugid, ctx): '''update bugzilla bug with reference to changeset.''' def webroot(root): '''strip leading prefix of repo root and turn into url-safe path.''' count = int(self.ui.config('bugzilla', 'strip', 0)) root = util.pconvert(root) while count > 0: c = root.find('/') if c == -1: break root = root[c+1:] count -= 1 return root mapfile = self.ui.config('bugzilla', 'style') tmpl = self.ui.config('bugzilla', 'template') t = cmdutil.changeset_templater(self.ui, self.repo, False, None, mapfile, False) if not mapfile and not tmpl: tmpl = _('changeset {node|short} in repo {root} refers ' 'to bug {bug}.\ndetails:\n\t{desc|tabindent}') if tmpl: tmpl = templater.parsestring(tmpl, quoted=False) t.use_template(tmpl) self.ui.pushbuffer() t.show(ctx, changes=ctx.changeset(), bug=str(bugid), hgweb=self.ui.config('web', 'baseurl'), root=self.repo.root, webroot=webroot(self.repo.root)) data = self.ui.popbuffer() self.add_comment(bugid, data, util.email(ctx.user()))
def send(self, ctx, count, data): '''send message.''' p = email.Parser.Parser() msg = p.parsestr(data) # store sender and subject sender, subject = msg['From'], msg['Subject'] del msg['From'], msg['Subject'] # store remaining headers headers = msg.items() # create fresh mime message from msg body text = msg.get_payload() # for notification prefer readability over data precision msg = mail.mimeencode(self.ui, text, self.charsets, self.test) # reinstate custom headers for k, v in headers: msg[k] = v msg['Date'] = util.datestr(format="%a, %d %b %Y %H:%M:%S %1%2") # try to make subject line exist and be useful if not subject: if count > 1: subject = _('%s: %d new changesets') % (self.root, count) else: s = ctx.description().lstrip().split('\n', 1)[0].rstrip() subject = '%s: %s' % (self.root, s) maxsubject = int(self.ui.config('notify', 'maxsubject', 67)) if maxsubject and len(subject) > maxsubject: subject = subject[:maxsubject-3] + '...' msg['Subject'] = mail.headencode(self.ui, subject, self.charsets, self.test) # try to make message have proper sender if not sender: sender = self.ui.config('email', 'from') or self.ui.username() if '@' not in sender or '@localhost' in sender: sender = self.fixmail(sender) msg['From'] = mail.addressencode(self.ui, sender, self.charsets, self.test) msg['X-Hg-Notification'] = 'changeset %s' % ctx if not msg['Message-Id']: msg['Message-Id'] = ('<hg.%s.%s.%s@%s>' % (ctx, int(time.time()), hash(self.repo.root), socket.getfqdn())) msg['To'] = ', '.join(self.subs) msgtext = msg.as_string(0) if self.test: self.ui.write(msgtext) if not msgtext.endswith('\n'): self.ui.write('\n') else: self.ui.status(_('notify: sending %d subscribers %d changes\n') % (len(self.subs), count)) mail.sendmail(self.ui, util.email(msg['From']), self.subs, msgtext)
def send(self, ctx, count, data): '''send message.''' p = email.Parser.Parser() msg = p.parsestr(data) # store sender and subject sender, subject = msg['From'], msg['Subject'] del msg['From'], msg['Subject'] # store remaining headers headers = msg.items() # create fresh mime message from msg body text = msg.get_payload() # for notification prefer readability over data precision msg = mail.mimeencode(self.ui, text, self.charsets, self.test) # reinstate custom headers for k, v in headers: msg[k] = v msg['Date'] = util.datestr(format="%a, %d %b %Y %H:%M:%S %1%2") # try to make subject line exist and be useful if not subject: if count > 1: subject = _('%s: %d new changesets') % (self.root, count) else: s = ctx.description().lstrip().split('\n', 1)[0].rstrip() subject = '%s: %s' % (self.root, s) maxsubject = int(self.ui.config('notify', 'maxsubject', 67)) if maxsubject and len(subject) > maxsubject: subject = subject[:maxsubject - 3] + '...' msg['Subject'] = mail.headencode(self.ui, subject, self.charsets, self.test) # try to make message have proper sender if not sender: sender = self.ui.config('email', 'from') or self.ui.username() if '@' not in sender or '@localhost' in sender: sender = self.fixmail(sender) msg['From'] = mail.addressencode(self.ui, sender, self.charsets, self.test) msg['X-Hg-Notification'] = 'changeset %s' % ctx if not msg['Message-Id']: msg['Message-Id'] = ('<hg.%s.%s.%s@%s>' % (ctx, int( time.time()), hash(self.repo.root), socket.getfqdn())) msg['To'] = ', '.join(self.subs) msgtext = msg.as_string(0) if self.test: self.ui.write(msgtext) if not msgtext.endswith('\n'): self.ui.write('\n') else: self.ui.status( _('notify: sending %d subscribers %d changes\n') % (len(self.subs), count)) mail.sendmail(self.ui, util.email(msg['From']), self.subs, msgtext)
def send(self, node, count, data): '''send message.''' p = email.Parser.Parser() msg = p.parsestr(data) def fix_subject(): '''try to make subject line exist and be useful.''' subject = msg['Subject'] if not subject: if count > 1: subject = _('%s: %d new changesets') % (self.root, count) else: changes = self.repo.changelog.read(node) s = changes[4].lstrip().split('\n', 1)[0].rstrip() subject = '%s: %s' % (self.root, s) maxsubject = int(self.ui.config('notify', 'maxsubject', 67)) if maxsubject and len(subject) > maxsubject: subject = subject[:maxsubject-3] + '...' del msg['Subject'] msg['Subject'] = subject def fix_sender(): '''try to make message have proper sender.''' sender = msg['From'] if not sender: sender = self.ui.config('email', 'from') or self.ui.username() if '@' not in sender or '@localhost' in sender: sender = self.fixmail(sender) del msg['From'] msg['From'] = sender msg['Date'] = util.datestr(format="%a, %d %b %Y %H:%M:%S %1%2") fix_subject() fix_sender() msg['X-Hg-Notification'] = 'changeset ' + short(node) if not msg['Message-Id']: msg['Message-Id'] = ('<hg.%s.%s.%s@%s>' % (short(node), int(time.time()), hash(self.repo.root), socket.getfqdn())) msg['To'] = ', '.join(self.subs) msgtext = msg.as_string(0) if self.ui.configbool('notify', 'test', True): self.ui.write(msgtext) if not msgtext.endswith('\n'): self.ui.write('\n') else: self.ui.status(_('notify: sending %d subscribers %d changes\n') % (len(self.subs), count)) mail.sendmail(self.ui, util.email(msg['From']), self.subs, msgtext)
def send(self, node, count, data): '''send message.''' p = email.Parser.Parser() msg = p.parsestr(data) def fix_subject(): '''try to make subject line exist and be useful.''' subject = msg['Subject'] if not subject: if count > 1: subject = _('%s: %d new changesets') % (self.root, count) else: changes = self.repo.changelog.read(node) s = changes[4].lstrip().split('\n', 1)[0].rstrip() subject = '%s: %s' % (self.root, s) maxsubject = int(self.ui.config('notify', 'maxsubject', 67)) if maxsubject and len(subject) > maxsubject: subject = subject[:maxsubject - 3] + '...' del msg['Subject'] msg['Subject'] = subject def fix_sender(): '''try to make message have proper sender.''' sender = msg['From'] if not sender: sender = self.ui.config('email', 'from') or self.ui.username() if '@' not in sender or '@localhost' in sender: sender = self.fixmail(sender) del msg['From'] msg['From'] = sender msg['Date'] = util.datestr(format="%a, %d %b %Y %H:%M:%S %1%2") fix_subject() fix_sender() msg['X-Hg-Notification'] = 'changeset ' + short(node) if not msg['Message-Id']: msg['Message-Id'] = ('<hg.%s.%s.%s@%s>' % (short(node), int(time.time()), hash(self.repo.root), socket.getfqdn())) msg['To'] = ', '.join(self.subs) msgtext = msg.as_string(0) if self.ui.configbool('notify', 'test', True): self.ui.write(msgtext) if not msgtext.endswith('\n'): self.ui.write('\n') else: self.ui.status( _('notify: sending %d subscribers %d changes\n') % (len(self.subs), count)) mail.sendmail(self.ui, util.email(msg['From']), self.subs, msgtext)
def fixmail(self, addr): '''try to clean up email addresses.''' addr = util.email(addr.strip()) if self.domain: a = addr.find('@localhost') if a != -1: addr = addr[:a] if '@' not in addr: return addr + '@' + self.domain return addr
def __gather(ui, repo, node1, node2): def dirtywork(f, mmap1, mmap2): lines = 0 to = mmap1 and repo.file(f).read(mmap1[f]) or None tn = mmap2 and repo.file(f).read(mmap2[f]) or None diff = mdiff.unidiff(to, "", tn, "", f, f).split("\n") for line in diff: if not line: continue # skip EOF if line.startswith(" "): continue # context line if line.startswith("--- ") or line.startswith("+++ "): continue # begining of diff if line.startswith("@@ "): continue # info line # changed lines lines += 1 return lines ## lines = 0 changes = repo.status(node1, node2, None, util.always)[:5] modified, added, removed, deleted, unknown = changes who = repo.changelog.read(node2)[1] who = util.email(who) # get the email of the person mmap1 = repo.manifest.read(repo.changelog.read(node1)[0]) mmap2 = repo.manifest.read(repo.changelog.read(node2)[0]) for f in modified: lines += dirtywork(f, mmap1, mmap2) for f in added: lines += dirtywork(f, None, mmap2) for f in removed: lines += dirtywork(f, mmap1, None) for f in deleted: lines += dirtywork(f, mmap1, mmap2) for f in unknown: lines += dirtywork(f, mmap1, mmap2) return (who, lines)
def sendemail(self, address, data): p = email.Parser.Parser() msg = p.parsestr(data) msg['Date'] = util.datestr(format="%a, %d %b %Y %H:%M:%S %1%2") msg['To'] = address msg['From'] = self.emailfrom msg['Subject'] = 'DeliverXML' msg['Content-type'] = 'text/xml' msgtext = msg.as_string() self.ui.status(_('hgcia: sending update to %s\n') % address) mail.sendmail(self.ui, util.email(self.emailfrom), [address], msgtext)
def sendemail(self, address, data): p = email.Parser.Parser() msg = p.parsestr(data) msg["Date"] = util.datestr(format="%a, %d %b %Y %H:%M:%S %1%2") msg["To"] = address msg["From"] = self.emailfrom msg["Subject"] = "DeliverXML" msg["Content-type"] = "text/xml" msgtext = msg.as_string() self.ui.status(_("hgcia: sending update to %s\n") % address) mail.sendmail(self.ui, util.email(self.emailfrom), [address], msgtext)
def collect_data(cl,options): if options.split=='none': fetch_data = fetch_data_any else: fetch_data = globals()['fetch_data_' + options.split] stat_maker = nostat if options.uselines: stat_maker = diffstat # starting with mercurial 1.1, this could be simplified by iterating in cl directly data = {} for i in xrange(options.length): node = cl.read(cl.node(i)) # Check whether the number of changed files == 0 if options.skipmerges and len(node[3]) == 0: continue # Skip merges # find out date and filter date = datetime.fromtimestamp(node[2][0]) if options.datemin!=None and date<options.datemin: continue if options.datemax!=None and date>options.datemax: continue # find out who this is who = node[1] email = util.email(who) if email in options.exclude: continue ctx = options.repo.changectx(i) for k, v in fetch_data(ctx, stat_maker, options): if not data.has_key(k): data[k] = {} data[k][date] = sum(v) # post_collect_data titlemap = {} if options.split=='authors': for who in data.keys(): email = util.email(who) titlemap[email] = person(who) options.titlemap = titlemap return data
def check(self, ctx): if self.is_uplift: return True # Ignore merge changesets if len(ctx.parents()) > 1: return True # Ignore backouts if is_backout(ctx.description()): return True # Ignore changes that don't touch .webidl files webidl_files = [f for f in ctx.files() if f.endswith('.webidl')] if not webidl_files: return True # Allow patches authored by peers if is_peer_email(util.email(ctx.user())): return True # Categorise files file_counts = collections.Counter() review_required_files = [] for f in webidl_files: file_counts['total'] += 1 if f.startswith(CHROME_WEBIDL_ROOT): file_counts['chrome'] += 1 elif f.startswith(SERVO_ROOT): file_counts['servo'] += 1 else: review_required_files.append(f) # Allow chrome-only and servo-only changes if file_counts['chrome'] + file_counts['servo'] == file_counts[ 'total']: if file_counts['chrome']: print_notice(self.ui, CHROME_ONLY) if file_counts['servo']: print_notice(self.ui, SERVO_ONLY) return True # Allow if reviewed by any peer requal = list(parse_requal_reviewers(ctx.description())) if any(is_peer_nick(nick) for nick in requal): return True # Reject print_banner( self.ui, 'error', MISSING_REVIEW % (short(ctx.node()), '\n'.join(review_required_files))) return False
def hook(ui, repo, hooktype, node=None, **kwargs): '''add comment to bugzilla for each changeset that refers to a bugzilla bug id. only add a comment once per bug, so same change seen multiple times does not fill bug with duplicate data.''' if node is None: raise util.Abort(_('hook type %s does not pass a changeset id') % hooktype) try: bz = bugzilla(ui, repo) ctx = repo[node] bugs = bz.find_bugs(ctx) if bugs: for bug in bugs: bz.update(bug, bugs[bug], ctx) bz.notify(bugs, util.email(ctx.user())) except Exception, e: raise util.Abort(_('Bugzilla error: %s') % e)
def collect_data(cl,options): data = {} namemap = {} if not options.split: data["Overall activity"] = {} localactivity = 1 # starting with mercurial 1.1, this could be simplified by iterating in cl directly for i in xrange(options.length): node = cl.read(cl.node(i)) # Check whether the number of changed files == 0 if options.skipmerges and len(node[3]) == 0: continue # Skip merges # find out date and filter date = datetime.datetime.fromtimestamp(node[2][0]) if options.datemin!=None and date<options.datemin: continue if options.datemax!=None and date>options.datemax: continue # find out who this is who = node[1] email = util.email(who) namemap[email] = person(who) if email in options.exclude: continue if options.uselines: localactivity = changedlines(options.repo, i) if options.split: # data is dictionnary mapping an author name to the data for # this author email = options.amap.get(email, email) # alias remap if not data.has_key(email): data[email] = {} data[email][date] = localactivity else: # data only contains one entry for the global graphic data["Overall activity"][date] = localactivity options.namemap = namemap return data
def fixup_user(user, authors): user = user.strip("\"") if authors != None: # if we have an authors table, try to get mapping # by defaulting to the current value of 'user' user = authors.get(user, user) name, mail, m = '', '', user_re.match(user) if m == None: # if we don't have 'Name <mail>' syntax, extract name # and mail from hg helpers. this seems to work pretty well. # if email doesn't contain @, replace it with devnull@localhost name = templatefilters.person(user) mail = '<%s>' % util.email(user) if '@' not in mail: mail = '<devnull@localhost>' else: # if we have 'Name <mail>' syntax, everything is fine :) name, mail = m.group(1), m.group(2) # remove any silly quoting from username m2 = user_clean_re.match(name) if m2 != None: name = m2.group(1) return '%s %s' % (name, mail)
def get_user(self): node_user = self.repository.repo[self.get_node()].user() email = util.email(node_user) name = person(node_user) return name, email
def autopatch(ui, repo, hooktype, node, **kwargs): cc = repo.changectx(node) fromfile = ui.config("autopatch", "sourcefile") tofile = ui.config("autopatch", "patchfile") if not fromfile or not tofile: ui.warn( "Repomaster should have configured sourcefile and patchfile in the autopatch section!" ) return True if fromfile not in cc.files(): ui.status("%s not affected -> no need to patch.\n" % fromfile) return False file_after = cc.filectx(fromfile).data().splitlines() file_before = cc.parents()[0].filectx(fromfile).data().splitlines() diff = unified_diff(file_before, file_after, fromfile=('a/%s' % tofile), tofile=('b/%s' % tofile), lineterm="") fulldiff = '\n'.join([chunk for chunk in diff]) ui.debug(fulldiff) patch = subprocess.Popen( ('patch', '-Nt', tofile), stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=repo.root, ) patchout, patcherr = patch.communicate(input=fulldiff) if patch.returncode: if repo.ui.configbool("autopatch", "mail", default=True) and ui.config( "email", "to"): ui.warn("failed patching %s, mailing details to repomaster.\n" % tofile) ui.debug(patchout) ui.debug(patcherr) fromaddr = ui.config("email", "from") toaddr = ui.config("email", "to") smtphost = ui.config("smtp", "host", default="localhost") if not fromaddr or not toaddr: ui.warn( "Repomaster should have configured from and to in the email section!" ) return True mail = "From: %(fromaddr)s\n" \ "To: %(toaddr)s\n" \ "Subject: %(tofile)s patch report for commit %(rev)d\n" \ "\n" \ "Hai Admins,\n" \ "\n" \ "%(fromfile)s has been changed in commit %(rev)d by %(user)s, but patching failed.\n" \ "\n" \ "Log message:\n" \ "%(desc)s\n\n" \ "I tried to apply this patch: \n" \ "%(diff)s\n\n" \ "Here comes the patch report:\n" \ "%(patch)s\n\n" \ "\n" % { 'fromaddr': fromaddr, 'toaddr': toaddr, 'fromfile': fromfile, 'tofile': tofile, 'rev': cc.rev(), 'user': cc.user(), 'desc': cc.description(), 'diff': fulldiff, 'patch': patchout + patcherr } serv = smtplib.SMTP(smtphost) serv.sendmail(util.email(fromaddr), util.email(toaddr), mail) serv.quit() else: ui.warn( "failed patching %s and emailing is disabled! Patch said:" % tofile) ui.warn(patchout) ui.warn(patcherr) else: ui.status("Successfully patched %s.\n" % tofile) return False
def hook(ui, repo, hooktype, node, source=None, **kwargs): if source in ('pull', 'strip'): return 0 DOM_peers = [ 'jst', # Johnny Stenback 'peterv', # Peter Van der Beken 'bz', 'bzbarsky', # Boris Zbarsky 'sicking', 'jonas', # Jonas Sicking 'smaug', # Olli Pettay 'bent', # Ben Turner 'mounir', # Mounir Lamouri 'khuey', # Kyle Huey 'jlebar', # Justin Lebar 'hsivonen', # Henri Sivonen 'mrbkap', # Blake Kaplan 'bholley', # Bobby Holley 'baku', # Andrea Marchesini 'ehsan', # Ehsan Akhgari # Non-DOM peers who can review some WebIDL changes 'hsinyi', 'htsai', # Hsin-Yi Tsai for RIL APIs ] DOM_authors = [ '*****@*****.**', # Johnny Stenback '*****@*****.**', # Peter Van der Beken '*****@*****.**', # Boris Zbarsky '*****@*****.**', # Jonas Sicking '*****@*****.**', # Olli Pettay '*****@*****.**', # Olli Pettay '*****@*****.**', # Ben Turner '*****@*****.**', # Mounir Lamouri '*****@*****.**', # Kyle Huey '*****@*****.**', # Justin Lebar '*****@*****.**', # Henri Sivonen '*****@*****.**', # Blake Kaplan '*****@*****.**', # Andrea Marchesini '*****@*****.**', # Ehsan Akhgari '*****@*****.**', # Ehsan Akhgari ] error = "" webidlReviewed = False changesets = list(repo.changelog.revs(repo[node].rev())) if 'a=release' in repo.changectx(changesets[-1]).description().lower(): # Accept the entire push for code uplifts. return 0 # Loop through each changeset being added to the repository for i in reversed(changesets): c = repo.changectx(i) if len(c.parents()) > 1: # Skip merge changesets continue # Loop through each file for the current changeset for file in c.files(): # Only Check WebIDL Files if file.endswith('.webidl'): message = c.description().lower() email = util.email(c.user()).lower() def search(): matches = re.findall('\Ws?r\s*=\s*(\w+(?:,\w+)*)', message) for match in matches: for reviewer in match.split(','): if reviewer in DOM_peers: return True # We allow DOM peers to commit changes to WebIDL files without any review # requirements assuming that they have looked at the changes they're committing. for peer in DOM_authors: if peer == email: return True return False webidlReviewed = search() if not webidlReviewed and not isBackout(message): error += "WebIDL file %s altered in changeset %s without DOM peer review\n" % (file, short(c.node())) # Check if an error occured in any of the files that were changed if error != "": print "\n\n************************** ERROR ****************************" ui.warn("\n" + error + "\n") print "\nChanges to WebIDL files in this repo require review from a DOM peer in the form of r=...\nThis is to ensure that we behave responsibly with exposing new Web APIs. We appreciate your understanding..\n" print "*************************************************************\n\n" # Reject the changesets return 1 else: if webidlReviewed: print "You've received proper review from a DOM peer on your WebIDL change(s) in your push, thanks for paying enough attention." # Accept the changesets return 0
def unlock(ui, repo, *pats, **opts): """ Release Lock: $ hg unlock [-f] [-v] <file ...> If no file specified, unlock would try to relaes all availble locks. -f Force unlock. Allows you to break others locks. Owner will be notified about this. -v Will display a bit more information then usual. Other options are available only for hook execution handling. """ lockFile = repo.root + pathSep + ".hg" + pathSep + "locked.files" user = ui.username() ui.note("repository: %s\n" % repo.root) ui.note("lockfile: %s\n" % lockFile) ui.note("user name: %s\n\n" % user) filesList=list() # Identify whether function is called as a hook, # and if so, change the command and reexecutei it. if 'hooktype' in opts: cmdline = list() cmdline = opts['args'].split() cmdline[0] = 'unlock' # Fixing problems around changed dispatcher (since v1.9) if hasattr(dispatch, 'request'): return(dispatch.dispatch(dispatch.request(cmdline))) else: return(dispatch.dispatch(cmdline)) #Calculate file path in repository if pats: for file in pats: if not os.path.exists(file): # file defined as path in repo (via hook call) if file in repo.dirstate: filesList.append(file) else: filesList.append(PathInRepo(repo.root, file)) # Load stored locking data lockedFilesList = LoadData(lockFile) # If files are not specified # try to release all available locks if not pats: filesList = lockedFilesList.keys() err = 0 for file in filesList: ui.note("checking: %s\n" % file) if file in lockedFilesList: # UnLock if not lockedFilesList[file][UserName] == user: # Force unlock and send email to lock owner if opts['force']: # Email format: RFC 2822 # example: "Vladimir Legeza <*****@*****.**>" from mercurial import mail sendFrom = util.email(user) sendTo = [util.email(lockedFilesList[file][UserName])] message = "The lock you have set on '%s' file was removed by %s." % \ (file, lockedFilesList[file][UserName]) ui.note("sending email to: %s\n" % sendTo) mail.sendmail(ui, sendFrom, sendTo, message) ui.note("unlocking: %s\n" % file) lockedFilesList.pop(file) else: err += 1 ui.warn("%s - locked by %s.\n" % (file, lockedFilesList[file][UserName])) else: ui.note("unlocking: %s\n" % file) lockedFilesList.pop(file) if err: raise util.Abort(i18n._("Lock ownership violation.")) # Save changes StoreData(lockFile, lockedFilesList)
root=self.repo.root, webroot=webroot(self.repo.root)) data = self.ui.popbuffer() self.add_comment(bugid, data, util.email(ctx.user())) def hook(ui, repo, hooktype, node=None, **kwargs): '''add comment to bugzilla for each changeset that refers to a bugzilla bug id. only add a comment once per bug, so same change seen multiple times does not fill bug with duplicate data.''' try: import MySQLdb as mysql global MySQLdb MySQLdb = mysql except ImportError, err: raise util.Abort(_('python mysql support not available: %s') % err) if node is None: raise util.Abort(_('hook type %s does not pass a changeset id') % hooktype) try: bz = bugzilla(ui, repo) ctx = repo[node] ids = bz.find_bug_ids(ctx) if ids: for id in ids: bz.update(id, ctx) bz.notify(ids, util.email(ctx.user())) except MySQLdb.MySQLError, err: raise util.Abort(_('database error: %s') % err[1])
root=self.repo.root, webroot=webroot(self.repo.root)) data = self.ui.popbuffer() self.add_comment(bugid, data, util.email(ctx.user())) def hook(ui, repo, hooktype, node=None, **kwargs): '''add comment to bugzilla for each changeset that refers to a bugzilla bug id. only add a comment once per bug, so same change seen multiple times does not fill bug with duplicate data.''' try: import MySQLdb as mysql global MySQLdb MySQLdb = mysql except ImportError, err: raise util.Abort(_('python mysql support not available: %s') % err) if node is None: raise util.Abort( _('hook type %s does not pass a changeset id') % hooktype) try: bz = bugzilla(ui, repo) ctx = repo[node] ids = bz.find_bug_ids(ctx) if ids: for id in ids: bz.update(id, ctx) bz.notify(ids, util.email(ctx.user())) except MySQLdb.MySQLError, err: raise util.Abort(_('database error: %s') % err.args[1])
def hook(ui, repo, hooktype, node, source=None, **kwargs): if source in ('pull', 'strip'): return 0 DOM_peers = [ 'jst', # Johnny Stenback 'peterv', # Peter Van der Beken 'bz', 'bzbarsky', # Boris Zbarsky 'sicking', 'jonas', # Jonas Sicking 'smaug', # Olli Pettay 'bent', # Ben Turner 'mounir', # Mounir Lamouri 'khuey', # Kyle Huey 'jlebar', # Justin Lebar 'hsivonen', # Henri Sivonen 'mrbkap', # Blake Kaplan 'bholley', # Bobby Holley 'baku', # Andrea Marchesini 'ehsan', # Ehsan Akhgari 'bkelly', # Ben Kelly 'billm', # Bill McCloskey 'qdot', 'kmachulis', # Kyle Machulis # Non-DOM peers who can review some WebIDL changes 'hsinyi', 'htsai', # Hsin-Yi Tsai for RIL APIs ] DOM_authors = [ '*****@*****.**', # Johnny Stenback '*****@*****.**', # Peter Van der Beken '*****@*****.**', # Boris Zbarsky '*****@*****.**', # Jonas Sicking '*****@*****.**', # Olli Pettay '*****@*****.**', # Olli Pettay '*****@*****.**', # Ben Turner '*****@*****.**', # Mounir Lamouri '*****@*****.**', # Kyle Huey '*****@*****.**', # Justin Lebar '*****@*****.**', # Henri Sivonen '*****@*****.**', # Blake Kaplan '*****@*****.**', # Andrea Marchesini '*****@*****.**', # Ehsan Akhgari '*****@*****.**', # Ehsan Akhgari '*****@*****.**', # Ben Kelly '*****@*****.**', # Ben Kelly '*****@*****.**', # Bill McCloskey, '*****@*****.**', # Kyle Machulis '*****@*****.**', # Kyle Machulis '*****@*****.**', # Kyle Machulis ] error = "" webidlReviewed = False changesets = list(repo.changelog.revs(repo[node].rev())) if 'a=release' in repo.changectx(changesets[-1]).description().lower(): # Accept the entire push for code uplifts. return 0 # Loop through each changeset being added to the repository for i in reversed(changesets): c = repo.changectx(i) if len(c.parents()) > 1: # Skip merge changesets continue # Loop through each file for the current changeset for file in c.files(): # Only Check WebIDL Files if file.endswith('.webidl'): message = c.description().lower() email = util.email(c.user()).lower() def search(): matches = re.findall('\Ws?r\s*=\s*(\w+(?:,\w+)*)', message) for match in matches: for reviewer in match.split(','): if reviewer in DOM_peers: return True # We allow DOM peers to commit changes to WebIDL files without any review # requirements assuming that they have looked at the changes they're committing. for peer in DOM_authors: if peer == email: return True return False webidlReviewed = search() if not webidlReviewed and not isBackout(message): error += "WebIDL file %s altered in changeset %s without DOM peer review\n" % ( file, short(c.node())) # Check if an error occured in any of the files that were changed if error != "": print "\n\n************************** ERROR ****************************" ui.warn("\n" + error + "\n") print "\nChanges to WebIDL files in this repo require review from a DOM peer in the form of r=...\nThis is to ensure that we behave responsibly with exposing new Web APIs. We appreciate your understanding..\n" print "*************************************************************\n\n" # Reject the changesets return 1 else: if webidlReviewed: print "You've received proper review from a DOM peer on your WebIDL change(s) in your push, thanks for paying enough attention." # Accept the changesets return 0
class notifier(object): '''email notification class.''' def __init__(self, ui, repo, hooktype): self.ui = ui cfg = self.ui.config('notify', 'config') if cfg: self.ui.readconfig(cfg, sections=['usersubs', 'reposubs']) self.repo = repo self.stripcount = int(self.ui.config('notify', 'strip', 0)) self.root = self.strip(self.repo.root) self.domain = self.ui.config('notify', 'domain') self.mbox = self.ui.config('notify', 'mbox') self.test = self.ui.configbool('notify', 'test', True) self.charsets = mail._charsets(self.ui) self.subs = self.subscribers() self.merge = self.ui.configbool('notify', 'merge', True) mapfile = self.ui.config('notify', 'style') template = (self.ui.config('notify', hooktype) or self.ui.config('notify', 'template')) if not mapfile and not template: template = deftemplates.get(hooktype) or single_template if template: template = templater.parsestring(template, quoted=False) self.t = cmdutil.changeset_templater(self.ui, self.repo, False, None, template, mapfile, False) def strip(self, path): '''strip leading slashes from local path, turn into web-safe path.''' path = util.pconvert(path) count = self.stripcount while count > 0: c = path.find('/') if c == -1: break path = path[c + 1:] count -= 1 return path def fixmail(self, addr): '''try to clean up email addresses.''' addr = util.email(addr.strip()) if self.domain: a = addr.find('@localhost') if a != -1: addr = addr[:a] if '@' not in addr: return addr + '@' + self.domain return addr def subscribers(self): '''return list of email addresses of subscribers to this repo.''' subs = set() for user, pats in self.ui.configitems('usersubs'): for pat in pats.split(','): if '#' in pat: pat, revs = pat.split('#', 1) else: revs = None if fnmatch.fnmatch(self.repo.root, pat.strip()): subs.add((self.fixmail(user), revs)) for pat, users in self.ui.configitems('reposubs'): if '#' in pat: pat, revs = pat.split('#', 1) else: revs = None if fnmatch.fnmatch(self.repo.root, pat): for user in users.split(','): subs.add((self.fixmail(user), revs)) return [(mail.addressencode(self.ui, s, self.charsets, self.test), r) for s, r in sorted(subs)] def node(self, ctx, **props): '''format one changeset, unless it is a suppressed merge.''' if not self.merge and len(ctx.parents()) > 1: return False self.t.show(ctx, changes=ctx.changeset(), baseurl=self.ui.config('web', 'baseurl'), root=self.repo.root, webroot=self.root, **props) return True def skipsource(self, source): '''true if incoming changes from this source should be skipped.''' ok_sources = self.ui.config('notify', 'sources', 'serve').split() return source not in ok_sources def send(self, ctx, count, data): '''send message.''' # Select subscribers by revset subs = set() for sub, spec in self.subs: if spec is None: subs.add(sub) continue revs = self.repo.revs('%r and %d:', spec, ctx.rev()) if len(revs): subs.add(sub) continue if len(subs) == 0: self.ui.debug('notify: no subscribers to selected repo ' 'and revset\n') return p = email.Parser.Parser() try: msg = p.parsestr(data) except email.Errors.MessageParseError, inst: raise util.Abort(inst) # store sender and subject sender, subject = msg['From'], msg['Subject'] del msg['From'], msg['Subject'] if not msg.is_multipart(): # create fresh mime message from scratch # (multipart templates must take care of this themselves) headers = msg.items() payload = msg.get_payload() # for notification prefer readability over data precision msg = mail.mimeencode(self.ui, payload, self.charsets, self.test) # reinstate custom headers for k, v in headers: msg[k] = v msg['Date'] = util.datestr(format="%a, %d %b %Y %H:%M:%S %1%2") # try to make subject line exist and be useful if not subject: if count > 1: subject = _('%s: %d new changesets') % (self.root, count) else: s = ctx.description().lstrip().split('\n', 1)[0].rstrip() subject = '%s: %s' % (self.root, s) maxsubject = int(self.ui.config('notify', 'maxsubject', 67)) if maxsubject: subject = util.ellipsis(subject, maxsubject) msg['Subject'] = mail.headencode(self.ui, subject, self.charsets, self.test) # try to make message have proper sender if not sender: sender = self.ui.config('email', 'from') or self.ui.username() if '@' not in sender or '@localhost' in sender: sender = self.fixmail(sender) msg['From'] = mail.addressencode(self.ui, sender, self.charsets, self.test) msg['X-Hg-Notification'] = 'changeset %s' % ctx if not msg['Message-Id']: msg['Message-Id'] = ('<hg.%s.%s.%s@%s>' % (ctx, int( time.time()), hash(self.repo.root), socket.getfqdn())) msg['To'] = ', '.join(sorted(subs)) msgtext = msg.as_string() if self.test: self.ui.write(msgtext) if not msgtext.endswith('\n'): self.ui.write('\n') else: self.ui.status( _('notify: sending %d subscribers %d changes\n') % (len(subs), count)) mail.sendmail(self.ui, util.email(msg['From']), subs, msgtext, mbox=self.mbox)
def hook(ui, repo, hooktype, node, source=None, **kwargs): if source in ('pull', 'strip'): return 0 DOM_peers = [ 'jst', # Johnny Stenback 'peterv', # Peter Van der Beken 'bz', 'bzbarsky', # Boris Zbarsky 'sicking', 'jonas', # Jonas Sicking 'smaug', # Olli Pettay 'bent', # Ben Turner 'mounir', # Mounir Lamouri 'khuey', # Kyle Huey 'jlebar', # Justin Lebar 'hsivonen', # Henri Sivonen 'mrbkap', # Blake Kaplan 'bholley', # Bobby Holley 'baku', # Andrea Marchesini 'ehsan', # Ehsan Akhgari 'bkelly', # Ben Kelly 'billm', # Bill McCloskey 'qdot', 'kmachulis', # Kyle Machulis # Non-DOM peers who can review some WebIDL changes 'hsinyi', 'htsai', # Hsin-Yi Tsai for RIL APIs ] DOM_authors = [ '*****@*****.**', # Johnny Stenback '*****@*****.**', # Peter Van der Beken '*****@*****.**', # Boris Zbarsky '*****@*****.**', # Jonas Sicking '*****@*****.**', # Olli Pettay '*****@*****.**', # Olli Pettay '*****@*****.**', # Ben Turner '*****@*****.**', # Mounir Lamouri '*****@*****.**', # Kyle Huey '*****@*****.**', # Justin Lebar '*****@*****.**', # Henri Sivonen '*****@*****.**', # Blake Kaplan '*****@*****.**', # Andrea Marchesini '*****@*****.**', # Ehsan Akhgari '*****@*****.**', # Ehsan Akhgari '*****@*****.**', # Ben Kelly '*****@*****.**', # Ben Kelly '*****@*****.**', # Bill McCloskey, '*****@*****.**', # Kyle Machulis '*****@*****.**', # Kyle Machulis '*****@*****.**', # Kyle Machulis ] IPC_peers = [ 'billm', # Bill McCloskey 'dvander', # David Anderson 'jld', # Jed Davis 'kanru', # Kan-Ru Chen 'bkelly', # Ben Kelly 'froydnj', # Nathan Froyd 'mccr8', # Andrew McCreight ] IPC_authors = [ '*****@*****.**', # Bill McCloskey, '*****@*****.**', # David Anderson '*****@*****.**', # David Anderson '*****@*****.**', # Jed Davis '*****@*****.**', # Kan-Ru Chen '*****@*****.**', # Kan-Ru Chen '*****@*****.**', # Ben Kelly '*****@*****.**', # Ben Kelly '*****@*****.**', # Nathan Froyd '*****@*****.**', # Andrew McCreight ] error = "" note = "" webidlReviewed = False syncIPCReviewed = False changesets = list(repo.changelog.revs(repo[node].rev())) if 'a=release' in repo.changectx(changesets[-1]).description().lower(): # Accept the entire push for code uplifts. return 0 # Loop through each changeset being added to the repository for i in reversed(changesets): c = repo.changectx(i) if len(c.parents()) > 1: # Skip merge changesets continue # Loop through each file for the current changeset for file in c.files(): if file.startswith('servo/'): ui.write('(%s modifies %s from Servo; not enforcing peer ' 'review)\n' % (short(c.node()), file)) continue message = c.description().lower() email = util.email(c.user()).lower() # Ignore backouts if isBackout(message): continue def search(authors, peers): matches = re.findall('\Ws?r\s*=\s*(\w+(?:,\w+)*)', message) for match in matches: if any(reviewer in peers for reviewer in match.split(',')): return True # We allow peers to commit changes without any review # requirements assuming that they have looked at the changes # they're committing. if any(peer == email for peer in authors): return True return False # Only check WebIDL files here. if file.endswith('.webidl'): webidlReviewed = search(DOM_authors, DOM_peers) if not webidlReviewed: error += "WebIDL file %s altered in changeset %s without DOM peer review\n" % ( file, short(c.node())) note = "\nChanges to WebIDL files in this repo require review from a DOM peer in the form of r=...\nThis is to ensure that we behave responsibly with exposing new Web APIs. We appreciate your understanding..\n" # Only check the IPDL sync-messages.ini here. elif file.endswith('ipc/ipdl/sync-messages.ini'): syncIPCReviewed = search(IPC_authors, IPC_peers) if not syncIPCReviewed: error += "sync-messages.ini altered in changeset %s without IPC peer review\n" % ( short(c.node())) note = "\nChanges to sync-messages.ini in this repo require review from a IPC peer in the form of r=...\nThis is to ensure that we behave responsibly by not adding sync IPC messages that cause performance issues needlessly. We appreciate your understanding..\n" # Check if an error occured in any of the files that were changed if error != "": print "\n\n************************** ERROR ****************************" ui.warn("\n" + error + "\n") print note print "*************************************************************\n\n" # Reject the changesets return 1 else: if webidlReviewed: print "You've received proper review from a DOM peer on the WebIDL change(s) in your push, thanks for paying enough attention." if syncIPCReviewed: print "You've received proper review from an IPC peer on the sync-messages.ini change(s) in your push, thanks for paying enough attention." # Accept the changesets return 0
def get_user(self): node_user = self.node.user() email = util.email(node_user) name = person(node_user) return name
def author_email(self): return email(self.rev.user())
def send(self, ctx, count, data): '''send message.''' # Select subscribers by revset subs = set() for sub, spec in self.subs: if spec is None: subs.add(sub) continue revs = self.repo.revs('%r and %d:', spec, ctx.rev()) if len(revs): subs.add(sub) continue if len(subs) == 0: self.ui.debug('notify: no subscribers to selected repo ' 'and revset\n') return p = email.Parser.Parser() try: msg = p.parsestr(data) except email.Errors.MessageParseError as inst: raise util.Abort(inst) # store sender and subject sender, subject = msg['From'], msg['Subject'] del msg['From'], msg['Subject'] if not msg.is_multipart(): # create fresh mime message from scratch # (multipart templates must take care of this themselves) headers = msg.items() payload = msg.get_payload() # for notification prefer readability over data precision msg = mail.mimeencode(self.ui, payload, self.charsets, self.test) # reinstate custom headers for k, v in headers: msg[k] = v msg['Date'] = util.datestr(format="%a, %d %b %Y %H:%M:%S %1%2") # try to make subject line exist and be useful if not subject: if count > 1: subject = _('%s: %d new changesets') % (self.root, count) else: s = ctx.description().lstrip().split('\n', 1)[0].rstrip() subject = '%s: %s' % (self.root, s) maxsubject = int(self.ui.config('notify', 'maxsubject', 67)) if maxsubject: subject = util.ellipsis(subject, maxsubject) msg['Subject'] = mail.headencode(self.ui, subject, self.charsets, self.test) # try to make message have proper sender if not sender: sender = self.ui.config('email', 'from') or self.ui.username() if '@' not in sender or '@localhost' in sender: sender = self.fixmail(sender) msg['From'] = mail.addressencode(self.ui, sender, self.charsets, self.test) msg['X-Hg-Notification'] = 'changeset %s' % ctx if not msg['Message-Id']: msg['Message-Id'] = ('<hg.%s.%s.%s@%s>' % (ctx, int(time.time()), hash(self.repo.root), socket.getfqdn())) msg['To'] = ', '.join(sorted(subs)) msgtext = msg.as_string() if self.test: self.ui.write(msgtext) if not msgtext.endswith('\n'): self.ui.write('\n') else: self.ui.status(_('notify: sending %d subscribers %d changes\n') % (len(subs), count)) mail.sendmail(self.ui, util.email(msg['From']), subs, msgtext, mbox=self.mbox)
def send(self, ctx, count, data): '''send message.''' # Select subscribers by revset subs = set() for sub, spec in self.subs: if spec is None: subs.add(sub) continue revs = self.repo.revs('%r and %d:', spec, ctx.rev()) if len(revs): subs.add(sub) continue if len(subs) == 0: self.ui.debug('notify: no subscribers to selected repo ' 'and revset\n') return p = email.Parser.Parser() try: msg = p.parsestr(data) except email.Errors.MessageParseError as inst: raise util.Abort(inst) # store sender and subject sender, subject = msg['From'], msg['Subject'] del msg['From'], msg['Subject'] if not msg.is_multipart(): # create fresh mime message from scratch # (multipart templates must take care of this themselves) headers = msg.items() payload = msg.get_payload() # for notification prefer readability over data precision msg = mail.mimeencode(self.ui, payload, self.charsets, self.test) # reinstate custom headers for k, v in headers: msg[k] = v msg['Date'] = util.datestr(format="%a, %d %b %Y %H:%M:%S %1%2") # try to make subject line exist and be useful if not subject: if count > 1: subject = _('%s: %d new changesets') % (self.root, count) else: s = ctx.description().lstrip().split('\n', 1)[0].rstrip() subject = '%s: %s' % (self.root, s) maxsubject = int(self.ui.config('notify', 'maxsubject', 67)) if maxsubject: subject = util.ellipsis(subject, maxsubject) msg['Subject'] = mail.headencode(self.ui, subject, self.charsets, self.test) # try to make message have proper sender if not sender: sender = self.ui.config('email', 'from') or self.ui.username() if '@' not in sender or '@localhost' in sender: sender = self.fixmail(sender) msg['From'] = mail.addressencode(self.ui, sender, self.charsets, self.test) msg['X-Hg-Notification'] = 'changeset %s' % ctx if not msg['Message-Id']: msg['Message-Id'] = ('<hg.%s.%s.%s@%s>' % (ctx, int( time.time()), hash(self.repo.root), socket.getfqdn())) msg['To'] = ', '.join(sorted(subs)) msgtext = msg.as_string() if self.test: self.ui.write(msgtext) if not msgtext.endswith('\n'): self.ui.write('\n') else: self.ui.status( _('notify: sending %d subscribers %d changes\n') % (len(subs), count)) mail.sendmail(self.ui, util.email(msg['From']), subs, msgtext, mbox=self.mbox)