Coverage for src/idiap_devtools/gitlab/__init__.py: 38%

34 statements  

« prev     ^ index     » next       coverage.py v7.4.3, created at 2024-04-22 14:46 +0200

1# Copyright © 2022 Idiap Research Institute <contact@idiap.ch> 

2# 

3# SPDX-License-Identifier: BSD-3-Clause 

4"""Utilities to interact with GitLab.""" 

5 

6import logging 

7import os 

8import pathlib 

9import shutil 

10import tarfile 

11import tempfile 

12 

13from io import BytesIO 

14 

15import gitlab 

16import gitlab.v4.objects 

17 

18logger = logging.getLogger(__name__) 

19 

20 

21def get_gitlab_instance() -> gitlab.Gitlab: 

22 """Return an instance of the gitlab object for remote operations.""" 

23 # tries to figure if we can authenticate using a global configuration 

24 cfgs = [ 

25 pathlib.Path(k).expanduser() 

26 for k in ["~/.python-gitlab.cfg", "/etc/python-gitlab.cfg"] 

27 ] 

28 if any([k.exists() for k in cfgs]): 

29 gl = gitlab.Gitlab.from_config("idiap", [str(k) for k in cfgs if k.exists()]) 

30 else: # ask the user for a token or use one from the current runner 

31 server = os.environ.get("CI_SERVER_URL", "https://gitlab.idiap.ch") 

32 token = os.environ.get("CI_JOB_TOKEN") 

33 if token is None: 

34 logger.debug( 

35 "Did not find any of %s nor CI_JOB_TOKEN is defined. " 

36 "Asking for user token on the command line...", 

37 "|".join([str(k) for k in cfgs]), 

38 ) 

39 token = input(f"{server} (private) token: ") 

40 gl = gitlab.Gitlab(server, private_token=token, api_version="4") 

41 

42 return gl 

43 

44 

45def download_path( 

46 package: gitlab.v4.objects.projects.Project, 

47 path: str, 

48 output: pathlib.Path | None = None, 

49 ref: str | None = None, 

50) -> None: 

51 """Download paths from gitlab, with an optional recurse. 

52 

53 This method will download an archive of the repository from chosen 

54 reference, and then it will search inside the zip blob for the path to be 

55 copied into output. It uses :py:class:`zipfile.ZipFile` to do this search. 

56 This method will not be very efficient for larger repository references, 

57 but works recursively by default. 

58 

59 Args: 

60 

61 package: the gitlab package object to use (should be pre-fetched) 

62 

63 path: the path on the project to download 

64 

65 output: where to place the path to be downloaded - if not provided, use 

66 the basename of ``path`` as storage point with respect to the current 

67 directory 

68 

69 ref: the name of the git reference (branch, tag or commit hash) to use. 

70 If None specified, defaults to the default branch of the input package 

71 """ 

72 

73 output = output or pathlib.Path(os.path.realpath(os.curdir)) 

74 ref = ref or package.default_branch 

75 

76 logger.debug( 

77 'Downloading archive of "%s" from "%s"...', 

78 ref, 

79 package.attributes["path_with_namespace"], 

80 ) 

81 archive = package.repository_archive(ref=ref) 

82 logger.debug("Archive has %d bytes", len(archive)) 

83 logger.debug('Searching for "%s" within archive...', path) 

84 

85 with tempfile.TemporaryDirectory() as d: 

86 with tarfile.open(fileobj=BytesIO(archive), mode="r:gz") as f: 

87 f.extractall(path=d) 

88 

89 # move stuff to "output" 

90 basedir = os.listdir(d)[0] 

91 shutil.move(pathlib.Path(d) / basedir / path, output)