| |
@@ -51,12 +51,16 @@
|
| |
types = scm_url_schemes()
|
| |
|
| |
def __init__(self, url, branch=None, allowed_scm=None, allow_local=False):
|
| |
- """Initialize the SCM object using the specified scmurl.
|
| |
+ """
|
| |
+ Initialize the SCM object using the specified SCM URL.
|
| |
|
| |
If url is not in the list of allowed_scm, an error will be raised.
|
| |
|
| |
:param str url: The unmodified scmurl
|
| |
- :param list allowed_scm: The list of allowed SCMs, optional
|
| |
+ :param str branch: The optional source control branch. This defaults to "master" when git
|
| |
+ is used.
|
| |
+ :param list allowed_scm: The optional list of allowed SCM URL prefixes
|
| |
+ :param bool allow_local: Allow SCM URLs that start with "file://"
|
| |
:raises: Forbidden or ValidationError
|
| |
"""
|
| |
|
| |
@@ -94,8 +98,6 @@
|
| |
self.name = self.name[:-4]
|
| |
self.commit = match.group("commit")
|
| |
self.branch = branch if branch else "master"
|
| |
- if not self.commit:
|
| |
- self.commit = self.get_latest(self.branch)
|
| |
self.version = None
|
| |
else:
|
| |
raise ValidationError("Unhandled SCM scheme: %s" % self.scheme)
|
| |
@@ -167,30 +169,24 @@
|
| |
if self.scheme == "git":
|
| |
self.sourcedir = "%s/%s" % (scmdir, self.name)
|
| |
|
| |
- module_clone_cmd = ["git", "clone", "-q"]
|
| |
- if self.commit:
|
| |
- module_clone_cmd.append("--no-checkout")
|
| |
- module_checkout_cmd = ["git", "checkout", "-q", self.commit]
|
| |
- else:
|
| |
- module_clone_cmd.extend(["--depth", "1"])
|
| |
- module_clone_cmd.extend([self.repository, self.sourcedir])
|
| |
-
|
| |
+ module_clone_cmd = [
|
| |
+ "git", "clone", "-q", "--no-checkout", self.repository, self.sourcedir
|
| |
+ ]
|
| |
+ module_checkout_cmd = ["git", "checkout", "-q", self.commit]
|
| |
# perform checkouts
|
| |
SCM._run(module_clone_cmd, chdir=scmdir)
|
| |
- if self.commit:
|
| |
- try:
|
| |
- SCM._run(module_checkout_cmd, chdir=self.sourcedir)
|
| |
- except RuntimeError as e:
|
| |
- if (
|
| |
- e.message.endswith(' did not match any file(s) known to git.\\n"')
|
| |
- or "fatal: reference is not a tree: " in e.message
|
| |
- ):
|
| |
- raise UnprocessableEntity(
|
| |
- "checkout: The requested commit hash was not found "
|
| |
- "within the repository. Perhaps you forgot to push. "
|
| |
- "The original message was: %s" % e.message
|
| |
- )
|
| |
- raise
|
| |
+ try:
|
| |
+ SCM._run(module_checkout_cmd, chdir=self.sourcedir)
|
| |
+ except RuntimeError as e:
|
| |
+ if (
|
| |
+ e.message.endswith(' did not match any file(s) known to git.\\n"')
|
| |
+ or "fatal: reference is not a tree: " in e.message
|
| |
+ ):
|
| |
+ raise UnprocessableEntity(
|
| |
+ "checkout: The requested commit hash was not found within the repository. "
|
| |
+ "Perhaps you forgot to push. The original message was: %s" % e.message
|
| |
+ )
|
| |
+ raise
|
| |
|
| |
timestamp = SCM._run(["git", "show", "-s", "--format=%ct"], chdir=self.sourcedir)[1]
|
| |
dt = datetime.datetime.utcfromtimestamp(int(timestamp))
|
| |
@@ -199,28 +195,28 @@
|
| |
raise RuntimeError("checkout: Unhandled SCM scheme.")
|
| |
return self.sourcedir
|
| |
|
| |
- def get_latest(self, ref="master"):
|
| |
+ def get_latest(self, ref=None):
|
| |
""" Get the latest commit hash based on the provided git ref
|
| |
|
| |
- :param ref: a string of a git ref (either a branch or commit hash)
|
| |
+ :param ref: a string of a git ref (either a branch or commit hash). This defaults to
|
| |
+ self.branch.
|
| |
:returns: a string of the latest commit hash
|
| |
:raises: RuntimeError
|
| |
"""
|
| |
if ref is None:
|
| |
- ref = "master"
|
| |
+ ref = self.branch
|
| |
+
|
| |
if self.scheme == "git":
|
| |
log.debug("Getting/verifying commit hash for %s" % self.repository)
|
| |
try:
|
| |
- # This will fail if `ref` is not a branch name, but for example commit hash.
|
| |
- # It is valid use-case to use commit hashes in modules instead of branch names
|
| |
- # and in this case, there is no other way to get the full commit hash then
|
| |
- # fallbac to `get_full_commit_hash`. We do not want to retry here, because
|
| |
- # in case module contains only commit hashes, it would block for very long
|
| |
- # time.
|
| |
- _, output, _ = SCM._run_without_retry(
|
| |
- ["git", "ls-remote", "--exit-code", self.repository, "refs/heads/" + ref]
|
| |
- )
|
| |
+ # This will fail if `ref` is not a branch name, but this works for commit hashes.
|
| |
+ # If the ref is not a branch, then fallback to `get_full_commit_hash`. We do not
|
| |
+ # want to retry here, since if it's a commit, it would block for a very long time.
|
| |
+ cmd = ["git", "ls-remote", "--exit-code", self.repository, "refs/heads/" + ref]
|
| |
+ log.debug("Checking to see if the ref %s is a branch with `%s`", ref, " ".join(cmd))
|
| |
+ _, output, _ = SCM._run_without_retry(cmd)
|
| |
except UnprocessableEntity:
|
| |
+ log.debug("The ref %s is not a branch. Checking to see if it's a commit hash", ref)
|
| |
# The call below will either return the commit hash as is (if a full one was
|
| |
# provided) or the full commit hash (if a short hash was provided). If ref is not
|
| |
# a commit hash, then this will raise an exception.
|
| |
@@ -242,18 +238,33 @@
|
| |
"""
|
| |
if commit_hash:
|
| |
commit_to_check = commit_hash
|
| |
- elif self.commit:
|
| |
- commit_to_check = self.commit
|
| |
+ elif self._commit:
|
| |
+ commit_to_check = self._commit
|
| |
else:
|
| |
- raise RuntimeError('No commit hash was specified for "{0}"'.format(self.url))
|
| |
+ try:
|
| |
+ # If self._commit was None, then calling `self.commit` will resolve the ref based
|
| |
+ # on the branch
|
| |
+ return self.commit
|
| |
+ except UnprocessableEntity:
|
| |
+ # If there was an exception resolving the ref based on the branch (could be the
|
| |
+ # default branch that doesn't exist), then there is not enough information to get
|
| |
+ # the commit hash
|
| |
+ raise RuntimeError('No commit hash was specified for "{0}"'.format(self.url))
|
| |
|
| |
if self.scheme == "git":
|
| |
- log.debug('Getting the full commit hash for "{0}"'.format(self.repository))
|
| |
+ log.debug(
|
| |
+ "Getting the full commit hash on %s from %s", self.repository, commit_to_check)
|
| |
td = None
|
| |
try:
|
| |
td = tempfile.mkdtemp()
|
| |
SCM._run(["git", "clone", "-q", self.repository, td, "--bare"])
|
| |
- output = SCM._run(["git", "rev-parse", commit_to_check], chdir=td)[1]
|
| |
+ cmd = ["git", "rev-parse", commit_to_check]
|
| |
+ log.debug(
|
| |
+ "Running `%s` to get the full commit hash for %s",
|
| |
+ " ".join(cmd),
|
| |
+ commit_to_check
|
| |
+ )
|
| |
+ output = SCM._run(cmd, chdir=td)[1]
|
| |
finally:
|
| |
if td and os.path.exists(td):
|
| |
shutil.rmtree(td)
|
| |
@@ -304,79 +315,14 @@
|
| |
else:
|
| |
raise RuntimeError("is_full_commit_hash: Unhandled SCM scheme.")
|
| |
|
| |
- def is_available(self, strict=False):
|
| |
- """Check whether the scmurl is available for checkout.
|
| |
-
|
| |
- :param bool strict: When True, raise expection on error instead of
|
| |
- returning False.
|
| |
- :returns: bool -- the scmurl is available for checkout
|
| |
- """
|
| |
- td = None
|
| |
- try:
|
| |
- td = tempfile.mkdtemp()
|
| |
- self.checkout(td)
|
| |
- return True
|
| |
- except Exception:
|
| |
- if strict:
|
| |
- raise
|
| |
- return False
|
| |
- finally:
|
| |
- try:
|
| |
- if td is not None:
|
| |
- shutil.rmtree(td)
|
| |
- except Exception as e:
|
| |
- log.warning("Failed to remove temporary directory {!r}: {}".format(td, str(e)))
|
| |
-
|
| |
- @property
|
| |
- def url(self):
|
| |
- """The original scmurl."""
|
| |
- return self._url
|
| |
-
|
| |
- @url.setter
|
| |
- def url(self, s):
|
| |
- self._url = str(s)
|
| |
-
|
| |
- @property
|
| |
- def scheme(self):
|
| |
- """The SCM scheme."""
|
| |
- return self._scheme
|
| |
-
|
| |
- @scheme.setter
|
| |
- def scheme(self, s):
|
| |
- self._scheme = str(s)
|
| |
-
|
| |
- @property
|
| |
- def sourcedir(self):
|
| |
- """The SCM source directory."""
|
| |
- return self._sourcedir
|
| |
-
|
| |
- @sourcedir.setter
|
| |
- def sourcedir(self, s):
|
| |
- self._sourcedir = str(s)
|
| |
-
|
| |
- @property
|
| |
- def repository(self):
|
| |
- """The repository part of the scmurl."""
|
| |
- return self._repository
|
| |
-
|
| |
- @repository.setter
|
| |
- def repository(self, s):
|
| |
- self._repository = str(s)
|
| |
-
|
| |
@property
|
| |
def commit(self):
|
| |
"""The commit ID, for example the git hash, or None."""
|
| |
+ if not self._commit:
|
| |
+ self._commit = self.get_latest(self.branch)
|
| |
+
|
| |
return self._commit
|
| |
|
| |
@commit.setter
|
| |
def commit(self, s):
|
| |
self._commit = str(s) if s else None
|
| |
-
|
| |
- @property
|
| |
- def name(self):
|
| |
- """The module name."""
|
| |
- return self._name
|
| |
-
|
| |
- @name.setter
|
| |
- def name(self, s):
|
| |
- self._name = str(s)
|
| |
This should fix #1224
This also includes some additional cleanup in the SCM class.