#
# Jasy - Web Tooling Framework
# Copyright 2010-2012 Zynga Inc.
#
import os.path
import re
import urllib.parse
import shutil
from jasy.core.Util import executeCommand
import jasy.core.Console as Console
__versionNumber = re.compile(r"^v?([0-9\.]+)(-?(a|b|rc|alpha|beta)([0-9]+)?)?\+?$")
__gitAccountUrl = re.compile("([a-zA-Z0-9-_]+)@([a-zA-Z0-9-_\.]+):([a-zA-Z0-9/_-]+\.git)")
__gitHash = re.compile(r"^[a-f0-9]{40}$")
__gitSchemes = ('git', 'git+http', 'git+https', 'git+ssh', 'git+git', 'git+file')
[docs]def update(url, version, path, update=True, submodules=True):
"""Clones the given repository URL (optionally with overriding/update features)"""
# Prepend git+ so that user knows that we identified the URL as git repository
if not url.startswith("git+"):
url = "git+%s" % url
old = os.getcwd()
if os.path.exists(path) and os.path.exists(os.path.join(path, ".git")):
if not os.path.exists(os.path.join(path, ".git", "HEAD")):
Console.error("Invalid git clone. Cleaning up...")
shutil.rmtree(path)
else:
os.chdir(path)
revision = executeCommand(["git", "rev-parse", "HEAD"], "Could not detect current revision")
if update and (version == "master" or "refs/heads/" in version):
if update:
Console.info("Updating %s", Console.colorize("%s @ " % url, "bold") + Console.colorize(version, "magenta"))
Console.indent()
try:
executeCommand(["git", "fetch", "-q", "--depth", "1", "origin", version], "Could not fetch updated revision!")
executeCommand(["git", "reset", "-q", "--hard", "FETCH_HEAD"], "Could not update checkout!")
newRevision = executeCommand(["git", "rev-parse", "HEAD"], "Could not detect current revision")
if revision != newRevision:
Console.info("Updated from %s to %s", revision[:10], newRevision[:10])
revision = newRevision
if submodules and os.path.exists(".gitmodules"):
Console.info("Updating sub modules (this might take some time)...")
executeCommand("git submodule update --recursive", "Could not initialize sub modules")
except Exception:
Console.error("Error during git transaction! Could not update clone.")
Console.error("Please verify that the host is reachable or disable automatic branch updates.")
Console.outdent()
os.chdir(old)
return
except KeyboardInterrupt:
print()
Console.error("Git transaction was aborted by user!")
Console.outdent()
os.chdir(old)
return
Console.outdent()
else:
Console.debug("Updates disabled")
else:
Console.debug("Using existing clone")
os.chdir(old)
return revision
Console.info("Cloning %s", Console.colorize("%s @ " % url, "bold") + Console.colorize(version, "magenta"))
Console.indent()
os.makedirs(path)
os.chdir(path)
try:
# cut of "git+" prefix
remoteurl = url[4:]
executeCommand(["git", "init", "."], "Could not initialize GIT repository!")
executeCommand(["git", "remote", "add", "origin", remoteurl], "Could not register remote repository!")
executeCommand(["git", "fetch", "-q", "--depth", "1", "origin", version], "Could not fetch revision!")
executeCommand(["git", "reset", "-q", "--hard", "FETCH_HEAD"], "Could not update checkout!")
revision = executeCommand(["git", "rev-parse", "HEAD"], "Could not detect current revision")
if submodules and os.path.exists(".gitmodules"):
Console.info("Updating sub modules (this might take some time)...")
executeCommand("git submodule update --init --recursive", "Could not initialize sub modules")
except Exception:
Console.error("Error during git transaction! Intitial clone required for continuing!")
Console.error("Please verify that the host is reachable.")
Console.error("Cleaning up...")
os.chdir(old)
shutil.rmtree(path)
Console.outdent()
return
except KeyboardInterrupt:
print()
Console.error("Git transaction was aborted by user!")
Console.error("Cleaning up...")
os.chdir(old)
shutil.rmtree(path)
Console.outdent()
return
os.chdir(old)
Console.outdent()
return revision
[docs]def getBranch(path=None):
"""Returns the name of the git branch."""
return executeCommand("git rev-parse --abbrev-ref HEAD", "Could not figure out git branch. Is there a valid Git repository?", path=path)
[docs]def getRevision(path=None):
"""Returns the last revision/hash of the git repository."""
return executeCommand("git rev-parse HEAD", "Could not figure out git revision. Is there a valid Git repository?", path=path)
[docs]def getShortRevision(path=None):
"""Returns the last shortened revision/hash of the git repository."""
return executeCommand("git rev-parse --short HEAD", "Could not figure out git revision. Is there a valid Git repository?", path=path)
[docs]def isUrl(url):
"""Figures out whether the given string is a valid Git repository URL."""
parsed = urllib.parse.urlparse(url)
if not parsed.params and not parsed.query and not parsed.fragment:
if parsed.scheme in __gitSchemes:
return True
elif parsed.scheme == "https" and parsed.path.endswith(".git"):
return True
elif not parsed.scheme and parsed.path == url and __gitAccountUrl.match(url) is not None:
return True
return False
[docs]def expandVersion(version=None):
if version is None:
version = "master"
version = str(version)
if version.startswith("refs/"):
pass
elif re.compile(r"^[a-f0-9]{40}$").match(version):
# See also: http://git.661346.n2.nabble.com/Fetch-by-SHA-missing-td5604552.html
raise Exception("Can't fetch non tags/branches: %s@%s!" % (url, version))
elif __versionNumber.match(version) is not None:
version = "refs/tags/" + version
else:
version = "refs/heads/" + version
return version
[docs]def cleanRepository():
"""Cleans git repository from untracked files."""
return executeCommand(["git", "clean", "-d", "-f"], "Could not clean GIT repository!")
[docs]def distcleanRepository():
"""
Cleans git repository from untracked files.
Ignores the files listed in ".gitignore".
"""
return executeCommand(["git", "clean", "-d", "-f", "-x"], "Could not distclean GIT repository!")