Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor git descriptors #913

Open
wants to merge 40 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
35fd01e
⚡ git descriptors: less clone command
Aug 23, 2023
8c49114
🩹 git descriptors: more accurate exceptions
Aug 23, 2023
0698cca
🩹 git descriptors: _get_latest_by_pattern already includes get_latest…
Aug 23, 2023
d828e8a
♻️ git descriptors: refactor get short/long commit hash logic
Aug 23, 2023
c865dfc
🩹 git descriptors: more reliable check availability with where/which …
Aug 23, 2023
add55c5
🐛 git descriptors: commands might be a list
Aug 23, 2023
9ecba66
🩹 git descriptors: unneeded check
Aug 23, 2023
eaef0ae
🩹 git descriptors: replace tk.filesystem methods with builtins os/shu…
Aug 23, 2023
902fd4d
🐛 git descriptors: missing constant
Aug 23, 2023
3d624d8
🩹 git descriptors: clone_then_execute might have a blank command
Aug 23, 2023
3db0f81
🩹 git descriptors: remote access checks only heads
Aug 23, 2023
32abf93
🩹 git descriptors: less complex git cmd maker
Aug 23, 2023
977d780
⚡ git descriptors: add caching
Aug 23, 2023
c9b6752
🩹 git tag descriptor: move regex outside
Aug 23, 2023
2aedf8d
🐛 git tag descriptor: resolve tag version at class init
Aug 23, 2023
e069624
🩹 git tag descriptor: force download destination
Aug 23, 2023
009b5e8
✨ git branch descriptor: version is optionnal, fetch latest commit if…
Aug 23, 2023
0d26216
🩹 git branch descriptor: add get_short_version
Aug 23, 2023
0fdb493
🩹 git branch descriptor: force download destination
Aug 23, 2023
ce713ca
⬇️ py2.7 does not support fstrings
Aug 23, 2023
1903876
🎨 black
Aug 23, 2023
b41c717
🐛 git descriptor: missing copy skip list
Aug 23, 2023
0e001fd
🐛 git tag descriptor: tags are fetched on first property call
Aug 23, 2023
092913e
🐛 git tag descriptor: fix wrong tag format
Aug 23, 2023
41dd74b
🎨 black
Aug 23, 2023
2c6a85f
🐛 git descriptors: fix local repo path support
Aug 24, 2023
6bd24fe
🐛 update version tags used by tests
Aug 26, 2023
c6d86ea
🏁 fix tests: cant move readonly file on windows
Aug 26, 2023
dfbeae7
🐛 git descriptors: fix local repo path support
Aug 26, 2023
f4acf8c
⏪ rollback on using filesystem.copy_folder
Aug 26, 2023
efbb383
🏗️ git descriptors: overload private _download_local instead of public
Aug 26, 2023
868fd23
🎨 black
Aug 26, 2023
af170cf
🎨 black
Aug 26, 2023
727c0d8
🐛 git descriptors: get clone commands as list instead of str
Aug 26, 2023
48d6b10
🎨 black
Aug 26, 2023
1cdfddd
🐛 filesystem.copy_folder may fail if destination already exists and i…
Aug 26, 2023
542695c
🐛 fix latest tag in tests
Aug 26, 2023
a9c78dc
🐛 git descriptor: cache id for branch was too loose
Aug 27, 2023
168e364
🎨 black
Aug 27, 2023
608c0c0
Merge branch 'master' into feature/git_descriptor
mathbou Aug 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
342 changes: 165 additions & 177 deletions python/tank/descriptor/io_descriptor/git.py

Large diffs are not rendered by default.

110 changes: 50 additions & 60 deletions python/tank/descriptor/io_descriptor/git_branch.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@
import os
import copy

from .git import IODescriptorGit, TankGitError, _check_output
from .git import IODescriptorGit, TankGitError

from ..errors import TankDescriptorError
from ... import LogManager

from tank_vendor import six
from ...util.process import SubprocessCalledProcessError

log = LogManager.get_logger(__name__)

