hgext/push-to-try/__init__.py (88 lines of code) (raw):

# This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. import os import json from mercurial import ( commands, context, registrar, ) from mercurial.i18n import _ OUR_DIR = os.path.normpath(os.path.dirname(__file__)).encode("ascii") with open(os.path.join(OUR_DIR, b"..", b"bootstrap.py")) as f: exec(f.read()) from mozhg.rewrite import preservefilectx cmdtable = {} command = registrar.command(cmdtable) configtable = {} configitem = registrar.configitem(configtable) configitem(b"push-to-try", b"nodate", default=False) testedwith = b"4.6 4.7 4.8 4.9 5.0 5.1 5.2 5.3 5.4 5.5" minimumhgversion = b"4.6" @command( b"push-to-try", [ (b"m", b"message", b"", b"commit message to use", b"MESSAGE"), (b"s", b"server", b"", b"push destination", b"URL"), ], b"-m MESSAGE -s URL", ) def push_to_try(ui, repo, server, message=None): nodate = ui.configbool(b"push-to-try", b"nodate") if not server: if b"try" in ui.paths: server = b"try" else: server = b"ssh://hg.mozilla.org/try" if not message: ui.status(b"STOP! A commit message is required.\n") return cctx = context.workingctx(repo) if b"try_task_config.json" not in cctx and b"try:" not in message: ui.status( b"STOP! Either try_task_config.json must be added or the commit " b"message must contain try syntax.\n" ) return if b"try_task_config.json" in cctx: data = repo.wvfs.tryread(b"try_task_config.json") try: # data could be an empty string if tryread failed, which will # produce a ValueError here. data = json.loads(data) except ValueError as e: ui.status(b"Error reading try_task_config.json: could not decode as JSON\n") return # Invent a temporary commit with our message. ui.status(b"Creating temporary commit for remote...\n") status = repo.status() if status.modified + status.added + status.removed: # TODO: Achieve this by re-using the status call above to avoid the # cost of running it twice. commands.status(ui, repo) preserve_ctx = preservefilectx(cctx) def mk_memfilectx(repo, memctx, path): if path not in status.removed: return preserve_ctx(repo, memctx, path) return None mctx = context.memctx( repo, repo.dirstate.parents(), message, cctx.files(), mk_memfilectx, date=b"0 0" if nodate else None, ) # These messages are expected when we abort our transaction, but aren't # helpful to a user and may be misleading so we surpress them here. filtered_phrases = {_(b"transaction abort!\n"), _(b"rollback completed\n")} def filtered_warn(*msgs, **opts): if msgs: filtered = [m for m in msgs if m not in filtered_phrases] if filtered: ui.warn(*filtered, **opts) lock = tr = None try: lock = repo.lock() tr = repo.transaction(b"push-to-try", report=filtered_warn) m = mctx.commit() # Push to try. commands.push(ui, repo, server, force=True, rev=[repo[m].rev()]) ui.status(b"push complete\n") # And rollback to the previous state. tr.abort() finally: if tr: tr.release() if lock: lock.release() ui.status(b"temporary commit removed, repository restored\n")