[Notes] [Git][BuildStream/buildstream][valentindavid/git_describe_tracking] 5 commits: _yamlcache.py: Use a project's junction name if present



Title: GitLab

Valentin David pushed to branch valentindavid/git_describe_tracking at BuildStream / buildstream

Commits:

27 changed files:

Changes:

  • NEWS
    ... ... @@ -74,6 +74,10 @@ buildstream 1.3.1
    74 74
       o Add sandbox API for command batching and use it for build, script, and
    
    75 75
         compose elements.
    
    76 76
     
    
    77
    +  o BREAKING CHANGE: The `git` plugin does not create a local `.git`
    
    78
    +    repository by default.  If `git describe` is required to work, the
    
    79
    +    plugin has now a tag tracking feature instead. This can be enabled
    
    80
    +    by setting 'track-tags'.
    
    77 81
     
    
    78 82
     =================
    
    79 83
     buildstream 1.1.5
    

  • buildstream/_versions.py
    ... ... @@ -23,7 +23,7 @@
    23 23
     # This version is bumped whenever enhancements are made
    
    24 24
     # to the `project.conf` format or the core element format.
    
    25 25
     #
    
    26
    -BST_FORMAT_VERSION = 18
    
    26
    +BST_FORMAT_VERSION = 19
    
    27 27
     
    
    28 28
     
    
    29 29
     # The base BuildStream artifact version
    

  • buildstream/_yamlcache.py
    ... ... @@ -68,7 +68,7 @@ class YamlCache():
    68 68
         #    (bool): Whether the file is cached.
    
    69 69
         def is_cached(self, project, filepath):
    
    70 70
             cache_path = self._get_filepath(project, filepath)
    
    71
    -        project_name = project.name if project else ""
    
    71
    +        project_name = self.get_project_name(project)
    
    72 72
             try:
    
    73 73
                 project_cache = self._project_caches[project_name]
    
    74 74
                 if cache_path in project_cache.elements:
    
    ... ... @@ -167,7 +167,7 @@ class YamlCache():
    167 167
         #    value (decorated dict): The data to put into the cache.
    
    168 168
         def put_from_key(self, project, filepath, key, value):
    
    169 169
             cache_path = self._get_filepath(project, filepath)
    
    170
    -        project_name = project.name if project else ""
    
    170
    +        project_name = self.get_project_name(project)
    
    171 171
             try:
    
    172 172
                 project_cache = self._project_caches[project_name]
    
    173 173
             except KeyError:
    
    ... ... @@ -237,7 +237,7 @@ class YamlCache():
    237 237
         #    (decorated dict): The parsed yaml from the cache, or None if the file isn't in the cache.
    
    238 238
         def _get(self, project, filepath, key):
    
    239 239
             cache_path = self._get_filepath(project, filepath)
    
    240
    -        project_name = project.name if project else ""
    
    240
    +        project_name = self.get_project_name(project)
    
    241 241
             try:
    
    242 242
                 project_cache = self._project_caches[project_name]
    
    243 243
                 try:
    
    ... ... @@ -253,6 +253,30 @@ class YamlCache():
    253 253
                 pass
    
    254 254
             return None
    
    255 255
     
    
    256
    +    # get_project_name():
    
    257
    +    #
    
    258
    +    # Gets a name appropriate for Project. Projects must use their junction's
    
    259
    +    # name if present, otherwise elements with the same contents under the
    
    260
    +    # same path with identically-named projects are considered the same yaml
    
    261
    +    # object, despite existing in different Projects.
    
    262
    +    #
    
    263
    +    # Args:
    
    264
    +    #    project (Project): The project this file is in, or None.
    
    265
    +    #
    
    266
    +    # Returns:
    
    267
    +    #    (str): The project's junction's name if present, the project's name,
    
    268
    +    #           or an empty string if there is no project
    
    269
    +    @staticmethod
    
    270
    +    def get_project_name(project):
    
    271
    +        if project:
    
    272
    +            if project.junction:
    
    273
    +                project_name = project.junction.name
    
    274
    +            else:
    
    275
    +                project_name = project.name
    
    276
    +        else:
    
    277
    +            project_name = ""
    
    278
    +        return project_name
    
    279
    +
    
    256 280
     
    
    257 281
     CachedProject = namedtuple('CachedProject', ['elements'])
    
    258 282
     
    
    ... ... @@ -287,7 +311,7 @@ class BstPickler(pickle.Pickler):
    287 311
             if isinstance(obj, _yaml.ProvenanceFile):
    
    288 312
                 if obj.project:
    
    289 313
                     # ProvenanceFile's project object cannot be stored as it is.
    
    290
    -                project_tag = obj.project.name
    
    314
    +                project_tag = YamlCache.get_project_name(obj.project)
    
    291 315
                     # ProvenanceFile's filename must be stored relative to the
    
    292 316
                     # project, as the project dir may move.
    
    293 317
                     name = os.path.relpath(obj.name, obj.project.directory)
    
    ... ... @@ -319,14 +343,14 @@ class BstUnpickler(pickle.Unpickler):
    319 343
     
    
    320 344
                 if project_tag is not None:
    
    321 345
                     for p in self._context.get_projects():
    
    322
    -                    if project_tag == p.name:
    
    346
    +                    if YamlCache.get_project_name(p) == project_tag:
    
    323 347
                             project = p
    
    324 348
                             break
    
    325 349
     
    
    326 350
                     name = os.path.join(project.directory, tagged_name)
    
    327 351
     
    
    328 352
                     if not project:
    
    329
    -                    projects = [p.name for p in self._context.get_projects()]
    
    353
    +                    projects = [YamlCache.get_project_name(p) for p in self._context.get_projects()]
    
    330 354
                         raise pickle.UnpicklingError("No project with name {} found in {}"
    
    331 355
                                                      .format(project_tag, projects))
    
    332 356
                 else:
    

  • buildstream/plugins/sources/git.py
    ... ... @@ -76,6 +76,24 @@ git - stage files from a git repository
    76 76
            url: upstream:baz.git
    
    77 77
            checkout: False
    
    78 78
     
    
    79
    +   # Enable tag tracking. This allows to create a dummy shallow
    
    80
    +   # repository with necessary tag informations required to run 'git
    
    81
    +   # describe'. Default is 'False'.
    
    82
    +   track-tags: True
    
    83
    +
    
    84
    +   # A dummy shallow repository is created with the following tags.
    
    85
    +   # Tags should not be edited by hand but rather be populated by
    
    86
    +   # 'bst track' with 'track-tags' set to True. These tags can
    
    87
    +   # also be stored in 'project.refs'. If you do not want to track
    
    88
    +   # the 'ref' at the same time, set 'track' to the wanted commit.
    
    89
    +   tags:
    
    90
    +   - tag: lightweight-example
    
    91
    +     commit: 04ad0dc656cb7cc6feb781aa13bdbf1d67d0af78
    
    92
    +     annotated: false
    
    93
    +   - tag: annotated-example
    
    94
    +     commit: 10abe77fe8d77385d86f225b503d9185f4ef7f3a
    
    95
    +     annotated: true
    
    96
    +
    
    79 97
     See :ref:`built-in functionality doumentation <core_source_builtins>` for
    
    80 98
     details on common configuration options for sources.
    
    81 99
     
    
    ... ... @@ -95,6 +113,7 @@ import re
    95 113
     import shutil
    
    96 114
     from collections.abc import Mapping
    
    97 115
     from io import StringIO
    
    116
    +from tempfile import TemporaryFile
    
    98 117
     
    
    99 118
     from configparser import RawConfigParser
    
    100 119
     
    
    ... ... @@ -115,13 +134,14 @@ INCONSISTENT_SUBMODULE = "inconsistent-submodules"
    115 134
     #
    
    116 135
     class GitMirror(SourceFetcher):
    
    117 136
     
    
    118
    -    def __init__(self, source, path, url, ref, *, primary=False):
    
    137
    +    def __init__(self, source, path, url, ref, *, primary=False, tags=[]):
    
    119 138
     
    
    120 139
             super().__init__()
    
    121 140
             self.source = source
    
    122 141
             self.path = path
    
    123 142
             self.url = url
    
    124 143
             self.ref = ref
    
    144
    +        self.tags = tags
    
    125 145
             self.primary = primary
    
    126 146
             self.mirror = os.path.join(source.get_mirror_directory(), utils.url_directory_name(url))
    
    127 147
             self.mark_download_url(url)
    
    ... ... @@ -214,7 +234,7 @@ class GitMirror(SourceFetcher):
    214 234
                 raise SourceError("{}: expected ref '{}' was not found in git repository: '{}'"
    
    215 235
                                   .format(self.source, self.ref, self.url))
    
    216 236
     
    
    217
    -    def latest_commit(self, tracking):
    
    237
    +    def latest_commit_with_tags(self, tracking, track_tags=False):
    
    218 238
             _, output = self.source.check_output(
    
    219 239
                 [self.source.host_git, 'rev-parse', tracking],
    
    220 240
                 fail="Unable to find commit for specified branch name '{}'".format(tracking),
    
    ... ... @@ -230,7 +250,28 @@ class GitMirror(SourceFetcher):
    230 250
                 if exit_code == 0:
    
    231 251
                     ref = output.rstrip('\n')
    
    232 252
     
    
    233
    -        return ref
    
    253
    +        if not track_tags:
    
    254
    +            return ref, []
    
    255
    +
    
    256
    +        tags = set()
    
    257
    +        for options in [[], ['--first-parent'], ['--tags'], ['--tags', '--first-parent']]:
    
    258
    +            exit_code, output = self.source.check_output(
    
    259
    +                [self.source.host_git, 'describe', '--abbrev=0', ref] + options,
    
    260
    +                cwd=self.mirror)
    
    261
    +            if exit_code == 0:
    
    262
    +                tag = output.strip()
    
    263
    +                _, commit_ref = self.source.check_output(
    
    264
    +                    [self.source.host_git, 'rev-parse', tag + '^{commit}'],
    
    265
    +                    fail="Unable to resolve tag '{}'".format(tag),
    
    266
    +                    cwd=self.mirror)
    
    267
    +                exit_code = self.source.call(
    
    268
    +                    [self.source.host_git, 'cat-file', 'tag', tag],
    
    269
    +                    cwd=self.mirror)
    
    270
    +                annotated = (exit_code == 0)
    
    271
    +
    
    272
    +                tags.add((tag, commit_ref.strip(), annotated))
    
    273
    +
    
    274
    +        return ref, list(tags)
    
    234 275
     
    
    235 276
         def stage(self, directory, track=None):
    
    236 277
             fullpath = os.path.join(directory, self.path)
    
    ... ... @@ -246,13 +287,15 @@ class GitMirror(SourceFetcher):
    246 287
                              fail="Failed to checkout git ref {}".format(self.ref),
    
    247 288
                              cwd=fullpath)
    
    248 289
     
    
    290
    +        # Remove .git dir
    
    291
    +        shutil.rmtree(os.path.join(fullpath, ".git"))
    
    292
    +
    
    293
    +        self._rebuild_git(fullpath)
    
    294
    +
    
    249 295
             # Check that the user specified ref exists in the track if provided & not already tracked
    
    250 296
             if track:
    
    251 297
                 self.assert_ref_in_track(fullpath, track)
    
    252 298
     
    
    253
    -        # Remove .git dir
    
    254
    -        shutil.rmtree(os.path.join(fullpath, ".git"))
    
    255
    -
    
    256 299
         def init_workspace(self, directory, track=None):
    
    257 300
             fullpath = os.path.join(directory, self.path)
    
    258 301
             url = self.source.translate_url(self.url)
    
    ... ... @@ -359,6 +402,78 @@ class GitMirror(SourceFetcher):
    359 402
                              .format(self.source, self.ref, track, self.url),
    
    360 403
                              detail=detail, warning_token=CoreWarnings.REF_NOT_IN_TRACK)
    
    361 404
     
    
    405
    +    def _rebuild_git(self, fullpath):
    
    406
    +        if not self.tags:
    
    407
    +            return
    
    408
    +
    
    409
    +        with self.source.tempdir() as tmpdir:
    
    410
    +            included = set()
    
    411
    +            shallow = set()
    
    412
    +            for _, commit_ref, _ in self.tags:
    
    413
    +
    
    414
    +                _, out = self.source.check_output([self.source.host_git, 'rev-list',
    
    415
    +                                                   '--boundary', '{}..{}'.format(commit_ref, self.ref)],
    
    416
    +                                                  fail="Failed to get git history {}..{} in directory: {}"
    
    417
    +                                                  .format(commit_ref, self.ref, fullpath),
    
    418
    +                                                  fail_temporarily=True,
    
    419
    +                                                  cwd=self.mirror)
    
    420
    +                for line in out.splitlines():
    
    421
    +                    rev = line.lstrip('-')
    
    422
    +                    if line[0] == '-':
    
    423
    +                        shallow.add(rev)
    
    424
    +                    else:
    
    425
    +                        included.add(rev)
    
    426
    +
    
    427
    +            shallow -= included
    
    428
    +            included |= shallow
    
    429
    +
    
    430
    +            self.source.call([self.source.host_git, 'init'],
    
    431
    +                             fail="Cannot initialize git repository: {}".format(fullpath),
    
    432
    +                             cwd=fullpath)
    
    433
    +
    
    434
    +            for rev in included:
    
    435
    +                with TemporaryFile(dir=tmpdir) as commit_file:
    
    436
    +                    self.source.call([self.source.host_git, 'cat-file', 'commit', rev],
    
    437
    +                                     stdout=commit_file,
    
    438
    +                                     fail="Failed to get commit {}".format(rev),
    
    439
    +                                     cwd=self.mirror)
    
    440
    +                    commit_file.seek(0, 0)
    
    441
    +                    self.source.call([self.source.host_git, 'hash-object', '-w', '-t', 'commit', '--stdin'],
    
    442
    +                                     stdin=commit_file,
    
    443
    +                                     fail="Failed to add commit object {}".format(rev),
    
    444
    +                                     cwd=fullpath)
    
    445
    +
    
    446
    +            with open(os.path.join(fullpath, '.git', 'shallow'), 'w') as shallow_file:
    
    447
    +                for rev in shallow:
    
    448
    +                    shallow_file.write('{}\n'.format(rev))
    
    449
    +
    
    450
    +            for tag, commit_ref, annotated in self.tags:
    
    451
    +                if annotated:
    
    452
    +                    with TemporaryFile(dir=tmpdir) as tag_file:
    
    453
    +                        tag_data = 'object {}\ntype commit\ntag {}\n'.format(commit_ref, tag)
    
    454
    +                        tag_file.write(tag_data.encode('ascii'))
    
    455
    +                        tag_file.seek(0, 0)
    
    456
    +                        _, tag_ref = self.source.check_output(
    
    457
    +                            [self.source.host_git, 'hash-object', '-w', '-t',
    
    458
    +                             'tag', '--stdin'],
    
    459
    +                            stdin=tag_file,
    
    460
    +                            fail="Failed to add tag object {}".format(tag),
    
    461
    +                            cwd=fullpath)
    
    462
    +
    
    463
    +                    self.source.call([self.source.host_git, 'tag', tag, tag_ref.strip()],
    
    464
    +                                     fail="Failed to tag: {}".format(tag),
    
    465
    +                                     cwd=fullpath)
    
    466
    +                else:
    
    467
    +                    self.source.call([self.source.host_git, 'tag', tag, commit_ref],
    
    468
    +                                     fail="Failed to tag: {}".format(tag),
    
    469
    +                                     cwd=fullpath)
    
    470
    +
    
    471
    +            with open(os.path.join(fullpath, '.git', 'HEAD'), 'w') as head:
    
    472
    +                self.source.call([self.source.host_git, 'rev-parse', self.ref],
    
    473
    +                                 stdout=head,
    
    474
    +                                 fail="Failed to parse commit {}".format(self.ref),
    
    475
    +                                 cwd=self.mirror)
    
    476
    +
    
    362 477
     
    
    363 478
     class GitSource(Source):
    
    364 479
         # pylint: disable=attribute-defined-outside-init
    
    ... ... @@ -366,11 +481,20 @@ class GitSource(Source):
    366 481
         def configure(self, node):
    
    367 482
             ref = self.node_get_member(node, str, 'ref', None)
    
    368 483
     
    
    369
    -        config_keys = ['url', 'track', 'ref', 'submodules', 'checkout-submodules', 'ref-format']
    
    484
    +        config_keys = ['url', 'track', 'ref', 'submodules',
    
    485
    +                       'checkout-submodules', 'ref-format',
    
    486
    +                       'track-tags', 'tags']
    
    370 487
             self.node_validate(node, config_keys + Source.COMMON_CONFIG_KEYS)
    
    371 488
     
    
    489
    +        tags_node = self.node_get_member(node, list, 'tags', [])
    
    490
    +        for tag_node in tags_node:
    
    491
    +            self.node_validate(tag_node, ['tag', 'commit', 'annotated'])
    
    492
    +
    
    493
    +        tags = self._load_tags(node)
    
    494
    +        self.track_tags = self.node_get_member(node, bool, 'track-tags', False)
    
    495
    +
    
    372 496
             self.original_url = self.node_get_member(node, str, 'url')
    
    373
    -        self.mirror = GitMirror(self, '', self.original_url, ref, primary=True)
    
    497
    +        self.mirror = GitMirror(self, '', self.original_url, ref, tags=tags, primary=True)
    
    374 498
             self.tracking = self.node_get_member(node, str, 'track', None)
    
    375 499
     
    
    376 500
             self.ref_format = self.node_get_member(node, str, 'ref-format', 'sha1')
    
    ... ... @@ -417,6 +541,10 @@ class GitSource(Source):
    417 541
             # the ref, if the user changes the alias to fetch the same sources
    
    418 542
             # from another location, it should not affect the cache key.
    
    419 543
             key = [self.original_url, self.mirror.ref]
    
    544
    +        if self.mirror.tags:
    
    545
    +            tags = {}
    
    546
    +            tags = {tag: (commit, annotated) for tag, commit, annotated in self.mirror.tags}
    
    547
    +            key.append({'tags': tags})
    
    420 548
     
    
    421 549
             # Only modify the cache key with checkout_submodules if it's something
    
    422 550
             # other than the default behaviour.
    
    ... ... @@ -442,12 +570,33 @@ class GitSource(Source):
    442 570
     
    
    443 571
         def load_ref(self, node):
    
    444 572
             self.mirror.ref = self.node_get_member(node, str, 'ref', None)
    
    573
    +        self.mirror.tags = self._load_tags(node)
    
    445 574
     
    
    446 575
         def get_ref(self):
    
    447
    -        return self.mirror.ref
    
    448
    -
    
    449
    -    def set_ref(self, ref, node):
    
    450
    -        node['ref'] = self.mirror.ref = ref
    
    576
    +        return self.mirror.ref, self.mirror.tags
    
    577
    +
    
    578
    +    def set_ref(self, ref_data, node):
    
    579
    +        if not ref_data:
    
    580
    +            self.mirror.ref = None
    
    581
    +            if 'ref' in node:
    
    582
    +                del node['ref']
    
    583
    +            self.mirror.tags = []
    
    584
    +            if 'tags' in node:
    
    585
    +                del node['tags']
    
    586
    +        else:
    
    587
    +            ref, tags = ref_data
    
    588
    +            node['ref'] = self.mirror.ref = ref
    
    589
    +            self.mirror.tags = tags
    
    590
    +            if tags:
    
    591
    +                node['tags'] = []
    
    592
    +                for tag, commit_ref, annotated in tags:
    
    593
    +                    data = {'tag': tag,
    
    594
    +                            'commit': commit_ref,
    
    595
    +                            'annotated': annotated}
    
    596
    +                    node['tags'].append(data)
    
    597
    +            else:
    
    598
    +                if 'tags' in node:
    
    599
    +                    del node['tags']
    
    451 600
     
    
    452 601
         def track(self):
    
    453 602
     
    
    ... ... @@ -470,7 +619,7 @@ class GitSource(Source):
    470 619
                 self.mirror._fetch()
    
    471 620
     
    
    472 621
                 # Update self.mirror.ref and node.ref from the self.tracking branch
    
    473
    -            ret = self.mirror.latest_commit(self.tracking)
    
    622
    +            ret = self.mirror.latest_commit_with_tags(self.tracking, self.track_tags)
    
    474 623
     
    
    475 624
             # Set tracked attribute, parameter for if self.mirror.assert_ref_in_track is needed
    
    476 625
             self.tracked = True
    
    ... ... @@ -556,6 +705,16 @@ class GitSource(Source):
    556 705
     
    
    557 706
             self.submodules = submodules
    
    558 707
     
    
    708
    +    def _load_tags(self, node):
    
    709
    +        tags = []
    
    710
    +        tags_node = self.node_get_member(node, list, 'tags', [])
    
    711
    +        for tag_node in tags_node:
    
    712
    +            tag = self.node_get_member(tag_node, str, 'tag')
    
    713
    +            commit_ref = self.node_get_member(tag_node, str, 'commit')
    
    714
    +            annotated = self.node_get_member(tag_node, bool, 'annotated')
    
    715
    +            tags.append((tag, commit_ref, annotated))
    
    716
    +        return tags
    
    717
    +
    
    559 718
     
    
    560 719
     # Plugin entry point
    
    561 720
     def setup():
    

  • tests/cachekey/project/sources/git3.bst
    1
    +kind: import
    
    2
    +sources:
    
    3
    +- kind: git
    
    4
    +  url: https://example.com/git/repo.git
    
    5
    +  ref: 6ac68af3e80b7b17c23a3c65233043550a7fa685
    
    6
    +  tags:
    
    7
    +  - tag: lightweight
    
    8
    +    commit: 0a3917d57477ee9afe7be49a0e8a76f56d176df1
    
    9
    +    annotated: false
    
    10
    +  - tag: annotated
    
    11
    +    commit: 68c7f0bd386684742c41ec2a54ce2325e3922f6c
    
    12
    +    annotated: true

  • tests/cachekey/project/sources/git3.expected
    1
    +6a25f539bd8629a36399c58efd2f5c9c117feb845076a37dc321b55d456932b6
    \ No newline at end of file

  • tests/cachekey/project/target.bst
    ... ... @@ -7,6 +7,7 @@ depends:
    7 7
     - sources/bzr1.bst
    
    8 8
     - sources/git1.bst
    
    9 9
     - sources/git2.bst
    
    10
    +- sources/git3.bst
    
    10 11
     - sources/local1.bst
    
    11 12
     - sources/local2.bst
    
    12 13
     - sources/ostree1.bst
    

  • tests/cachekey/project/target.expected
    1
    -125d9e7dcf4f49e5f80d85b7f144b43ed43186064afc2e596e57f26cce679cf5
    \ No newline at end of file
    1
    +bc99c288f855ac2619787f0067223f7812d2e10a9d2c7f2bf47de7113c0fd25c
    \ No newline at end of file

  • tests/loader/junctions.py
    ... ... @@ -47,6 +47,16 @@ def test_simple_build(cli, tmpdir, datafiles):
    47 47
         assert(os.path.exists(os.path.join(checkoutdir, 'foo.txt')))
    
    48 48
     
    
    49 49
     
    
    50
    +@pytest.mark.datafiles(DATA_DIR)
    
    51
    +def test_build_of_same_junction_used_twice(cli, tmpdir, datafiles):
    
    52
    +    project = os.path.join(str(datafiles), 'inconsistent-names')
    
    53
    +
    
    54
    +    # Check we can build a project that contains the same junction
    
    55
    +    # that is used twice, but named differently
    
    56
    +    result = cli.run(project=project, args=['build', 'target.bst'])
    
    57
    +    assert result.exit_code == 0
    
    58
    +
    
    59
    +
    
    50 60
     @pytest.mark.datafiles(DATA_DIR)
    
    51 61
     def test_nested_simple(cli, tmpdir, datafiles):
    
    52 62
         foo = os.path.join(str(datafiles), 'foo')
    

  • tests/loader/junctions/inconsistent-names/elements/junction-A.bst
    1
    +kind: junction
    
    2
    +sources:
    
    3
    +- kind: local
    
    4
    +  path: junctionA

  • tests/loader/junctions/inconsistent-names/elements/junction-B-diff-name.bst
    1
    +kind: junction
    
    2
    +sources:
    
    3
    +- kind: local
    
    4
    +  path: junctionA/junctionB

  • tests/loader/junctions/inconsistent-names/elements/target.bst
    1
    +kind: import
    
    2
    +sources:
    
    3
    +- kind: local
    
    4
    +  path: files/foo
    
    5
    +depends:
    
    6
    +- filename: lib2.bst
    
    7
    +  junction: junction-B-diff-name.bst
    
    8
    +- filename: lib.bst
    
    9
    +  junction: junction-A.bst

  • tests/loader/junctions/inconsistent-names/files/foo

  • tests/loader/junctions/inconsistent-names/junctionA/elements/app.bst
    1
    +kind: import
    
    2
    +sources:
    
    3
    +- kind: local
    
    4
    +  path: files/app
    
    5
    +depends:
    
    6
    +- lib.bst

  • tests/loader/junctions/inconsistent-names/junctionA/elements/junction-B.bst
    1
    +kind: junction
    
    2
    +sources:
    
    3
    +- kind: local
    
    4
    +  path: junctionB

  • tests/loader/junctions/inconsistent-names/junctionA/elements/lib.bst
    1
    +kind: import
    
    2
    +sources:
    
    3
    +- kind: local
    
    4
    +  path: files/lib
    
    5
    +depends:
    
    6
    +- filename: base.bst
    
    7
    +  junction: junction-B.bst

  • tests/loader/junctions/inconsistent-names/junctionA/files/app

  • tests/loader/junctions/inconsistent-names/junctionA/files/lib

  • tests/loader/junctions/inconsistent-names/junctionA/junctionB/base/baseimg

  • tests/loader/junctions/inconsistent-names/junctionA/junctionB/elements/base.bst
    1
    +kind: import
    
    2
    +sources:
    
    3
    +- kind: local
    
    4
    +  path: base

  • tests/loader/junctions/inconsistent-names/junctionA/junctionB/elements/lib2.bst
    1
    +kind: import
    
    2
    +sources:
    
    3
    +- kind: local
    
    4
    +  path: files/lib2
    
    5
    +depends:
    
    6
    +- base.bst

  • tests/loader/junctions/inconsistent-names/junctionA/junctionB/files/lib2

  • tests/loader/junctions/inconsistent-names/junctionA/junctionB/project.conf
    1
    +# Unique project name
    
    2
    +name: projectB
    
    3
    +
    
    4
    +# Subdirectory where elements are stored
    
    5
    +element-path: elements

  • tests/loader/junctions/inconsistent-names/junctionA/project.conf
    1
    +# Unique project name
    
    2
    +name: projectA
    
    3
    +
    
    4
    +# Subdirectory where elements are stored
    
    5
    +element-path: elements

  • tests/loader/junctions/inconsistent-names/project.conf
    1
    +# Unique project name
    
    2
    +name: inconsistent-names
    
    3
    +
    
    4
    +# Subdirectory where elements are stored
    
    5
    +element-path: elements

  • tests/sources/git.py
    ... ... @@ -22,6 +22,7 @@
    22 22
     
    
    23 23
     import os
    
    24 24
     import pytest
    
    25
    +import subprocess
    
    25 26
     
    
    26 27
     from buildstream._exceptions import ErrorDomain
    
    27 28
     from buildstream import _yaml
    
    ... ... @@ -523,3 +524,155 @@ def test_track_fetch(cli, tmpdir, datafiles, ref_format, tag, extra_commit):
    523 524
         # Fetch it
    
    524 525
         result = cli.run(project=project, args=['fetch', 'target.bst'])
    
    525 526
         result.assert_success()
    
    527
    +
    
    528
    +
    
    529
    +@pytest.mark.skipif(HAVE_GIT is False, reason="git is not available")
    
    530
    +@pytest.mark.datafiles(os.path.join(DATA_DIR, 'template'))
    
    531
    +@pytest.mark.parametrize("ref_storage", [('inline'), ('project.refs')])
    
    532
    +@pytest.mark.parametrize("tag_type", [('annotated'), ('lightweight')])
    
    533
    +def test_git_describe(cli, tmpdir, datafiles, ref_storage, tag_type):
    
    534
    +    project = str(datafiles)
    
    535
    +
    
    536
    +    project_config = _yaml.load(os.path.join(project, 'project.conf'))
    
    537
    +    project_config['ref-storage'] = ref_storage
    
    538
    +    _yaml.dump(_yaml.node_sanitize(project_config), os.path.join(project, 'project.conf'))
    
    539
    +
    
    540
    +    repofiles = os.path.join(str(tmpdir), 'repofiles')
    
    541
    +    os.makedirs(repofiles, exist_ok=True)
    
    542
    +    file0 = os.path.join(repofiles, 'file0')
    
    543
    +    with open(file0, 'w') as f:
    
    544
    +        f.write('test\n')
    
    545
    +
    
    546
    +    repo = create_repo('git', str(tmpdir))
    
    547
    +
    
    548
    +    def tag(name):
    
    549
    +        if tag_type == 'annotated':
    
    550
    +            repo.add_annotated_tag(name, name)
    
    551
    +        else:
    
    552
    +            repo.add_tag(name)
    
    553
    +
    
    554
    +    ref = repo.create(repofiles)
    
    555
    +    tag('uselesstag')
    
    556
    +
    
    557
    +    file1 = os.path.join(str(tmpdir), 'file1')
    
    558
    +    with open(file1, 'w') as f:
    
    559
    +        f.write('test\n')
    
    560
    +    repo.add_file(file1)
    
    561
    +    tag('tag1')
    
    562
    +
    
    563
    +    file2 = os.path.join(str(tmpdir), 'file2')
    
    564
    +    with open(file2, 'w') as f:
    
    565
    +        f.write('test\n')
    
    566
    +    repo.branch('branch2')
    
    567
    +    repo.add_file(file2)
    
    568
    +    tag('tag2')
    
    569
    +
    
    570
    +    repo.checkout('master')
    
    571
    +    file3 = os.path.join(str(tmpdir), 'file3')
    
    572
    +    with open(file3, 'w') as f:
    
    573
    +        f.write('test\n')
    
    574
    +    repo.add_file(file3)
    
    575
    +
    
    576
    +    repo.merge('branch2')
    
    577
    +
    
    578
    +    config = repo.source_config()
    
    579
    +    config['track'] = repo.latest_commit()
    
    580
    +    config['track-tags'] = True
    
    581
    +
    
    582
    +    # Write out our test target
    
    583
    +    element = {
    
    584
    +        'kind': 'import',
    
    585
    +        'sources': [
    
    586
    +            config
    
    587
    +        ],
    
    588
    +    }
    
    589
    +    element_path = os.path.join(project, 'target.bst')
    
    590
    +    _yaml.dump(element, element_path)
    
    591
    +
    
    592
    +    if ref_storage == 'inline':
    
    593
    +        result = cli.run(project=project, args=['track', 'target.bst'])
    
    594
    +        result.assert_success()
    
    595
    +    else:
    
    596
    +        result = cli.run(project=project, args=['track', 'target.bst', '--deps', 'all'])
    
    597
    +        result.assert_success()
    
    598
    +
    
    599
    +    if ref_storage == 'inline':
    
    600
    +        element = _yaml.load(element_path)
    
    601
    +        tags = _yaml.node_sanitize(element['sources'][0]['tags'])
    
    602
    +        assert len(tags) == 2
    
    603
    +        for tag in tags:
    
    604
    +            assert 'tag' in tag
    
    605
    +            assert 'commit' in tag
    
    606
    +            assert 'annotated' in tag
    
    607
    +            assert tag['annotated'] == (tag_type == 'annotated')
    
    608
    +
    
    609
    +        assert set([(tag['tag'], tag['commit']) for tag in tags]) == set([('tag1', repo.rev_parse('tag1^{commit}')),
    
    610
    +                                                                          ('tag2', repo.rev_parse('tag2^{commit}'))])
    
    611
    +
    
    612
    +    checkout = os.path.join(str(tmpdir), 'checkout')
    
    613
    +
    
    614
    +    result = cli.run(project=project, args=['build', 'target.bst'])
    
    615
    +    result.assert_success()
    
    616
    +    result = cli.run(project=project, args=['checkout', 'target.bst', checkout])
    
    617
    +    result.assert_success()
    
    618
    +
    
    619
    +    if tag_type == 'annotated':
    
    620
    +        options = []
    
    621
    +    else:
    
    622
    +        options = ['--tags']
    
    623
    +    describe = subprocess.check_output(['git', 'describe'] + options,
    
    624
    +                                       cwd=checkout).decode('ascii')
    
    625
    +    assert describe.startswith('tag2-2-')
    
    626
    +
    
    627
    +    describe_fp = subprocess.check_output(['git', 'describe', '--first-parent'] + options,
    
    628
    +                                          cwd=checkout).decode('ascii')
    
    629
    +    assert describe_fp.startswith('tag1-2-')
    
    630
    +
    
    631
    +    tags = subprocess.check_output(['git', 'tag'],
    
    632
    +                                   cwd=checkout).decode('ascii')
    
    633
    +    tags = set(tags.splitlines())
    
    634
    +    assert tags == set(['tag1', 'tag2'])
    
    635
    +
    
    636
    +    p = subprocess.run(['git', 'log', repo.rev_parse('uselesstag')],
    
    637
    +                       cwd=checkout)
    
    638
    +    assert p.returncode != 0
    
    639
    +
    
    640
    +
    
    641
    +@pytest.mark.skipif(HAVE_GIT is False, reason="git is not available")
    
    642
    +@pytest.mark.datafiles(os.path.join(DATA_DIR, 'template'))
    
    643
    +def test_default_do_not_track_tags(cli, tmpdir, datafiles):
    
    644
    +    project = str(datafiles)
    
    645
    +
    
    646
    +    project_config = _yaml.load(os.path.join(project, 'project.conf'))
    
    647
    +    project_config['ref-storage'] = 'inline'
    
    648
    +    _yaml.dump(_yaml.node_sanitize(project_config), os.path.join(project, 'project.conf'))
    
    649
    +
    
    650
    +    repofiles = os.path.join(str(tmpdir), 'repofiles')
    
    651
    +    os.makedirs(repofiles, exist_ok=True)
    
    652
    +    file0 = os.path.join(repofiles, 'file0')
    
    653
    +    with open(file0, 'w') as f:
    
    654
    +        f.write('test\n')
    
    655
    +
    
    656
    +    repo = create_repo('git', str(tmpdir))
    
    657
    +
    
    658
    +    ref = repo.create(repofiles)
    
    659
    +    repo.add_tag('tag')
    
    660
    +
    
    661
    +    config = repo.source_config()
    
    662
    +    config['track'] = repo.latest_commit()
    
    663
    +
    
    664
    +    # Write out our test target
    
    665
    +    element = {
    
    666
    +        'kind': 'import',
    
    667
    +        'sources': [
    
    668
    +            config
    
    669
    +        ],
    
    670
    +    }
    
    671
    +    element_path = os.path.join(project, 'target.bst')
    
    672
    +    _yaml.dump(element, element_path)
    
    673
    +
    
    674
    +    result = cli.run(project=project, args=['track', 'target.bst'])
    
    675
    +    result.assert_success()
    
    676
    +
    
    677
    +    element = _yaml.load(element_path)
    
    678
    +    assert 'tags' not in element['sources'][0]

  • tests/testutils/repo/git.py
    ... ... @@ -45,6 +45,9 @@ class Git(Repo):
    45 45
         def add_tag(self, tag):
    
    46 46
             self._run_git('tag', tag)
    
    47 47
     
    
    48
    +    def add_annotated_tag(self, tag, message):
    
    49
    +        self._run_git('tag', '-a', tag, '-m', message)
    
    50
    +
    
    48 51
         def add_commit(self):
    
    49 52
             self._run_git('commit', '--allow-empty', '-m', 'Additional commit')
    
    50 53
             return self.latest_commit()
    
    ... ... @@ -95,3 +98,14 @@ class Git(Repo):
    95 98
     
    
    96 99
         def branch(self, branch_name):
    
    97 100
             self._run_git('checkout', '-b', branch_name)
    
    101
    +
    
    102
    +    def checkout(self, commit):
    
    103
    +        self._run_git('checkout', commit)
    
    104
    +
    
    105
    +    def merge(self, commit):
    
    106
    +        self._run_git('merge', '-m', 'Merge', commit)
    
    107
    +        return self.latest_commit()
    
    108
    +
    
    109
    +    def rev_parse(self, rev):
    
    110
    +        output = self._run_git('rev-parse', rev, stdout=subprocess.PIPE).stdout
    
    111
    +        return output.decode('UTF-8').strip()



  • [Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]