Expand Down Expand Up @@ -61,7 +63,7 @@ def __init__(self, descriptor_dict, sg_connection, bundle_type):
"""
# make sure all required fields are there
self._validate_descriptor(
descriptor_dict, required=["type", "path", "version", "branch"], optional=[]
descriptor_dict, required=["type", "path", "branch"], optional=["version"]
)

# call base class
Expand All @@ -73,8 +75,8 @@ def __init__(self, descriptor_dict, sg_connection, bundle_type):
# have a path to a repo
self._sg_connection = sg_connection
self._bundle_type = bundle_type
self._version = descriptor_dict.get("version")
self._branch = six.ensure_str(descriptor_dict.get("branch"))
self._version = descriptor_dict.get("version") or self.get_latest_commit()

def __str__(self):
"""
Expand All @@ -91,44 +93,43 @@ def _get_bundle_cache_path(self, bundle_cache_root):
:param bundle_cache_root: Bundle cache root path
:return: Path to bundle cache location
"""
# If the descriptor is an integer change the version to a string type
if isinstance(self._version, int):
self._version = str(self._version)
# to be MAXPATH-friendly, we only use the first seven chars
short_hash = self._version[:7]

# [email protected]:manneohrstrom/tk-hiero-publish.git -> tk-hiero-publish.git
# /full/path/to/local/repo.git -> repo.git
name = os.path.basename(self._path)

return os.path.join(bundle_cache_root, "gitbranch", name, short_hash)
return os.path.join(
bundle_cache_root, "gitbranch", name, self.get_short_version()
)

def get_version(self):
"""
Returns the version number string for this item, .e.g 'v1.2.3'
or the branch name 'master'
"""
"""Returns the full commit sha."""
return self._version

def _is_latest_commit(self, version, branch):
def get_short_version(self):
"""Returns the short commit sha."""
return self._version[:7]

def get_latest_commit(self):
"""Fetch the latest commit on a specific branch"""
output = self._execute_git_commands(
["git", "ls-remote", self._path, self._branch]
)
latest_commit = output.split("\t")[0]
return six.ensure_str(latest_commit)

def get_latest_short_commit(self):
return self.get_latest_commit()[:7]

def _is_latest_commit(self):
"""
Check if the git_branch descriptor is pointing to the
latest commit version.
"""
# first probe to check that git exists in our PATH
log.debug("Checking if the version is pointing to the latest commit...")
try:
output = _check_output(["git", "ls-remote", self._path, branch])
except:
log.exception("Unexpected error:")
raise TankGitError(
"Cannot execute the 'git' command. Please make sure that git is "
"installed on your system and that the git executable has been added to the PATH."
)
latest_commit = output.split("\t")
short_latest_commit = latest_commit[0][:7]
short_latest_commit = self.get_latest_short_commit()

if short_latest_commit != version[:7]:
if short_latest_commit != self.get_short_version():
return False
log.debug(
"This version is pointing to the latest commit %s, lets enable shallow clones"
Expand All @@ -137,38 +138,29 @@ def _is_latest_commit(self, version, branch):

return True

def _download_local(self, destination_path):
def _download_local(self, target_path):
"""
Retrieves this version to local repo.
Will exit early if app already exists local.

This will connect to remote git repositories.
Depending on how git is configured, https repositories
requiring credentials may result in a shell opening up
requesting username and password.

The git repo will be cloned into the local cache and
will then be adjusted to point at the relevant commit.

:param destination_path: The destination path on disk to which
the git branch descriptor is to be downloaded to.
Downloads the data represented by the descriptor into the primary bundle
cache path.
"""
log.info("Downloading {}:{}".format(self.get_system_name(), self._version))

depth = None
is_latest_commit = self._is_latest_commit(self._version, self._branch)
is_latest_commit = self._is_latest_commit()
if is_latest_commit:
depth = 1
try:
# clone the repo, switch to the given branch
# then reset to the given commit
commands = ['checkout -q "%s"' % self._version]
commands = [f"checkout", "-q", self._version]
self._clone_then_execute_git_commands(
destination_path,
target_path,
commands,
depth=depth,
ref=self._branch,
is_latest_commit=is_latest_commit,
)
except Exception as e:
except (TankGitError, OSError, SubprocessCalledProcessError) as e:
raise TankDescriptorError(
"Could not download %s, branch %s, "
"commit %s: %s" % (self._path, self._branch, self._version, e)
Expand Down Expand Up @@ -206,24 +198,9 @@ def get_latest_version(self, constraint_pattern=None):
"Latest version will be used." % self
)

try:
# clone the repo, get the latest commit hash
# for the given branch
commands = [
'checkout -q "%s"' % self._branch,
"log -n 1 \"%s\" --pretty=format:'%%H'" % self._branch,
]
git_hash = self._tmp_clone_then_execute_git_commands(commands)

except Exception as e:
raise TankDescriptorError(
"Could not get latest commit for %s, "
"branch %s: %s" % (self._path, self._branch, e)
)

# make a new descriptor
new_loc_dict = copy.deepcopy(self._descriptor_dict)
new_loc_dict["version"] = six.ensure_str(git_hash)
new_loc_dict["version"] = self.get_latest_commit()
desc = IODescriptorGitBranch(
new_loc_dict, self._sg_connection, self._bundle_type
)
Expand Down Expand Up @@ -252,3 +229,16 @@ def get_latest_cached_version(self, constraint_pattern=None):
else:
# no cached version exists
return None

def _fetch_local_data(self, path):
version = self._execute_git_commands(
["git", "-C", os.path.normpath(path), "rev-parse", "HEAD"]
)

branch = self._execute_git_commands(
["git", "-C", os.path.normpath(path), "branch", "--show-current"]
)

local_data = {"version": version, "branch": branch}
log.debug("Get local repo data: {}".format(local_data))
return local_data
Loading
Loading