Commit 232b7ea3 authored by Tom Pollard's avatar Tom Pollard Committed by Javier Jardón
Browse files

plugins/git.py: Fail if ref is not in given track

Add a helper function assert_ref_in_track to git.py GitMirror()
which is used when staging & initing the source workspace. It
checks the element's ref exists in the track (branch/tag) if it
has been specified, raising an error if necessary. If the element
has been tracked with bst, it is assumed that the value of ref exists
in the track as it was generated from it & as such is not asserted.

WIP: Fix for issue #483
parent 480565c2
Loading
Loading
Loading
Loading
Loading
+36 −4
Original line number Diff line number Diff line
@@ -190,7 +190,7 @@ class GitMirror(SourceFetcher):
            cwd=self.mirror)
        return output.rstrip('\n')

    def stage(self, directory):
    def stage(self, directory, track=None):
        fullpath = os.path.join(directory, self.path)

        # Using --shared here avoids copying the objects into the checkout, in any
@@ -204,10 +204,14 @@ class GitMirror(SourceFetcher):
                         fail="Failed to checkout git ref {}".format(self.ref),
                         cwd=fullpath)

        # Check that the user specified ref exists in the track if provided & not already tracked
        if track:
            self.assert_ref_in_track(fullpath, track)

        # Remove .git dir
        shutil.rmtree(os.path.join(fullpath, ".git"))

    def init_workspace(self, directory):
    def init_workspace(self, directory, track=None):
        fullpath = os.path.join(directory, self.path)
        url = self.source.translate_url(self.url)

@@ -223,6 +227,10 @@ class GitMirror(SourceFetcher):
                         fail="Failed to checkout git ref {}".format(self.ref),
                         cwd=fullpath)

        # Check that the user specified ref exists in the track if provided & not already tracked
        if track:
            self.assert_ref_in_track(fullpath, track)

    # List the submodules (path/url tuples) present at the given ref of this repo
    def submodule_list(self):
        modules = "{}:{}".format(self.ref, GIT_MODULES)
@@ -287,6 +295,27 @@ class GitMirror(SourceFetcher):

            return None

    # Assert that ref exists in track, if track has been specified.
    def assert_ref_in_track(self, fullpath, track):
        _, branch = self.source.check_output([self.source.host_git, 'branch', '--list', track,
                                              '--contains', self.ref],
                                             cwd=fullpath,)
        if branch:
            return True
        else:
            _, tag = self.source.check_output([self.source.host_git, 'tag', '--list', track,
                                               '--contains', self.ref],
                                              cwd=fullpath,)
            if tag:
                return True

        detail = "The ref provided for the element does not exist locally in the provided track branch / tag" + \
                 "'{}'.\nYou may wish to track the element to update the ref from '{}'".format(track, track) + \
                 "with `bst track`,\nor examine the upstream at '{}' for the specific ref.".format(self.url)

        raise SourceError("{}: expected ref '{}' was not found in given track '{}' for staged repository: '{}'\n"
                          .format(self.source, self.ref, track, self.url), detail=detail, reason="ref-not-in-track")


class GitSource(Source):
    # pylint: disable=attribute-defined-outside-init
@@ -317,6 +346,7 @@ class GitSource(Source):
                self.submodule_checkout_overrides[path] = checkout

        self.mark_download_url(self.original_url)
        self.tracked = False

    def preflight(self):
        # Check if git is installed, get the binary at the same time
@@ -374,6 +404,8 @@ class GitSource(Source):
            # Update self.mirror.ref and node.ref from the self.tracking branch
            ret = self.mirror.latest_commit(self.tracking)

        # Set tracked attribute, parameter for if self.mirror.assert_ref_in_track is needed
        self.tracked = True
        return ret

    def init_workspace(self, directory):
@@ -381,7 +413,7 @@ class GitSource(Source):
        self.refresh_submodules()

        with self.timed_activity('Setting up workspace "{}"'.format(directory), silent_nested=True):
            self.mirror.init_workspace(directory)
            self.mirror.init_workspace(directory, track=(self.tracking if not self.tracked else None))
            for mirror in self.submodules:
                mirror.init_workspace(directory)

@@ -397,7 +429,7 @@ class GitSource(Source):
        # Stage the main repo in the specified directory
        #
        with self.timed_activity("Staging {}".format(self.mirror.url), silent_nested=True):
            self.mirror.stage(directory)
            self.mirror.stage(directory, track=(self.tracking if not self.tracked else None))
            for mirror in self.submodules:
                if mirror.path in self.submodule_checkout_overrides:
                    checkout = self.submodule_checkout_overrides[mirror.path]
+30 −0
Original line number Diff line number Diff line
@@ -359,3 +359,33 @@ def test_submodule_track_ignore_inconsistent(cli, tmpdir, datafiles):

    # Assert that we are just fine without it, and emit a warning to the user.
    assert "Ignoring inconsistent submodule" in result.stderr


@pytest.mark.skipif(HAVE_GIT is False, reason="git is not available")
@pytest.mark.datafiles(os.path.join(DATA_DIR, 'template'))
def test_ref_not_in_track(cli, tmpdir, datafiles):
    project = os.path.join(datafiles.dirname, datafiles.basename)

    # Create the repo from 'repofiles', create a branch without latest commit
    repo = create_repo('git', str(tmpdir))
    ref = repo.create(os.path.join(project, 'repofiles'))

    gitsource = repo.source_config(ref=ref)

    # Overwrite the track value to the added branch
    gitsource['track'] = 'foo'

    # Write out our test target
    element = {
        'kind': 'import',
        'sources': [
            gitsource
        ]
    }
    _yaml.dump(element, os.path.join(project, 'target.bst'))

    # Assert that build raises an error here,
    # as there's no branch foo containing ref
    result = cli.run(project=project, args=['build', 'target.bst'])
    result.assert_main_error(ErrorDomain.STREAM, None)
    result.assert_task_error(ErrorDomain.SOURCE, 'ref-not-in-track')