hghooks/mozhghooks/treeclosure.py (71 lines of code) (raw):

#!/usr/bin/env python # Copyright (C) 2012 Mozilla Foundation # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. from mercurial import ( urllibcompat, ) import os.path import re import json from mercurial import ( pycompat, ) magicwords = b"CLOSED TREE" def printError(ui, message): ui.write(b"\n\n************************** ERROR ****************************\n") ui.write(message) ui.write(b"*************************************************************\n\n\n") def isPushAllowed(ui, repo, name): treestatus_base_url = ui.config( b"mozilla", b"treestatus_base_url", b"https://treestatus.prod.lando.prod.cloudops.mozgcp.net/trees/%s", ) url = treestatus_base_url % (name,) try: request = urllibcompat.urlreq.request(pycompat.sysstr(url)) u = urllibcompat.urlreq.urlopen(request) data = json.load(u) if data["result"]["status"] == "closed": closure_text = b"%s is CLOSED! Reason: %s" % ( name, pycompat.bytestr(data["result"]["reason"]), ) # Block the push unless they know the magic words if repo[b"tip"].description().find(magicwords) == -1: printError( ui, b'%s\nTo push despite the closed tree, include "%s" in your push comment\n' % (closure_text, magicwords), ) return False ui.write( b"%s\nBut you included the magic words. Hope you had permission!\n" % closure_text ) elif data["result"]["status"] == "approval required": # Block the push unless they have approval or are backing out dlower = repo[b"tip"].description().lower() if not ( re.search(rb"a\S*=", dlower) or dlower.startswith(b"back") or dlower.startswith(b"revert") ): printError( ui, b"Pushing to an APPROVAL REQUIRED tree requires your top changeset comment to include: " b"a=... (or, more accurately, a\\S*=...)\n", ) return False except (ValueError, IOError) as err: # fail closed if treestatus is down, unless the magic words have been used printError( ui, b"Error accessing %s :\n" b"%s\n" b"Unable to check if the tree is open - treating as if CLOSED.\n" b'To push regardless, include "%s" in your push comment.\n' % (url, pycompat.bytestr(str(err)), magicwords), ) if repo[b"tip"].description().find(magicwords) == -1: return False return True def hook(ui, repo, source=None, **kwargs): if source in (b"pull", b"strip"): return 0 treestatus_name = os.path.basename(repo.root) return 0 if isPushAllowed(ui, repo, treestatus_name) else 1