Exemple #1
0
 def test_detect_worktree_failing_git(self):
     with self.executable_git() as git:
         with open(git, 'w') as fp:
             fp.write('#!/bin/sh\n')
             fp.write('exit 1')
         self.assertIsNone(Git.detect_worktree())
         self.assertIsNone(Git.detect_worktree(git))
Exemple #2
0
 def test_detect_worktree_failing_git(self):
     with self.executable_git() as git:
         with open(git, "w") as fp:
             fp.write("#!/bin/sh\n")
             fp.write("exit 1")
         self.assertIsNone(Git.detect_worktree())
         self.assertIsNone(Git.detect_worktree(git))
Exemple #3
0
 def test_detect_worktree_failing_git(self):
   with self.executable_git() as git:
     with open(git, 'w') as fp:
       fp.write('#!/bin/sh\n')
       fp.write('exit 1')
     self.assertIsNone(Git.detect_worktree())
     self.assertIsNone(Git.detect_worktree(git))
Exemple #4
0
 def test_detect_worktree_working_git(self):
   expected_worktree_dir = '/a/fake/worktree/dir'
   with self.executable_git() as git:
     with open(git, 'w') as fp:
       fp.write('#!/bin/sh\n')
       fp.write('echo ' + expected_worktree_dir)
     self.assertEqual(expected_worktree_dir, Git.detect_worktree())
     self.assertEqual(expected_worktree_dir, Git.detect_worktree(binary=git))
Exemple #5
0
 def test_detect_worktree_somewhere_else(self):
   with temporary_dir() as somewhere_else:
     with pushd(somewhere_else):
       loc = Git.detect_worktree(dir=somewhere_else)
       self.assertEquals(None, loc)
       subprocess.check_call(['git', 'init'])
       loc = Git.detect_worktree(dir=somewhere_else)
       self.assertEquals(os.path.realpath(somewhere_else), loc)
Exemple #6
0
 def test_detect_worktree_working_git(self):
     expected_worktree_dir = "/a/fake/worktree/dir"
     with self.executable_git() as git:
         with open(git, "w") as fp:
             fp.write("#!/bin/sh\n")
             fp.write("echo " + expected_worktree_dir)
         self.assertEqual(expected_worktree_dir, Git.detect_worktree())
         self.assertEqual(expected_worktree_dir,
                          Git.detect_worktree(binary=git))
Exemple #7
0
  def setUp(self):
    self.origin = safe_mkdtemp()
    with pushd(self.origin):
      subprocess.check_call(['git', 'init', '--bare'])

    self.gitdir = safe_mkdtemp()
    self.worktree = safe_mkdtemp()

    self.readme_file = os.path.join(self.worktree, 'README')

    with environment_as(GIT_DIR=self.gitdir, GIT_WORK_TREE=self.worktree):
      self.init_repo('depot', self.origin)

      touch(self.readme_file)
      subprocess.check_call(['git', 'add', 'README'])
      safe_mkdir(os.path.join(self.worktree, 'dir'))
      with open(os.path.join(self.worktree, 'dir', 'f'), 'w') as f:
        f.write("file in subdir")

      # Make some symlinks
      os.symlink('f', os.path.join(self.worktree, 'dir', 'relative-symlink'))
      os.symlink('no-such-file', os.path.join(self.worktree, 'dir', 'relative-nonexistent'))
      os.symlink('dir/f', os.path.join(self.worktree, 'dir', 'not-absolute\u2764'))
      os.symlink('../README', os.path.join(self.worktree, 'dir', 'relative-dotdot'))
      os.symlink('dir', os.path.join(self.worktree, 'link-to-dir'))
      os.symlink('README/f', os.path.join(self.worktree, 'not-a-dir'))
      os.symlink('loop1', os.path.join(self.worktree, 'loop2'))
      os.symlink('loop2', os.path.join(self.worktree, 'loop1'))

      subprocess.check_call(['git', 'add', 'README', 'dir', 'loop1', 'loop2',
                             'link-to-dir', 'not-a-dir'])
      subprocess.check_call(['git', 'commit', '-am', 'initial commit with decode -> \x81b'])
      self.initial_rev = subprocess.check_output(['git', 'rev-parse', 'HEAD']).strip()
      subprocess.check_call(['git', 'tag', 'first'])
      subprocess.check_call(['git', 'push', '--tags', 'depot', 'master'])
      subprocess.check_call(['git', 'branch', '--set-upstream-to', 'depot/master'])

      with safe_open(self.readme_file, 'w') as readme:
        readme.write('Hello World.\u2764'.encode('utf-8'))
      subprocess.check_call(['git', 'commit', '-am', 'Update README.'])

      self.current_rev = subprocess.check_output(['git', 'rev-parse', 'HEAD']).strip()

    self.clone2 = safe_mkdtemp()
    with pushd(self.clone2):
      self.init_repo('origin', self.origin)
      subprocess.check_call(['git', 'pull', '--tags', 'origin', 'master:master'])

      with safe_open(os.path.realpath('README'), 'a') as readme:
        readme.write('--')
      subprocess.check_call(['git', 'commit', '-am', 'Update README 2.'])
      subprocess.check_call(['git', 'push', '--tags', 'origin', 'master'])

    self.git = Git(gitdir=self.gitdir, worktree=self.worktree)
 def __call__(self, manifest_entries=None):
     """Returns a dict suitable for passing to 'manifest_entries' in a 'jvm_binary() definition"""
     manifest_entries = manifest_entries or {}
     buildroot = get_buildroot()
     worktree = Git.detect_worktree(
         subdir=os.path.join(buildroot, self._parse_context.rel_path))
     if worktree:
         git = Git(worktree=worktree)
         manifest_entries['Implementation-Version'] = git.commit_id
     manifest_entries['Built-By'] = pwd.getpwuid(os.getuid()).pw_name
     return manifest_entries
Exemple #9
0
  def test(self):
    self.assertEqual(set(), self.git.changed_files())
    self.assertEqual(set(['README']), self.git.changed_files(from_commit='HEAD^'))

    tip_sha = self.git.commit_id
    self.assertTrue(tip_sha)

    self.assertTrue(tip_sha in self.git.changelog())

    self.assertTrue(self.git.tag_name.startswith('first-'), msg='un-annotated tags should be found')
    self.assertEqual('master', self.git.branch_name)

    def edit_readme():
      with open(self.readme_file, 'a') as readme:
        readme.write('More data.')

    edit_readme()
    with open(os.path.join(self.worktree, 'INSTALL'), 'w') as untracked:
      untracked.write('make install')
    self.assertEqual(set(['README']), self.git.changed_files())
    self.assertEqual(set(['README', 'INSTALL']), self.git.changed_files(include_untracked=True))

    try:
      # These changes should be rejected because our branch point from origin is 1 commit behind
      # the changes pushed there in clone 2.
      self.git.commit('API Changes.')
    except Scm.RemoteException:
      with environment_as(GIT_DIR=self.gitdir, GIT_WORK_TREE=self.worktree):
        subprocess.check_call(['git', 'reset', '--hard', 'depot/master'])
      self.git.refresh()
      edit_readme()

    self.git.commit('''API '"' " Changes.''')
    self.git.tag('second', message='''Tagged ' " Changes''')

    with temporary_dir() as clone:
      with pushd(clone):
        self.init_repo('origin', self.origin)
        subprocess.check_call(['git', 'pull', '--tags', 'origin', 'master:master'])

        with open(os.path.realpath('README')) as readme:
          self.assertEqual('--More data.', readme.read())

        git = Git()

        # Check that we can pick up committed and uncommitted changes.
        with safe_open(os.path.realpath('CHANGES'), 'w') as changes:
          changes.write('none')
        subprocess.check_call(['git', 'add', 'CHANGES'])
        self.assertEqual(set(['README', 'CHANGES']), git.changed_files(from_commit='first'))

        self.assertEqual('master', git.branch_name)
        self.assertEqual('second', git.tag_name, msg='annotated tags should be found')
Exemple #10
0
def initialize_repo(worktree, gitdir=None):
  """Initialize a git repository for the given `worktree`.

  NB: The given `worktree` must contain at least one file which will be committed to form an initial
  commit.

  :param string worktree: The path to the git work tree.
  :param string gitdir: An optional path to the `.git` dir to use.
  :returns: A `Git` repository object that can be used to interact with the repo.
  :rtype: :class:`pants.scm.git.Git`
  """
  @contextmanager
  def use_gitdir():
    if gitdir:
      yield gitdir
    else:
      with temporary_dir() as d:
        yield d

  with use_gitdir() as git_dir, environment_as(GIT_DIR=git_dir, GIT_WORK_TREE=worktree):
    subprocess.check_call(['git', 'init'])
    subprocess.check_call(['git', 'config', 'user.email', '*****@*****.**'])
    subprocess.check_call(['git', 'config', 'user.name', 'Your Name'])
    subprocess.check_call(['git', 'add', '.'])
    subprocess.check_call(['git', 'commit', '-am', 'Add project files.'])

    yield Git(gitdir=git_dir, worktree=worktree)
  def project_template(self):
    target_levels = {Revision.lenient(platform['target_level'])
                     for platform in self.blob['jvm_platforms']['platforms'].values()}
    lang_level = max(target_levels) if target_levels else Revision(1, 8)

    configured_project = TemplateData(
      root_dir=get_buildroot(),
      outdir=self.output_directory,
      git_root=Git.detect_worktree(),
      modules=self.module_templates_by_filename.values(),
      java=TemplateData(
        encoding=self.java_encoding,
        maximum_heap_size=self.java_maximum_heap_size,
        jdk='{0}.{1}'.format(*lang_level.components[:2]),
        language_level='JDK_{0}_{1}'.format(*lang_level.components[:2]),
      ),
      resource_extensions=[],
      scala=None,
      checkstyle_classpath=';'.join([]),
      debug_port=self.debug_port,
      annotation_processing=self.annotation_processing_template,
      extra_components=[],
      junit_tests=self._junit_tests_config(),
      global_junit_vm_parameters=' '.join(self.global_junit_jvm_options),
    )
    return configured_project
Exemple #12
0
  def select(self, context):
    self.get_options()
    workdir = os.path.join(self.get_options().pants_workdir, self.options_scope,
                           'versions', self.get_options().version)
    tool_path = os.path.join(workdir, 'bin/protoc-gen-go')

    if not os.path.exists(tool_path):
      safe_mkdir(workdir, clean=True)

      # Checkout the git repo at a given version. `go get` always gets master.
      repo = Git.clone('https://github.com/golang/protobuf.git',
                       os.path.join(workdir, 'src/github.com/golang/protobuf'))
      repo.set_state(self.get_options().version)

      go = GoDistribution.global_instance()
      result, go_cmd = go.execute_go_cmd(
        cmd='install',
        gopath=workdir,
        args=['github.com/golang/protobuf/protoc-gen-go'],
        workunit_factory=context.new_workunit,
        workunit_labels=[WorkUnitLabel.BOOTSTRAP],
      )

      if result != 0:
        raise SubsystemError('{} failed with exit code {}'.format(go_cmd, result))

    logger.info('Selected {} binary bootstrapped to: {}'.format(self.options_scope, tool_path))
    return tool_path
Exemple #13
0
def initialize_repo(worktree: str,
                    *,
                    gitdir: Optional[str] = None) -> Iterator[Git]:
    """Initialize a git repository for the given `worktree`.

    NB: The given `worktree` must contain at least one file which will be committed to form an initial
    commit.

    :param worktree: The path to the git work tree.
    :param gitdir: An optional path to the `.git` dir to use.
    :returns: A `Git` repository object that can be used to interact with the repo.
    """
    @contextmanager
    def use_gitdir() -> Iterator[str]:
        if gitdir:
            yield gitdir
        else:
            with temporary_dir() as d:
                yield d

    with use_gitdir() as git_dir, environment_as(GIT_DIR=git_dir,
                                                 GIT_WORK_TREE=worktree):
        subprocess.run(["git", "init"], check=True)
        subprocess.run(["git", "config", "user.email", "*****@*****.**"],
                       check=True)
        # TODO: This method inherits the global git settings, so if a developer has gpg signing on, this
        # will turn that off. We should probably just disable reading from the global config somehow:
        # https://git-scm.com/docs/git-config.
        subprocess.run(["git", "config", "commit.gpgSign", "false"],
                       check=True)
        subprocess.run(["git", "config", "user.name", "Your Name"], check=True)
        subprocess.run(["git", "add", "."], check=True)
        subprocess.run(["git", "commit", "-am", "Add project files."],
                       check=True)
        yield Git(gitdir=git_dir, worktree=worktree)
Exemple #14
0
  def setUp(self):
    self.origin = safe_mkdtemp()
    with pushd(self.origin):
      subprocess.check_call(['git', 'init', '--bare'])

    self.gitdir = safe_mkdtemp()
    self.worktree = safe_mkdtemp()

    self.readme_file = os.path.join(self.worktree, 'README')

    with environment_as(GIT_DIR=self.gitdir, GIT_WORK_TREE=self.worktree):
      self.init_repo('depot', self.origin)

      touch(self.readme_file)
      subprocess.check_call(['git', 'add', 'README'])
      subprocess.check_call(['git', 'commit', '-am', 'initial commit with decode -> \x81b'])
      subprocess.check_call(['git', 'tag', 'first'])
      subprocess.check_call(['git', 'push', '--tags', 'depot', 'master'])
      subprocess.check_call(['git', 'branch', '--set-upstream', 'master', 'depot/master'])

      with safe_open(self.readme_file, 'w') as readme:
        readme.write('Hello World.')
      subprocess.check_call(['git', 'commit', '-am', 'Update README.'])

    self.clone2 = safe_mkdtemp()
    with pushd(self.clone2):
      self.init_repo('origin', self.origin)
      subprocess.check_call(['git', 'pull', '--tags', 'origin', 'master:master'])

      with safe_open(os.path.realpath('README'), 'a') as readme:
        readme.write('--')
      subprocess.check_call(['git', 'commit', '-am', 'Update README 2.'])
      subprocess.check_call(['git', 'push', '--tags', 'origin', 'master'])

    self.git = Git(gitdir=self.gitdir, worktree=self.worktree)
Exemple #15
0
 def worktree_relative_to(some_dir, expected):
     # Given a directory relative to the worktree, tests that the worktree is detected as 'expected'.
     subdir = os.path.join(clone, some_dir)
     if not os.path.isdir(subdir):
         os.mkdir(subdir)
     actual = Git.detect_worktree(subdir=subdir)
     self.assertEqual(expected, actual)
Exemple #16
0
  def setUpClass(cls):
    cls.origin = safe_mkdtemp()
    with pushd(cls.origin):
      subprocess.check_call(['git', 'init', '--bare'])

    cls.gitdir = safe_mkdtemp()
    cls.worktree = safe_mkdtemp()

    cls.readme_file = os.path.join(cls.worktree, 'README')

    with environment_as(GIT_DIR=cls.gitdir, GIT_WORK_TREE=cls.worktree):
      cls.init_repo('depot', cls.origin)

      touch(cls.readme_file)
      subprocess.check_call(['git', 'add', 'README'])
      subprocess.check_call(['git', 'commit', '-am', 'initial commit with decode -> \x81b'])
      subprocess.check_call(['git', 'tag', 'first'])
      subprocess.check_call(['git', 'push', '--tags', 'depot', 'master'])
      subprocess.check_call(['git', 'branch', '--set-upstream', 'master', 'depot/master'])

      with safe_open(cls.readme_file, 'w') as readme:
        readme.write('Hello World.')
      subprocess.check_call(['git', 'commit', '-am', 'Update README.'])

    cls.clone2 = safe_mkdtemp()
    with pushd(cls.clone2):
      cls.init_repo('origin', cls.origin)
      subprocess.check_call(['git', 'pull', '--tags', 'origin', 'master:master'])

      with safe_open(os.path.realpath('README'), 'a') as readme:
        readme.write('--')
      subprocess.check_call(['git', 'commit', '-am', 'Update README 2.'])
      subprocess.check_call(['git', 'push', '--tags', 'origin', 'master'])

    cls.git = Git(gitdir=cls.gitdir, worktree=cls.worktree)
Exemple #17
0
  def select(self, context):
    self.get_options()
    workdir = os.path.join(self.get_options().pants_workdir, self.options_scope,
                           'versions', self.get_options().version)
    tool_path = os.path.join(workdir, 'bin/protoc-gen-go')

    if not os.path.exists(tool_path):
      safe_mkdir(workdir, clean=True)

      # Checkout the git repo at a given version. `go get` always gets master.
      repo = Git.clone('https://github.com/golang/protobuf.git',
                       os.path.join(workdir, 'src/github.com/golang/protobuf'))
      repo.set_state(self.get_options().version)

      go = GoDistribution.global_instance()
      result, go_cmd = go.execute_go_cmd(
        cmd='install',
        gopath=workdir,
        args=['github.com/golang/protobuf/protoc-gen-go'],
        workunit_factory=context.new_workunit,
        workunit_labels=[WorkUnitLabel.BOOTSTRAP],
      )

      if result != 0:
        raise SubsystemError('{} failed with exit code {}'.format(go_cmd, result))

    logger.info('Selected {} binary bootstrapped to: {}'.format(self.options_scope, tool_path))
    return tool_path
Exemple #18
0
def initialize_repo(worktree, gitdir=None):
    """Initialize a git repository for the given `worktree`.

  NB: The given `worktree` must contain at least one file which will be committed to form an initial
  commit.

  :param string worktree: The path to the git work tree.
  :param string gitdir: An optional path to the `.git` dir to use.
  :returns: A `Git` repository object that can be used to interact with the repo.
  :rtype: :class:`pants.scm.git.Git`
  """
    @contextmanager
    def use_gitdir():
        if gitdir:
            yield gitdir
        else:
            with temporary_dir() as d:
                yield d

    with use_gitdir() as git_dir, environment_as(GIT_DIR=git_dir,
                                                 GIT_WORK_TREE=worktree):
        subprocess.check_call(['git', 'init'])
        subprocess.check_call(
            ['git', 'config', 'user.email', '*****@*****.**'])
        # TODO: This method inherits the global git settings, so if a developer has gpg signing on, this
        # will turn that off. We should probably just disable reading from the global config somehow:
        # https://git-scm.com/docs/git-config.
        subprocess.check_call(['git', 'config', 'commit.gpgSign', 'false'])
        subprocess.check_call(['git', 'config', 'user.name', 'Your Name'])
        subprocess.check_call(['git', 'add', '.'])
        subprocess.check_call(['git', 'commit', '-am', 'Add project files.'])

        yield Git(gitdir=git_dir, worktree=worktree)
Exemple #19
0
 def worktree_relative_to(some_dir, expected):
   # Given a directory relative to the worktree, tests that the worktree is detected as 'expected'.
   subdir = os.path.join(clone, some_dir)
   if not os.path.isdir(subdir):
     os.mkdir(subdir)
   actual = Git.detect_worktree(subdir=subdir)
   self.assertEqual(expected, actual)
Exemple #20
0
def get_scm():
    """Returns the pants Scm if any."""
    # TODO(John Sirois): Extract a module/class to carry the bootstrap logic.
    global _SCM
    if not _SCM:
        from pants.scm.git import Git
        # We know about git, so attempt an auto-configure
        worktree = Git.detect_worktree()
        if worktree and os.path.isdir(worktree):
            git = Git(worktree=worktree)
            try:
                log.info('Detected git repository at %s on branch %s' %
                         (worktree, git.branch_name))
                set_scm(git)
            except git.LocalException as e:
                log.info('Failed to load git repository at %s: %s' %
                         (worktree, e))
    return _SCM
 def _do_clone_and_checkout(self, download_path):
     desired_sha = self.version()
     try:
         checkout = Git.clone(self._GIT_CLONE_HTTPS_URL, download_path)
         checkout.set_state(desired_sha)
     except Scm.ScmException as e:
         raise self.RakudoBrewBootstrapError(
             "Error checking out revision '{}' from {}: {}".format(
                 desired_sha, self._GIT_CLONE_HTTPS_URL, e), e)
 def __call__(self, manifest_entries=None):
   """Returns a dict suitable for passing to 'manifest_entries' in a 'jvm_binary() definition"""
   manifest_entries = manifest_entries or {}
   buildroot = get_buildroot()
   worktree = Git.detect_worktree(subdir=os.path.join(buildroot,
                                                   self._parse_context.rel_path))
   if worktree:
     git = Git(worktree=worktree)
     manifest_entries['Implementation-Version'] = git.commit_id
   manifest_entries['Built-By'] = pwd.getpwuid(os.getuid()).pw_name
   return manifest_entries
Exemple #23
0
def get_scm() -> Optional[Scm]:
  """Returns the pants Scm if any.

  :API: public
  """
  # TODO(John Sirois): Extract a module/class to carry the bootstrap logic.
  global _SCM
  if _SCM:
    return _SCM
  from pants.scm.git import Git
  # We know about git, so attempt an auto-configure
  worktree = Git.detect_worktree()
  if worktree and os.path.isdir(worktree):
    git = Git(worktree=worktree)
    try:
      logger.debug(f'Detected git repository at {worktree} on branch {git.branch_name}')
      set_scm(git)
    except git.LocalException as e:
      logger.info(f'Failed to load git repository at {worktree}: {e!r}')
  return _SCM
Exemple #24
0
 def worktree_relative_to(cwd, expected):
     # Given a cwd relative to the worktree, tests that the worktree is detected as 'expected'.
     orig_cwd = os.getcwd()
     try:
         abs_cwd = os.path.join(clone, cwd)
         if not os.path.isdir(abs_cwd):
             os.mkdir(abs_cwd)
         os.chdir(abs_cwd)
         actual = Git.detect_worktree()
         self.assertEqual(expected, actual)
     finally:
         os.chdir(orig_cwd)
Exemple #25
0
 def worktree_relative_to(cwd, expected):
   """Given a cwd relative to the worktree, tests that the worktree is detected as 'expected'."""
   orig_cwd = os.getcwd()
   try:
     abs_cwd = os.path.join(clone, cwd)
     if not os.path.isdir(abs_cwd):
       os.mkdir(abs_cwd)
     os.chdir(abs_cwd)
     actual = Git.detect_worktree()
     self.assertEqual(expected, actual)
   finally:
     os.chdir(orig_cwd)
    def test_build_file_rev(self):
        # Test that the build_file_rev global option works.  Because the
        # test framework does not yet support bootstrap options, this test
        # in fact just directly calls ScmBuildFile.set_rev.

        with pushd(self.root_dir):
            subprocess.check_call(['git', 'init'])
            subprocess.check_call(
                ['git', 'config', 'user.email', '*****@*****.**'])
            subprocess.check_call(['git', 'config', 'user.name', 'Your Name'])
            subprocess.check_call(['git', 'add', '.'])
            subprocess.check_call(['git', 'commit', '-m' 'initial commit'])

            subprocess.check_call([
                'rm', '-rf', 'path-that-does-exist', 'grandparent', 'BUILD',
                'BUILD.twitter'
            ])

            self._project_tree = ScmProjectTree(self.root_dir,
                                                Git(worktree=self.root_dir),
                                                'HEAD')

            my_buildfile = self.create_buildfile('grandparent/parent/BUILD')
            buildfile = self.create_buildfile(
                'grandparent/parent/BUILD.twitter')

            self.assertEquals(
                OrderedSet([my_buildfile, buildfile]),
                OrderedSet(self.get_build_files_family('grandparent/parent')))

            self.assertEquals(
                OrderedSet([
                    self.create_buildfile(
                        'grandparent/parent/child2/child3/BUILD')
                ]),
                OrderedSet(
                    self.get_build_files_family(
                        'grandparent/parent/child2/child3')))

            buildfiles = self.scan_buildfiles('grandparent')

            self.assertEquals(
                OrderedSet([
                    self.create_buildfile('grandparent/parent/BUILD'),
                    self.create_buildfile('grandparent/parent/BUILD.twitter'),
                    self.create_buildfile('grandparent/parent/child1/BUILD'),
                    self.create_buildfile(
                        'grandparent/parent/child1/BUILD.twitter'),
                    self.create_buildfile(
                        'grandparent/parent/child2/child3/BUILD'),
                    self.create_buildfile('grandparent/parent/child5/BUILD'),
                ]), buildfiles)
Exemple #27
0
 def fetch(self, dest, rev=None):
   imported_repo = self._meta_tag_reader.get_imported_repo(self.import_path)
   if not imported_repo:
     raise FetchError('No <meta name="go-import"> tag found, so cannot fetch repo '
                      'at {}'.format(self.import_path))
   if imported_repo.vcs != 'git':
     # TODO: Support other vcs systems as needed.
     raise FetchError("Don't know how to fetch for vcs type {}.".format(imported_repo.vcs))
   # TODO: Do this in a workunit (see https://github.com/pantsbuild/pants/issues/3502).
   logger.info('Cloning {} into {}'.format(imported_repo.url, dest))
   repo = Git.clone(imported_repo.url, dest)
   if rev:
     repo.set_state(rev)
Exemple #28
0
def initialize_repo(worktree):
    """Initialize git repository for the given worktree."""
    gitdir = safe_mkdtemp()
    with environment_as(GIT_DIR=gitdir, GIT_WORK_TREE=worktree):
        subprocess.check_call(['git', 'init'])
        subprocess.check_call(
            ['git', 'config', 'user.email', '*****@*****.**'])
        subprocess.check_call(['git', 'config', 'user.name', 'Your Name'])
        subprocess.check_call(['git', 'add', '.'])
        subprocess.check_call(['git', 'commit', '-am', 'Add project files.'])

        yield Git(gitdir=gitdir, worktree=worktree)

        safe_rmtree(gitdir)
Exemple #29
0
 def fetch(self, dest, rev=None):
     imported_repo = self._meta_tag_reader.get_imported_repo(
         self.import_path)
     if not imported_repo:
         raise FetchError(
             'No <meta name="go-import"> tag found, so cannot fetch repo '
             'at {}'.format(self.import_path))
     if imported_repo.vcs != 'git':
         # TODO: Support other vcs systems as needed.
         raise FetchError("Don't know how to fetch for vcs type {}.".format(
             imported_repo.vcs))
     # TODO: Do this in a workunit (see https://github.com/pantsbuild/pants/issues/3502).
     logger.info('Cloning {} into {}'.format(imported_repo.url, dest))
     repo = Git.clone(imported_repo.url, dest)
     if rev:
         repo.set_state(rev)
Exemple #30
0
def get_scm():
  """Returns the pants Scm if any."""
  # TODO(John Sirois): Extract a module/class to carry the bootstrap logic.
  global _SCM
  if not _SCM:
    # We know about git, so attempt an auto-configure
    git_dir = os.path.join(get_buildroot(), '.git')
    if os.path.isdir(git_dir):
      from pants.scm.git import Git
      git = Git(worktree=get_buildroot())
      try:
        log.info('Detected git repository on branch %s' % git.branch_name)
        set_scm(git)
      except git.LocalException:
        pass
  return _SCM
Exemple #31
0
def get_scm():
  """Returns the pants Scm if any."""
  # TODO(John Sirois): Extract a module/class to carry the bootstrap logic.
  global _SCM
  if not _SCM:
    from pants.scm.git import Git
    # We know about git, so attempt an auto-configure
    worktree = Git.detect_worktree()
    if worktree and os.path.isdir(worktree):
      git = Git(worktree=worktree)
      try:
        logger.info('Detected git repository at {} on branch {}'.format(worktree, git.branch_name))
        set_scm(git)
      except git.LocalException as e:
        logger.info('Failed to load git repository at {}: {}'.format(worktree, e))
  return _SCM
Exemple #32
0
    def select(self, context):
        self.get_options()
        workdir = os.path.join(
            self.get_options().pants_workdir,
            self.options_scope,
            "versions",
            self.get_options().version,
        )
        tool_path = os.path.join(workdir, "bin/protoc-gen-go")

        if not os.path.exists(tool_path):
            safe_mkdir(workdir, clean=True)

            # Checkout the git repo at a given version. `go get` always gets master.
            repo = Git.clone(
                "https://github.com/golang/protobuf.git",
                os.path.join(workdir, "src/github.com/golang/protobuf"),
            )
            repo.set_state(self.get_options().version)

            go = GoDistribution.global_instance()
            result, go_cmd = go.execute_go_cmd(
                cmd="install",
                gopath=workdir,
                args=["github.com/golang/protobuf/protoc-gen-go"],
                workunit_factory=context.new_workunit,
                workunit_labels=[WorkUnitLabel.BOOTSTRAP],
            )

            if result != 0:
                raise SubsystemError(
                    f"{go_cmd} failed with exit code {result}")

        logger.info(
            f"Selected {self.options_scope} binary bootstrapped to: {tool_path}"
        )
        return tool_path
Exemple #33
0
class GitTest(unittest.TestCase):

  @staticmethod
  def init_repo(remote_name, remote):
    # TODO (peiyu) clean this up, use `git_util.initialize_repo`.
    subprocess.check_call(['git', 'init'])
    subprocess.check_call(['git', 'config', 'user.email', '*****@*****.**'])
    subprocess.check_call(['git', 'config', 'user.name', 'Your Name'])
    subprocess.check_call(['git', 'remote', 'add', remote_name, remote])

  def setUp(self):
    self.origin = safe_mkdtemp()
    with pushd(self.origin):
      subprocess.check_call(['git', 'init', '--bare'])

    self.gitdir = safe_mkdtemp()
    self.worktree = safe_mkdtemp()

    self.readme_file = os.path.join(self.worktree, 'README')

    with environment_as(GIT_DIR=self.gitdir, GIT_WORK_TREE=self.worktree):
      self.init_repo('depot', self.origin)

      touch(self.readme_file)
      subprocess.check_call(['git', 'add', 'README'])
      safe_mkdir(os.path.join(self.worktree, 'dir'))
      with open(os.path.join(self.worktree, 'dir', 'f'), 'w') as f:
        f.write("file in subdir")

      # Make some symlinks
      os.symlink('f', os.path.join(self.worktree, 'dir', 'relative-symlink'))
      os.symlink('no-such-file', os.path.join(self.worktree, 'dir', 'relative-nonexistent'))
      os.symlink('dir/f', os.path.join(self.worktree, 'dir', 'not-absolute\u2764'))
      os.symlink('../README', os.path.join(self.worktree, 'dir', 'relative-dotdot'))
      os.symlink('dir', os.path.join(self.worktree, 'link-to-dir'))
      os.symlink('README/f', os.path.join(self.worktree, 'not-a-dir'))
      os.symlink('loop1', os.path.join(self.worktree, 'loop2'))
      os.symlink('loop2', os.path.join(self.worktree, 'loop1'))

      subprocess.check_call(['git', 'add', 'README', 'dir', 'loop1', 'loop2',
                             'link-to-dir', 'not-a-dir'])
      subprocess.check_call(['git', 'commit', '-am', 'initial commit with decode -> \x81b'])
      self.initial_rev = subprocess.check_output(['git', 'rev-parse', 'HEAD']).strip()
      subprocess.check_call(['git', 'tag', 'first'])
      subprocess.check_call(['git', 'push', '--tags', 'depot', 'master'])
      subprocess.check_call(['git', 'branch', '--set-upstream-to', 'depot/master'])

      with safe_open(self.readme_file, 'w') as readme:
        readme.write('Hello World.\u2764'.encode('utf-8'))
      subprocess.check_call(['git', 'commit', '-am', 'Update README.'])

      self.current_rev = subprocess.check_output(['git', 'rev-parse', 'HEAD']).strip()

    self.clone2 = safe_mkdtemp()
    with pushd(self.clone2):
      self.init_repo('origin', self.origin)
      subprocess.check_call(['git', 'pull', '--tags', 'origin', 'master:master'])

      with safe_open(os.path.realpath('README'), 'a') as readme:
        readme.write('--')
      subprocess.check_call(['git', 'commit', '-am', 'Update README 2.'])
      subprocess.check_call(['git', 'push', '--tags', 'origin', 'master'])

    self.git = Git(gitdir=self.gitdir, worktree=self.worktree)

  @contextmanager
  def mkremote(self, remote_name):
    with temporary_dir() as remote_uri:
      subprocess.check_call(['git', 'remote', 'add', remote_name, remote_uri])
      try:
        yield remote_uri
      finally:
        subprocess.check_call(['git', 'remote', 'remove', remote_name])

  def tearDown(self):
    safe_rmtree(self.origin)
    safe_rmtree(self.gitdir)
    safe_rmtree(self.worktree)
    safe_rmtree(self.clone2)

  def test_listdir(self):
    reader = self.git.repo_reader(self.initial_rev)

    for dirname in '.', './.':
      results = reader.listdir(dirname)
      self.assertEquals(['README',
                         'dir',
                         'link-to-dir',
                         'loop1',
                         'loop2',
                         'not-a-dir'],
                        sorted(results))

    for dirname in 'dir', './dir':
      results = reader.listdir(dirname)
      self.assertEquals(['f',
                         'not-absolute\u2764'.encode('utf-8'),
                         'relative-dotdot',
                         'relative-nonexistent',
                         'relative-symlink'],
                        sorted(results))

    results = reader.listdir('link-to-dir')
    self.assertEquals(['f',
                       'not-absolute\u2764'.encode('utf-8'),
                       'relative-dotdot',
                       'relative-nonexistent',
                       'relative-symlink'],
                      sorted(results))

    with self.assertRaises(reader.MissingFileException):
      with reader.listdir('bogus'):
        pass

  def test_lstat(self):
    reader = self.git.repo_reader(self.initial_rev)
    def lstat(*components):
      return type(reader.lstat(os.path.join(*components)))
    self.assertEquals(reader.Symlink, lstat('dir', 'relative-symlink'))
    self.assertEquals(reader.Symlink, lstat('not-a-dir'))
    self.assertEquals(reader.File, lstat('README'))
    self.assertEquals(reader.Dir, lstat('dir'))
    self.assertEquals(types.NoneType, lstat('nope-not-here'))

  def test_readlink(self):
    reader = self.git.repo_reader(self.initial_rev)
    def readlink(*components):
      return reader.readlink(os.path.join(*components))
    self.assertEquals('dir/f', readlink('dir', 'relative-symlink'))
    self.assertEquals(None, readlink('not-a-dir'))
    self.assertEquals(None, readlink('README'))
    self.assertEquals(None, readlink('dir'))
    self.assertEquals(None, readlink('nope-not-here'))

  def test_open(self):
    reader = self.git.repo_reader(self.initial_rev)

    with reader.open('README') as f:
      self.assertEquals('', f.read())

    with reader.open('dir/f') as f:
      self.assertEquals('file in subdir', f.read())

    with self.assertRaises(reader.MissingFileException):
      with reader.open('no-such-file') as f:
        self.assertEquals('', f.read())

    with self.assertRaises(reader.MissingFileException):
      with reader.open('dir/no-such-file') as f:
        pass

    with self.assertRaises(reader.IsDirException):
      with reader.open('dir') as f:
        self.assertEquals('', f.read())

    current_reader = self.git.repo_reader(self.current_rev)

    with current_reader.open('README') as f:
      self.assertEquals('Hello World.\u2764'.encode('utf-8'), f.read())

    with current_reader.open('link-to-dir/f') as f:
      self.assertEquals('file in subdir', f.read())

    with current_reader.open('dir/relative-symlink') as f:
      self.assertEquals('file in subdir', f.read())

    with self.assertRaises(current_reader.SymlinkLoopException):
      with current_reader.open('loop1') as f:
        pass

    with self.assertRaises(current_reader.MissingFileException):
      with current_reader.open('dir/relative-nonexistent') as f:
        pass

    with self.assertRaises(current_reader.NotADirException):
      with current_reader.open('not-a-dir') as f:
        pass

    with self.assertRaises(current_reader.MissingFileException):
      with current_reader.open('dir/not-absolute\u2764') as f:
        pass

    with self.assertRaises(current_reader.MissingFileException):
      with current_reader.open('dir/relative-nonexistent') as f:
        pass

    with current_reader.open('dir/relative-dotdot') as f:
      self.assertEquals('Hello World.\u2764'.encode('utf-8'), f.read())

  def test_integration(self):
    self.assertEqual(set(), self.git.changed_files())
    self.assertEqual({'README'}, self.git.changed_files(from_commit='HEAD^'))

    tip_sha = self.git.commit_id
    self.assertTrue(tip_sha)

    self.assertTrue(tip_sha in self.git.changelog())

    merge_base = self.git.merge_base()
    self.assertTrue(merge_base)

    self.assertTrue(merge_base in self.git.changelog())

    with self.assertRaises(Scm.LocalException):
      self.git.server_url

    with environment_as(GIT_DIR=self.gitdir, GIT_WORK_TREE=self.worktree):
      with self.mkremote('origin') as origin_uri:
        # We shouldn't be fooled by remotes with origin in their name.
        with self.mkremote('temp_origin'):
          origin_url = self.git.server_url
          self.assertEqual(origin_url, origin_uri)

    self.assertTrue(self.git.tag_name.startswith('first-'), msg='un-annotated tags should be found')
    self.assertEqual('master', self.git.branch_name)

    def edit_readme():
      with open(self.readme_file, 'a') as fp:
        fp.write('More data.')

    edit_readme()
    with open(os.path.join(self.worktree, 'INSTALL'), 'w') as untracked:
      untracked.write('make install')
    self.assertEqual({'README'}, self.git.changed_files())
    self.assertEqual({'README', 'INSTALL'}, self.git.changed_files(include_untracked=True))

    # Confirm that files outside of a given relative_to path are ignored
    self.assertEqual(set(), self.git.changed_files(relative_to='non-existent'))

    self.git.commit('API Changes.')
    try:
      # These changes should be rejected because our branch point from origin is 1 commit behind
      # the changes pushed there in clone 2.
      self.git.push()
    except Scm.RemoteException:
      with environment_as(GIT_DIR=self.gitdir, GIT_WORK_TREE=self.worktree):
        subprocess.check_call(['git', 'reset', '--hard', 'depot/master'])
      self.git.refresh()
      edit_readme()

    self.git.commit('''API '"' " Changes.''')
    self.git.push()
    # HEAD is merged into master
    self.assertEqual(self.git.commit_date(self.git.merge_base()), self.git.commit_date('HEAD'))
    self.assertEqual(self.git.commit_date('HEAD'), self.git.commit_date('HEAD'))
    self.git.tag('second', message='''Tagged ' " Changes''')

    with temporary_dir() as clone:
      with pushd(clone):
        self.init_repo('origin', self.origin)
        subprocess.check_call(['git', 'pull', '--tags', 'origin', 'master:master'])

        with open(os.path.realpath('README')) as readme:
          self.assertEqual('--More data.', readme.read())

        git = Git()

        # Check that we can pick up committed and uncommitted changes.
        with safe_open(os.path.realpath('CHANGES'), 'w') as changes:
          changes.write('none')
        subprocess.check_call(['git', 'add', 'CHANGES'])
        self.assertEqual({'README', 'CHANGES'}, git.changed_files(from_commit='first'))

        self.assertEqual('master', git.branch_name)
        self.assertEqual('second', git.tag_name, msg='annotated tags should be found')

  def test_detect_worktree(self):
    with temporary_dir() as _clone:
      with pushd(_clone):
        clone = os.path.realpath(_clone)

        self.init_repo('origin', self.origin)
        subprocess.check_call(['git', 'pull', '--tags', 'origin', 'master:master'])

        def worktree_relative_to(cwd, expected):
          # Given a cwd relative to the worktree, tests that the worktree is detected as 'expected'.
          orig_cwd = os.getcwd()
          try:
            abs_cwd = os.path.join(clone, cwd)
            if not os.path.isdir(abs_cwd):
              os.mkdir(abs_cwd)
            os.chdir(abs_cwd)
            actual = Git.detect_worktree()
            self.assertEqual(expected, actual)
          finally:
            os.chdir(orig_cwd)

        worktree_relative_to('..', None)
        worktree_relative_to('.', clone)
        worktree_relative_to('is', clone)
        worktree_relative_to('is/a', clone)
        worktree_relative_to('is/a/dir', clone)

  def test_detect_worktree_no_cwd(self):
    with temporary_dir() as _clone:
      with pushd(_clone):
        clone = os.path.realpath(_clone)

        self.init_repo('origin', self.origin)
        subprocess.check_call(['git', 'pull', '--tags', 'origin', 'master:master'])

        def worktree_relative_to(some_dir, expected):
          # Given a directory relative to the worktree, tests that the worktree is detected as 'expected'.
          subdir = os.path.join(clone, some_dir)
          if not os.path.isdir(subdir):
            os.mkdir(subdir)
          actual = Git.detect_worktree(subdir=subdir)
          self.assertEqual(expected, actual)

        worktree_relative_to('..', None)
        worktree_relative_to('.', clone)
        worktree_relative_to('is', clone)
        worktree_relative_to('is/a', clone)
        worktree_relative_to('is/a/dir', clone)

  @property
  def test_changes_in(self):
    """Test finding changes in a diffspecs

    To some extent this is just testing functionality of git not pants, since all pants says
    is that it will pass the diffspec to git diff-tree, but this should serve to at least document
    the functionality we belive works.
    """
    with environment_as(GIT_DIR=self.gitdir, GIT_WORK_TREE=self.worktree):
      def commit_contents_to_files(content, *files):
        for path in files:
          with safe_open(os.path.join(self.worktree, path), 'w') as fp:
            fp.write(content)
        subprocess.check_call(['git', 'add', '.'])
        subprocess.check_call(['git', 'commit', '-m', 'change {}'.format(files)])
        return subprocess.check_output(['git', 'rev-parse', 'HEAD']).strip()

      # We can get changes in HEAD or by SHA
      c1 = commit_contents_to_files('1', 'foo')
      self.assertEqual({'foo'}, self.git.changes_in('HEAD'))
      self.assertEqual({'foo'}, self.git.changes_in(c1))

      # Changes in new HEAD, from old-to-new HEAD, in old HEAD, or from old-old-head to new.
      commit_contents_to_files('2', 'bar')
      self.assertEqual({'bar'}, self.git.changes_in('HEAD'))
      self.assertEqual({'bar'}, self.git.changes_in('HEAD^..HEAD'))
      self.assertEqual({'foo'}, self.git.changes_in('HEAD^'))
      self.assertEqual({'foo'}, self.git.changes_in('HEAD~1'))
      self.assertEqual({'foo', 'bar'}, self.git.changes_in('HEAD^^..HEAD'))

      # New commit doesn't change results-by-sha
      self.assertEqual({'foo'}, self.git.changes_in(c1))

      # Files changed in multiple diffs within a range
      c3 = commit_contents_to_files('3', 'foo')
      self.assertEqual({'foo', 'bar'}, self.git.changes_in('{}..{}'.format(c1, c3)))

      # Changes in a tag
      subprocess.check_call(['git', 'tag', 'v1'])
      self.assertEqual({'foo'}, self.git.changes_in('v1'))

      # Introduce a new filename
      c4 = commit_contents_to_files('4', 'baz')
      self.assertEqual({'baz'}, self.git.changes_in('HEAD'))

      # Tag-to-sha
      self.assertEqual({'baz'}, self.git.changes_in('{}..{}'.format('v1', c4)))

      # We can get multiple changes from one ref
      commit_contents_to_files('5', 'foo', 'bar')
      self.assertEqual({'foo', 'bar'}, self.git.changes_in('HEAD'))
      self.assertEqual({'foo', 'bar', 'baz'}, self.git.changes_in('HEAD~4..HEAD'))
      self.assertEqual({'foo', 'bar', 'baz'}, self.git.changes_in('{}..HEAD'.format(c1)))
      self.assertEqual({'foo', 'bar', 'baz'}, self.git.changes_in('{}..{}'.format(c1, c4)))

  def test_changelog_utf8(self):
    with environment_as(GIT_DIR=self.gitdir, GIT_WORK_TREE=self.worktree):
      def commit_contents_to_files(message, encoding, content, *files):
        for path in files:
          with safe_open(os.path.join(self.worktree, path), 'w') as fp:
            fp.write(content)
        subprocess.check_call(['git', 'add', '.'])

        subprocess.check_call(['git', 'config', '--local', '--add', 'i18n.commitencoding',
                               encoding])
        try:
          subprocess.check_call(['git', 'commit', '-m', message.encode(encoding)])
        finally:
          subprocess.check_call(['git', 'config', '--local', '--unset-all', 'i18n.commitencoding'])

        return subprocess.check_output(['git', 'rev-parse', 'HEAD']).strip()

      # Mix in a non-UTF-8 author to all commits to exercise the corner described here does not
      # adversely impact the ability to render the changelog (even if rendering for certain
      # characters is incorrect): http://comments.gmane.org/gmane.comp.version-control.git/262685
      # NB: This method of override requires we include `user.name` and `user.email` even though we
      # only use `user.name` to exercise non-UTF-8.  Without `user.email`, it will be unset and
      # commits can then fail on machines without a proper hostname setup for git to fall back to
      # when concocting a last-ditch `user.email`.
      non_utf8_config = dedent("""
      [user]
        name = Noralf Trønnes
        email = [email protected]
      """).encode('iso-8859-1')

      with open(os.path.join(self.gitdir, 'config'), 'wb') as fp:
        fp.write(non_utf8_config)

      # Note the copyright symbol is used as the non-ascii character in the next 3 commits
      commit_contents_to_files('START1 © END', 'iso-8859-1', '1', 'foo')
      commit_contents_to_files('START2 © END', 'latin1', '1', 'bar')
      commit_contents_to_files('START3 © END', 'utf-8', '1', 'baz')

      commit_contents_to_files('START4 ~ END', 'us-ascii', '1', 'bip')

      # Prove our non-utf-8 encodings were stored in the commit metadata.
      log = subprocess.check_output(['git', 'log', '--format=%e'])
      self.assertEqual(['us-ascii', 'latin1', 'iso-8859-1'], filter(None, log.strip().splitlines()))

      # And show that the git log successfully transcodes all the commits none-the-less to utf-8
      changelog = self.git.changelog()

      # The ascii commit should combine with the iso-8859-1 author an fail to transcode the
      # o-with-stroke character, and so it should be replaced with the utf-8 replacement character
      # \uFFF or �.
      self.assertIn('Noralf Tr�nnes', changelog)
      self.assertIn('Noralf Tr\uFFFDnnes', changelog)

      # For the other 3 commits, each of iso-8859-1, latin1 and utf-8 have an encoding for the
      # o-with-stroke character - \u00F8 or ø - so we should find it;
      self.assertIn('Noralf Trønnes', changelog)
      self.assertIn('Noralf Tr\u00F8nnes', changelog)

      self.assertIn('START1 © END', changelog)
      self.assertIn('START2 © END', changelog)
      self.assertIn('START3 © END', changelog)
      self.assertIn('START4 ~ END', changelog)

  def test_refresh_with_conflict(self):
    with environment_as(GIT_DIR=self.gitdir, GIT_WORK_TREE=self.worktree):
      self.assertEqual(set(), self.git.changed_files())
      self.assertEqual({'README'}, self.git.changed_files(from_commit='HEAD^'))
      self.assertEqual({'README'}, self.git.changes_in('HEAD'))

      # Create a change on this branch that is incompatible with the change to master
      with open(self.readme_file, 'w') as readme:
        readme.write('Conflict')

      subprocess.check_call(['git', 'commit', '-am', 'Conflict'])

      self.assertEquals(set(), self.git.changed_files(include_untracked=True, from_commit='HEAD'))
      with self.assertRaises(Scm.LocalException):
        self.git.refresh(leave_clean=False)
      # The repo is dirty
      self.assertEquals({'README'},
                        self.git.changed_files(include_untracked=True, from_commit='HEAD'))

      with environment_as(GIT_DIR=self.gitdir, GIT_WORK_TREE=self.worktree):
        subprocess.check_call(['git', 'reset', '--hard', 'HEAD'])

      # Now try with leave_clean
      with self.assertRaises(Scm.LocalException):
        self.git.refresh(leave_clean=True)
      # The repo is clean
      self.assertEquals(set(), self.git.changed_files(include_untracked=True, from_commit='HEAD'))

  def test_commit_with_new_untracked_file_adds_file(self):
    new_file = os.path.join(self.worktree, 'untracked_file')

    touch(new_file)

    self.assertEqual({'untracked_file'}, self.git.changed_files(include_untracked=True))

    self.git.add(new_file)

    self.assertEqual({'untracked_file'}, self.git.changed_files())

    self.git.commit('API Changes.')

    self.assertEqual(set(), self.git.changed_files(include_untracked=True))
Exemple #34
0
    def test_integration(self):
        self.assertEqual(set(), self.git.changed_files())
        self.assertEqual({'README'},
                         self.git.changed_files(from_commit='HEAD^'))

        tip_sha = self.git.commit_id
        self.assertTrue(tip_sha)

        self.assertTrue(tip_sha in self.git.changelog())

        merge_base = self.git.merge_base()
        self.assertTrue(merge_base)

        self.assertTrue(merge_base in self.git.changelog())

        with self.assertRaises(Scm.LocalException):
            self.git.server_url

        with environment_as(GIT_DIR=self.gitdir, GIT_WORK_TREE=self.worktree):
            with self.mkremote('origin') as origin_uri:
                # We shouldn't be fooled by remotes with origin in their name.
                with self.mkremote('temp_origin'):
                    origin_url = self.git.server_url
                    self.assertEqual(origin_url, origin_uri)

        self.assertTrue(self.git.tag_name.startswith('first-'),
                        msg='un-annotated tags should be found')
        self.assertEqual('master', self.git.branch_name)

        def edit_readme():
            with open(self.readme_file, 'a') as fp:
                fp.write('More data.')

        edit_readme()
        with open(os.path.join(self.worktree, 'INSTALL'), 'w') as untracked:
            untracked.write('make install')
        self.assertEqual({'README'}, self.git.changed_files())
        self.assertEqual({'README', 'INSTALL'},
                         self.git.changed_files(include_untracked=True))

        # Confirm that files outside of a given relative_to path are ignored
        self.assertEqual(set(),
                         self.git.changed_files(relative_to='non-existent'))

        self.git.commit('API Changes.')
        try:
            # These changes should be rejected because our branch point from origin is 1 commit behind
            # the changes pushed there in clone 2.
            self.git.push()
        except Scm.RemoteException:
            with environment_as(GIT_DIR=self.gitdir,
                                GIT_WORK_TREE=self.worktree):
                subprocess.check_call(
                    ['git', 'reset', '--hard', 'depot/master'])
            self.git.refresh()
            edit_readme()

        self.git.commit('''API '"' " Changes.''')
        self.git.push()
        # HEAD is merged into master
        self.assertEqual(self.git.commit_date(self.git.merge_base()),
                         self.git.commit_date('HEAD'))
        self.assertEqual(self.git.commit_date('HEAD'),
                         self.git.commit_date('HEAD'))
        self.git.tag('second', message='''Tagged ' " Changes''')

        with temporary_dir() as clone:
            with pushd(clone):
                self.init_repo('origin', self.origin)
                subprocess.check_call(
                    ['git', 'pull', '--tags', 'origin', 'master:master'])

                with open(os.path.realpath('README'), 'r') as readme:
                    self.assertEqual('--More data.', readme.read())

                git = Git()

                # Check that we can pick up committed and uncommitted changes.
                with safe_open(os.path.realpath('CHANGES'), 'w') as changes:
                    changes.write('none')
                subprocess.check_call(['git', 'add', 'CHANGES'])
                self.assertEqual({'README', 'CHANGES'},
                                 git.changed_files(from_commit='first'))

                self.assertEqual('master', git.branch_name)
                self.assertEqual('second',
                                 git.tag_name,
                                 msg='annotated tags should be found')
Exemple #35
0
class GitTest(unittest.TestCase):
    @staticmethod
    def init_repo(remote_name, remote):
        # TODO (peiyu) clean this up, use `git_util.initialize_repo`.
        subprocess.check_call(['git', 'init'])
        subprocess.check_call(
            ['git', 'config', 'user.email', '*****@*****.**'])
        subprocess.check_call(['git', 'config', 'user.name', 'Your Name'])
        subprocess.check_call(['git', 'config', 'commit.gpgSign', 'false'])
        subprocess.check_call(['git', 'remote', 'add', remote_name, remote])

    def setUp(self):
        self.origin = safe_mkdtemp()
        with pushd(self.origin):
            subprocess.check_call(['git', 'init', '--bare'])

        self.gitdir = safe_mkdtemp()
        self.worktree = safe_mkdtemp()

        self.readme_file = os.path.join(self.worktree, 'README')

        with environment_as(GIT_DIR=self.gitdir, GIT_WORK_TREE=self.worktree):
            self.init_repo('depot', self.origin)

            touch(self.readme_file)
            subprocess.check_call(['git', 'add', 'README'])
            safe_mkdir(os.path.join(self.worktree, 'dir'))
            with open(os.path.join(self.worktree, 'dir', 'f'), 'w') as f:
                f.write("file in subdir")

            # Make some symlinks
            os.symlink('f',
                       os.path.join(self.worktree, 'dir', 'relative-symlink'))
            os.symlink(
                'no-such-file',
                os.path.join(self.worktree, 'dir', 'relative-nonexistent'))
            os.symlink(
                'dir/f',
                os.path.join(self.worktree, 'dir', 'not-absolute\u2764'))
            os.symlink('../README',
                       os.path.join(self.worktree, 'dir', 'relative-dotdot'))
            os.symlink('dir', os.path.join(self.worktree, 'link-to-dir'))
            os.symlink('README/f', os.path.join(self.worktree, 'not-a-dir'))
            os.symlink('loop1', os.path.join(self.worktree, 'loop2'))
            os.symlink('loop2', os.path.join(self.worktree, 'loop1'))

            subprocess.check_call([
                'git', 'add', 'README', 'dir', 'loop1', 'loop2', 'link-to-dir',
                'not-a-dir'
            ])
            subprocess.check_call([
                'git', 'commit', '-am', 'initial commit with decode -> \x81b'
            ])
            self.initial_rev = subprocess.check_output(
                ['git', 'rev-parse', 'HEAD']).strip()
            subprocess.check_call(['git', 'tag', 'first'])
            subprocess.check_call(['git', 'push', '--tags', 'depot', 'master'])
            subprocess.check_call(
                ['git', 'branch', '--set-upstream-to', 'depot/master'])

            with safe_open(self.readme_file, 'wb') as readme:
                readme.write('Hello World.\u2764'.encode())
            subprocess.check_call(['git', 'commit', '-am', 'Update README.'])

            self.current_rev = subprocess.check_output(
                ['git', 'rev-parse', 'HEAD']).strip()

        self.clone2 = safe_mkdtemp()
        with pushd(self.clone2):
            self.init_repo('origin', self.origin)
            subprocess.check_call(
                ['git', 'pull', '--tags', 'origin', 'master:master'])

            with safe_open(os.path.realpath('README'), 'a') as readme:
                readme.write('--')
            subprocess.check_call(['git', 'commit', '-am', 'Update README 2.'])
            subprocess.check_call(
                ['git', 'push', '--tags', 'origin', 'master'])

        self.git = Git(gitdir=self.gitdir, worktree=self.worktree)

    @contextmanager
    def mkremote(self, remote_name):
        with temporary_dir() as remote_uri:
            subprocess.check_call(
                ['git', 'remote', 'add', remote_name, remote_uri])
            try:
                yield remote_uri
            finally:
                subprocess.check_call(['git', 'remote', 'remove', remote_name])

    def tearDown(self):
        safe_rmtree(self.origin)
        safe_rmtree(self.gitdir)
        safe_rmtree(self.worktree)
        safe_rmtree(self.clone2)

    def test_listdir(self):
        reader = self.git.repo_reader(self.initial_rev)

        for dirname in '.', './.':
            results = reader.listdir(dirname)
            self.assertEqual([
                b'README', b'dir', b'link-to-dir', b'loop1', b'loop2',
                b'not-a-dir'
            ], sorted(results))

        for dirname in 'dir', './dir':
            results = reader.listdir(dirname)
            self.assertEqual([
                b'f', 'not-absolute\u2764'.encode(), b'relative-dotdot',
                b'relative-nonexistent', b'relative-symlink'
            ], sorted(results))

        results = reader.listdir('link-to-dir')
        self.assertEqual([
            b'f', 'not-absolute\u2764'.encode(), b'relative-dotdot',
            b'relative-nonexistent', b'relative-symlink'
        ], sorted(results))

        with self.assertRaises(reader.MissingFileException):
            with reader.listdir('bogus'):
                pass

    def test_lstat(self):
        reader = self.git.repo_reader(self.initial_rev)

        def lstat(*components):
            return type(reader.lstat(os.path.join(*components)))

        self.assertEqual(reader.Symlink, lstat('dir', 'relative-symlink'))
        self.assertEqual(reader.Symlink, lstat('not-a-dir'))
        self.assertEqual(reader.File, lstat('README'))
        self.assertEqual(reader.Dir, lstat('dir'))
        self.assertEqual(type(None), lstat('nope-not-here'))

    def test_readlink(self):
        reader = self.git.repo_reader(self.initial_rev)

        def readlink(*components):
            return reader.readlink(os.path.join(*components))

        self.assertEqual('dir/f', readlink('dir', 'relative-symlink'))
        self.assertEqual(None, readlink('not-a-dir'))
        self.assertEqual(None, readlink('README'))
        self.assertEqual(None, readlink('dir'))
        self.assertEqual(None, readlink('nope-not-here'))

    def test_open(self):
        reader = self.git.repo_reader(self.initial_rev)

        with reader.open('README') as f:
            self.assertEqual(b'', f.read())

        with reader.open('dir/f') as f:
            self.assertEqual(b'file in subdir', f.read())

        with self.assertRaises(reader.MissingFileException):
            with reader.open('no-such-file') as f:
                self.assertEqual(b'', f.read())

        with self.assertRaises(reader.MissingFileException):
            with reader.open('dir/no-such-file') as f:
                pass

        with self.assertRaises(reader.IsDirException):
            with reader.open('dir') as f:
                self.assertEqual(b'', f.read())

        current_reader = self.git.repo_reader(self.current_rev)

        with current_reader.open('README') as f:
            self.assertEqual('Hello World.\u2764'.encode(), f.read())

        with current_reader.open('link-to-dir/f') as f:
            self.assertEqual(b'file in subdir', f.read())

        with current_reader.open('dir/relative-symlink') as f:
            self.assertEqual(b'file in subdir', f.read())

        with self.assertRaises(current_reader.SymlinkLoopException):
            with current_reader.open('loop1') as f:
                pass

        with self.assertRaises(current_reader.MissingFileException):
            with current_reader.open('dir/relative-nonexistent') as f:
                pass

        with self.assertRaises(current_reader.NotADirException):
            with current_reader.open('not-a-dir') as f:
                pass

        with self.assertRaises(current_reader.MissingFileException):
            with current_reader.open('dir/not-absolute\u2764') as f:
                pass

        with self.assertRaises(current_reader.MissingFileException):
            with current_reader.open('dir/relative-nonexistent') as f:
                pass

        with current_reader.open('dir/relative-dotdot') as f:
            self.assertEqual('Hello World.\u2764'.encode(), f.read())

    def test_integration(self):
        self.assertEqual(set(), self.git.changed_files())
        self.assertEqual({'README'},
                         self.git.changed_files(from_commit='HEAD^'))

        tip_sha = self.git.commit_id
        self.assertTrue(tip_sha)

        self.assertTrue(tip_sha in self.git.changelog())

        merge_base = self.git.merge_base()
        self.assertTrue(merge_base)

        self.assertTrue(merge_base in self.git.changelog())

        with self.assertRaises(Scm.LocalException):
            self.git.server_url

        with environment_as(GIT_DIR=self.gitdir, GIT_WORK_TREE=self.worktree):
            with self.mkremote('origin') as origin_uri:
                # We shouldn't be fooled by remotes with origin in their name.
                with self.mkremote('temp_origin'):
                    origin_url = self.git.server_url
                    self.assertEqual(origin_url, origin_uri)

        self.assertTrue(self.git.tag_name.startswith('first-'),
                        msg='un-annotated tags should be found')
        self.assertEqual('master', self.git.branch_name)

        def edit_readme():
            with open(self.readme_file, 'a') as fp:
                fp.write('More data.')

        edit_readme()
        with open(os.path.join(self.worktree, 'INSTALL'), 'w') as untracked:
            untracked.write('make install')
        self.assertEqual({'README'}, self.git.changed_files())
        self.assertEqual({'README', 'INSTALL'},
                         self.git.changed_files(include_untracked=True))

        # Confirm that files outside of a given relative_to path are ignored
        self.assertEqual(set(),
                         self.git.changed_files(relative_to='non-existent'))

        self.git.commit('API Changes.')
        try:
            # These changes should be rejected because our branch point from origin is 1 commit behind
            # the changes pushed there in clone 2.
            self.git.push()
        except Scm.RemoteException:
            with environment_as(GIT_DIR=self.gitdir,
                                GIT_WORK_TREE=self.worktree):
                subprocess.check_call(
                    ['git', 'reset', '--hard', 'depot/master'])
            self.git.refresh()
            edit_readme()

        self.git.commit('''API '"' " Changes.''')
        self.git.push()
        # HEAD is merged into master
        self.assertEqual(self.git.commit_date(self.git.merge_base()),
                         self.git.commit_date('HEAD'))
        self.assertEqual(self.git.commit_date('HEAD'),
                         self.git.commit_date('HEAD'))
        self.git.tag('second', message='''Tagged ' " Changes''')

        with temporary_dir() as clone:
            with pushd(clone):
                self.init_repo('origin', self.origin)
                subprocess.check_call(
                    ['git', 'pull', '--tags', 'origin', 'master:master'])

                with open(os.path.realpath('README'), 'r') as readme:
                    self.assertEqual('--More data.', readme.read())

                git = Git()

                # Check that we can pick up committed and uncommitted changes.
                with safe_open(os.path.realpath('CHANGES'), 'w') as changes:
                    changes.write('none')
                subprocess.check_call(['git', 'add', 'CHANGES'])
                self.assertEqual({'README', 'CHANGES'},
                                 git.changed_files(from_commit='first'))

                self.assertEqual('master', git.branch_name)
                self.assertEqual('second',
                                 git.tag_name,
                                 msg='annotated tags should be found')

    def test_detect_worktree(self):
        with temporary_dir() as _clone:
            with pushd(_clone):
                clone = os.path.realpath(_clone)

                self.init_repo('origin', self.origin)
                subprocess.check_call(
                    ['git', 'pull', '--tags', 'origin', 'master:master'])

                def worktree_relative_to(cwd, expected):
                    # Given a cwd relative to the worktree, tests that the worktree is detected as 'expected'.
                    orig_cwd = os.getcwd()
                    try:
                        abs_cwd = os.path.join(clone, cwd)
                        if not os.path.isdir(abs_cwd):
                            os.mkdir(abs_cwd)
                        os.chdir(abs_cwd)
                        actual = Git.detect_worktree()
                        self.assertEqual(expected, actual)
                    finally:
                        os.chdir(orig_cwd)

                worktree_relative_to('..', None)
                worktree_relative_to('.', clone)
                worktree_relative_to('is', clone)
                worktree_relative_to('is/a', clone)
                worktree_relative_to('is/a/dir', clone)

    def test_detect_worktree_no_cwd(self):
        with temporary_dir() as _clone:
            with pushd(_clone):
                clone = os.path.realpath(_clone)

                self.init_repo('origin', self.origin)
                subprocess.check_call(
                    ['git', 'pull', '--tags', 'origin', 'master:master'])

                def worktree_relative_to(some_dir, expected):
                    # Given a directory relative to the worktree, tests that the worktree is detected as 'expected'.
                    subdir = os.path.join(clone, some_dir)
                    if not os.path.isdir(subdir):
                        os.mkdir(subdir)
                    actual = Git.detect_worktree(subdir=subdir)
                    self.assertEqual(expected, actual)

                worktree_relative_to('..', None)
                worktree_relative_to('.', clone)
                worktree_relative_to('is', clone)
                worktree_relative_to('is/a', clone)
                worktree_relative_to('is/a/dir', clone)

    @property
    def test_changes_in(self):
        """Test finding changes in a diffspecs

    To some extent this is just testing functionality of git not pants, since all pants says
    is that it will pass the diffspec to git diff-tree, but this should serve to at least document
    the functionality we belive works.
    """
        with environment_as(GIT_DIR=self.gitdir, GIT_WORK_TREE=self.worktree):

            def commit_contents_to_files(content, *files):
                for path in files:
                    with safe_open(os.path.join(self.worktree, path),
                                   'w') as fp:
                        fp.write(content)
                subprocess.check_call(['git', 'add', '.'])
                subprocess.check_call(
                    ['git', 'commit', '-m', f'change {files}'])
                return subprocess.check_output(['git', 'rev-parse',
                                                'HEAD']).strip()

            # We can get changes in HEAD or by SHA
            c1 = commit_contents_to_files('1', 'foo')
            self.assertEqual({'foo'}, self.git.changes_in('HEAD'))
            self.assertEqual({'foo'}, self.git.changes_in(c1))

            # Changes in new HEAD, from old-to-new HEAD, in old HEAD, or from old-old-head to new.
            commit_contents_to_files('2', 'bar')
            self.assertEqual({'bar'}, self.git.changes_in('HEAD'))
            self.assertEqual({'bar'}, self.git.changes_in('HEAD^..HEAD'))
            self.assertEqual({'foo'}, self.git.changes_in('HEAD^'))
            self.assertEqual({'foo'}, self.git.changes_in('HEAD~1'))
            self.assertEqual({'foo', 'bar'},
                             self.git.changes_in('HEAD^^..HEAD'))

            # New commit doesn't change results-by-sha
            self.assertEqual({'foo'}, self.git.changes_in(c1))

            # Files changed in multiple diffs within a range
            c3 = commit_contents_to_files('3', 'foo')
            self.assertEqual({'foo', 'bar'},
                             self.git.changes_in(f'{c1}..{c3}'))

            # Changes in a tag
            subprocess.check_call(['git', 'tag', 'v1'])
            self.assertEqual({'foo'}, self.git.changes_in('v1'))

            # Introduce a new filename
            c4 = commit_contents_to_files('4', 'baz')
            self.assertEqual({'baz'}, self.git.changes_in('HEAD'))

            # Tag-to-sha
            self.assertEqual({'baz'}, self.git.changes_in(f"v1..{c4}"))

            # We can get multiple changes from one ref
            commit_contents_to_files('5', 'foo', 'bar')
            self.assertEqual({'foo', 'bar'}, self.git.changes_in('HEAD'))
            self.assertEqual({'foo', 'bar', 'baz'},
                             self.git.changes_in('HEAD~4..HEAD'))
            self.assertEqual({'foo', 'bar', 'baz'},
                             self.git.changes_in(f'{c1}..HEAD'))
            self.assertEqual({'foo', 'bar', 'baz'},
                             self.git.changes_in(f'{c1}..{c4}'))

    def test_changelog_utf8(self):
        with environment_as(GIT_DIR=self.gitdir, GIT_WORK_TREE=self.worktree):

            def commit_contents_to_files(message, encoding, content, *files):
                for path in files:
                    with safe_open(os.path.join(self.worktree, path),
                                   'w') as fp:
                        fp.write(content)
                subprocess.check_call(['git', 'add', '.'])

                subprocess.check_call([
                    'git', 'config', '--local', '--add', 'i18n.commitencoding',
                    encoding
                ])
                subprocess.check_call(
                    ['git', 'config', '--local', 'commit.gpgSign', 'false'])
                try:
                    subprocess.check_call(
                        ['git', 'commit', '-m',
                         message.encode(encoding)])
                finally:
                    subprocess.check_call([
                        'git', 'config', '--local', '--unset-all',
                        'i18n.commitencoding'
                    ])

                return subprocess.check_output(['git', 'rev-parse',
                                                'HEAD']).strip()

            # Mix in a non-UTF-8 author to all commits to exercise the corner described here does not
            # adversely impact the ability to render the changelog (even if rendering for certain
            # characters is incorrect): http://comments.gmane.org/gmane.comp.version-control.git/262685
            # NB: This method of override requires we include `user.name` and `user.email` even though we
            # only use `user.name` to exercise non-UTF-8.  Without `user.email`, it will be unset and
            # commits can then fail on machines without a proper hostname setup for git to fall back to
            # when concocting a last-ditch `user.email`.
            non_utf8_config = dedent("""
      [user]
        name = Noralf Trønnes
        email = [email protected]
      """).encode('iso-8859-1')

            with open(os.path.join(self.gitdir, 'config'), 'wb') as fp:
                fp.write(non_utf8_config)

            # Note the copyright symbol is used as the non-ascii character in the next 3 commits
            commit_contents_to_files('START1 © END', 'iso-8859-1', '1', 'foo')
            commit_contents_to_files('START2 © END', 'latin1', '1', 'bar')
            commit_contents_to_files('START3 © END', 'utf-8', '1', 'baz')

            commit_contents_to_files('START4 ~ END', 'us-ascii', '1', 'bip')

            # Prove our non-utf-8 encodings were stored in the commit metadata.
            log = subprocess.check_output(['git', 'log', '--format=%e'])
            self.assertEqual([b'us-ascii', b'latin1', b'iso-8859-1'],
                             [_f for _f in log.strip().splitlines() if _f])

            # And show that the git log successfully transcodes all the commits none-the-less to utf-8
            changelog = self.git.changelog()

            # The ascii commit should combine with the iso-8859-1 author an fail to transcode the
            # o-with-stroke character, and so it should be replaced with the utf-8 replacement character
            # \uFFF or �.
            self.assertIn('Noralf Tr�nnes', changelog)
            self.assertIn('Noralf Tr\uFFFDnnes', changelog)

            # For the other 3 commits, each of iso-8859-1, latin1 and utf-8 have an encoding for the
            # o-with-stroke character - \u00F8 or ø - so we should find it;
            self.assertIn('Noralf Trønnes', changelog)
            self.assertIn('Noralf Tr\u00F8nnes', changelog)

            self.assertIn('START1 © END', changelog)
            self.assertIn('START2 © END', changelog)
            self.assertIn('START3 © END', changelog)
            self.assertIn('START4 ~ END', changelog)

    def test_refresh_with_conflict(self):
        with environment_as(GIT_DIR=self.gitdir, GIT_WORK_TREE=self.worktree):
            self.assertEqual(set(), self.git.changed_files())
            self.assertEqual({'README'},
                             self.git.changed_files(from_commit='HEAD^'))
            self.assertEqual({'README'}, self.git.changes_in('HEAD'))

            # Create a change on this branch that is incompatible with the change to master
            with open(self.readme_file, 'w') as readme:
                readme.write('Conflict')

            subprocess.check_call(['git', 'commit', '-am', 'Conflict'])

            self.assertEqual(
                set(),
                self.git.changed_files(include_untracked=True,
                                       from_commit='HEAD'))
            with self.assertRaises(Scm.LocalException):
                self.git.refresh(leave_clean=False)
            # The repo is dirty
            self.assertEqual({'README'},
                             self.git.changed_files(include_untracked=True,
                                                    from_commit='HEAD'))

            with environment_as(GIT_DIR=self.gitdir,
                                GIT_WORK_TREE=self.worktree):
                subprocess.check_call(['git', 'reset', '--hard', 'HEAD'])

            # Now try with leave_clean
            with self.assertRaises(Scm.LocalException):
                self.git.refresh(leave_clean=True)
            # The repo is clean
            self.assertEqual(
                set(),
                self.git.changed_files(include_untracked=True,
                                       from_commit='HEAD'))

    def test_commit_with_new_untracked_file_adds_file(self):
        new_file = os.path.join(self.worktree, 'untracked_file')

        touch(new_file)

        self.assertEqual({'untracked_file'},
                         self.git.changed_files(include_untracked=True))

        self.git.add(new_file)

        self.assertEqual({'untracked_file'}, self.git.changed_files())

        self.git.commit('API Changes.')

        self.assertEqual(set(), self.git.changed_files(include_untracked=True))
Exemple #36
0
class GitTest(unittest.TestCase):
  @staticmethod
  def init_repo(remote_name, remote):
    subprocess.check_call(['git', 'init'])
    subprocess.check_call(['git', 'config', 'user.email', '*****@*****.**'])
    subprocess.check_call(['git', 'config', 'user.name', 'Your Name'])
    subprocess.check_call(['git', 'remote', 'add', remote_name, remote])

  @classmethod
  def setUp(self):
    self.origin = safe_mkdtemp()
    with pushd(self.origin):
      subprocess.check_call(['git', 'init', '--bare'])

    self.gitdir = safe_mkdtemp()
    self.worktree = safe_mkdtemp()

    self.readme_file = os.path.join(self.worktree, 'README')

    with environment_as(GIT_DIR=self.gitdir, GIT_WORK_TREE=self.worktree):
      self.init_repo('depot', self.origin)

      touch(self.readme_file)
      subprocess.check_call(['git', 'add', 'README'])
      subprocess.check_call(['git', 'commit', '-am', 'initial commit with decode -> \x81b'])
      subprocess.check_call(['git', 'tag', 'first'])
      subprocess.check_call(['git', 'push', '--tags', 'depot', 'master'])
      subprocess.check_call(['git', 'branch', '--set-upstream', 'master', 'depot/master'])

      with safe_open(self.readme_file, 'w') as readme:
        readme.write('Hello World.')
      subprocess.check_call(['git', 'commit', '-am', 'Update README.'])

    self.clone2 = safe_mkdtemp()
    with pushd(self.clone2):
      self.init_repo('origin', self.origin)
      subprocess.check_call(['git', 'pull', '--tags', 'origin', 'master:master'])

      with safe_open(os.path.realpath('README'), 'a') as readme:
        readme.write('--')
      subprocess.check_call(['git', 'commit', '-am', 'Update README 2.'])
      subprocess.check_call(['git', 'push', '--tags', 'origin', 'master'])

    self.git = Git(gitdir=self.gitdir, worktree=self.worktree)

  @staticmethod
  @contextmanager
  def mkremote(remote_name):
    with temporary_dir() as remote_uri:
      subprocess.check_call(['git', 'remote', 'add', remote_name, remote_uri])
      try:
        yield remote_uri
      finally:
        subprocess.check_call(['git', 'remote', 'remove', remote_name])

  @classmethod
  def tearDown(self):
    safe_rmtree(self.origin)
    safe_rmtree(self.gitdir)
    safe_rmtree(self.worktree)
    safe_rmtree(self.clone2)

  def test_integration(self):
    self.assertEqual(set(), self.git.changed_files())
    self.assertEqual(set(['README']), self.git.changed_files(from_commit='HEAD^'))

    tip_sha = self.git.commit_id
    self.assertTrue(tip_sha)

    self.assertTrue(tip_sha in self.git.changelog())

    merge_base = self.git.merge_base()
    self.assertTrue(merge_base)

    self.assertTrue(merge_base in self.git.changelog())

    with pytest.raises(Scm.LocalException):
      self.git.server_url

    with environment_as(GIT_DIR=self.gitdir, GIT_WORK_TREE=self.worktree):
      with self.mkremote('origin') as origin_uri:
        origin_url = self.git.server_url
        self.assertEqual(origin_url, origin_uri)

    self.assertTrue(self.git.tag_name.startswith('first-'), msg='un-annotated tags should be found')
    self.assertEqual('master', self.git.branch_name)

    def edit_readme():
      with open(self.readme_file, 'a') as readme:
        readme.write('More data.')

    edit_readme()
    with open(os.path.join(self.worktree, 'INSTALL'), 'w') as untracked:
      untracked.write('make install')
    self.assertEqual(set(['README']), self.git.changed_files())
    self.assertEqual(set(['README', 'INSTALL']), self.git.changed_files(include_untracked=True))

    # confirm that files outside of a given relative_to path are ignored
    self.assertEqual(set(), self.git.changed_files(relative_to='non-existent'))

    self.git.commit('API Changes.')
    try:
      # These changes should be rejected because our branch point from origin is 1 commit behind
      # the changes pushed there in clone 2.
      self.git.push()
    except Scm.RemoteException:
      with environment_as(GIT_DIR=self.gitdir, GIT_WORK_TREE=self.worktree):
        subprocess.check_call(['git', 'reset', '--hard', 'depot/master'])
      self.git.refresh()
      edit_readme()

    self.git.commit('''API '"' " Changes.''')
    self.git.push()
    # HEAD is merged into master
    self.assertEqual(self.git.commit_date(self.git.merge_base()), self.git.commit_date('HEAD'))
    self.assertEqual(self.git.commit_date('HEAD'), self.git.commit_date('HEAD'))
    self.git.tag('second', message='''Tagged ' " Changes''')

    with temporary_dir() as clone:
      with pushd(clone):
        self.init_repo('origin', self.origin)
        subprocess.check_call(['git', 'pull', '--tags', 'origin', 'master:master'])

        with open(os.path.realpath('README')) as readme:
          self.assertEqual('--More data.', readme.read())

        git = Git()

        # Check that we can pick up committed and uncommitted changes.
        with safe_open(os.path.realpath('CHANGES'), 'w') as changes:
          changes.write('none')
        subprocess.check_call(['git', 'add', 'CHANGES'])
        self.assertEqual(set(['README', 'CHANGES']), git.changed_files(from_commit='first'))

        self.assertEqual('master', git.branch_name)
        self.assertEqual('second', git.tag_name, msg='annotated tags should be found')

  def test_detect_worktree(self):
    with temporary_dir() as _clone:
      with pushd(_clone):
        clone = os.path.realpath(_clone)

        self.init_repo('origin', self.origin)
        subprocess.check_call(['git', 'pull', '--tags', 'origin', 'master:master'])

        def worktree_relative_to(cwd, expected):
          """Given a cwd relative to the worktree, tests that the worktree is detected as 'expected'."""
          orig_cwd = os.getcwd()
          try:
            abs_cwd = os.path.join(clone, cwd)
            if not os.path.isdir(abs_cwd):
              os.mkdir(abs_cwd)
            os.chdir(abs_cwd)
            actual = Git.detect_worktree()
            self.assertEqual(expected, actual)
          finally:
            os.chdir(orig_cwd)

        worktree_relative_to('..', None)
        worktree_relative_to('.', clone)
        worktree_relative_to('is', clone)
        worktree_relative_to('is/a', clone)
        worktree_relative_to('is/a/dir', clone)

  def test_diffspec(self):
    """Test finding changes in a diffspecs

    To some extent this is just testing functionality of git not pants, since all pants says
    is that it will pass the diffspec to git diff-tree, but this should serve to at least document
    the functionality we belive works.
    """
    with environment_as(GIT_DIR=self.gitdir, GIT_WORK_TREE=self.worktree):
      def commit_contents_to_files(content, *files):
        for path in files:
          with safe_open(os.path.join(self.worktree, path), 'w') as fp:
            fp.write(content)
        subprocess.check_call(['git', 'add', '.'])
        subprocess.check_call(['git', 'commit', '-m', 'change '+path])
        return subprocess.check_output(['git', 'rev-parse', 'HEAD']).strip()

      # We can get changes in HEAD or by SHA
      c1 = commit_contents_to_files('1', 'foo')
      self.assertEqual(set(['foo']), self.git.changes_in('HEAD'))
      self.assertEqual(set(['foo']), self.git.changes_in(c1))

      # Changes in new HEAD, from old-to-new HEAD, in old HEAD, or from old-old-head to new.
      c2 = commit_contents_to_files('2', 'bar')
      self.assertEqual(set(['bar']), self.git.changes_in('HEAD'))
      self.assertEqual(set(['bar']), self.git.changes_in('HEAD^..HEAD'))
      self.assertEqual(set(['foo']), self.git.changes_in('HEAD^'))
      self.assertEqual(set(['foo']), self.git.changes_in('HEAD~1'))
      self.assertEqual(set(['foo', 'bar']), self.git.changes_in('HEAD^^..HEAD'))

      # New commit doesn't change results-by-sha
      self.assertEqual(set(['foo']), self.git.changes_in(c1))

      # Files changed in multiple diffs within a range
      c3 = commit_contents_to_files('3', 'foo')
      self.assertEqual(set(['foo', 'bar']), self.git.changes_in('{}..{}'.format(c1, c3)))

      # Changes in a tag
      subprocess.check_call(['git', 'tag', 'v1'])
      self.assertEqual(set(['foo']), self.git.changes_in('v1'))

      # Introduce a new filename
      c4 = commit_contents_to_files('4', 'baz')
      self.assertEqual(set(['baz']), self.git.changes_in('HEAD'))

      # Tag-to-sha
      self.assertEqual(set(['baz']), self.git.changes_in('{}..{}'.format('v1', c4)))

      # We can get multiple changes from one ref
      c5 = commit_contents_to_files('5', 'foo', 'bar')
      self.assertEqual(set(['foo', 'bar']), self.git.changes_in('HEAD'))
      self.assertEqual(set(['foo', 'bar', 'baz']), self.git.changes_in('HEAD~4..HEAD'))
      self.assertEqual(set(['foo', 'bar', 'baz']), self.git.changes_in('{}..HEAD'.format(c1)))
      self.assertEqual(set(['foo', 'bar', 'baz']), self.git.changes_in('{}..{}'.format(c1, c4)))

  def test_refresh_with_conflict(self):
    with environment_as(GIT_DIR=self.gitdir, GIT_WORK_TREE=self.worktree):
      self.assertEqual(set(), self.git.changed_files())
      self.assertEqual(set(['README']), self.git.changed_files(from_commit='HEAD^'))
      self.assertEqual(set(['README']), self.git.changes_in('HEAD'))

      # Create a change on this branch that is incompatible with the change to master
      with open(self.readme_file, 'w') as readme:
        readme.write('Conflict')

      subprocess.check_call(['git', 'commit', '-am', 'Conflict'])

      self.assertEquals(set([]), self.git.changed_files(include_untracked=True, from_commit='HEAD'))
      with self.assertRaises(Scm.LocalException):
        self.git.refresh(leave_clean=False)
      # The repo is dirty
      self.assertEquals(set(['README']), self.git.changed_files(include_untracked=True, from_commit='HEAD'))

      with environment_as(GIT_DIR=self.gitdir, GIT_WORK_TREE=self.worktree):
        subprocess.check_call(['git', 'reset', '--hard', 'HEAD'])

      # Now try with leave_clean
      with self.assertRaises(Scm.LocalException):
        self.git.refresh(leave_clean=True)
      # The repo is clean
      self.assertEquals(set([]), self.git.changed_files(include_untracked=True, from_commit='HEAD'))
Exemple #37
0
class GitTest(unittest.TestCase):
    @staticmethod
    def init_repo(remote_name, remote):
        subprocess.check_call(['git', 'init'])
        subprocess.check_call(
            ['git', 'config', 'user.email', '*****@*****.**'])
        subprocess.check_call(['git', 'config', 'user.name', 'Your Name'])
        subprocess.check_call(['git', 'remote', 'add', remote_name, remote])

    @classmethod
    def setUp(self):
        self.origin = safe_mkdtemp()
        with pushd(self.origin):
            subprocess.check_call(['git', 'init', '--bare'])

        self.gitdir = safe_mkdtemp()
        self.worktree = safe_mkdtemp()

        self.readme_file = os.path.join(self.worktree, 'README')

        with environment_as(GIT_DIR=self.gitdir, GIT_WORK_TREE=self.worktree):
            self.init_repo('depot', self.origin)

            touch(self.readme_file)
            subprocess.check_call(['git', 'add', 'README'])
            subprocess.check_call([
                'git', 'commit', '-am', 'initial commit with decode -> \x81b'
            ])
            subprocess.check_call(['git', 'tag', 'first'])
            subprocess.check_call(['git', 'push', '--tags', 'depot', 'master'])
            subprocess.check_call(
                ['git', 'branch', '--set-upstream', 'master', 'depot/master'])

            with safe_open(self.readme_file, 'w') as readme:
                readme.write('Hello World.')
            subprocess.check_call(['git', 'commit', '-am', 'Update README.'])

        self.clone2 = safe_mkdtemp()
        with pushd(self.clone2):
            self.init_repo('origin', self.origin)
            subprocess.check_call(
                ['git', 'pull', '--tags', 'origin', 'master:master'])

            with safe_open(os.path.realpath('README'), 'a') as readme:
                readme.write('--')
            subprocess.check_call(['git', 'commit', '-am', 'Update README 2.'])
            subprocess.check_call(
                ['git', 'push', '--tags', 'origin', 'master'])

        self.git = Git(gitdir=self.gitdir, worktree=self.worktree)

    @staticmethod
    @contextmanager
    def mkremote(remote_name):
        with temporary_dir() as remote_uri:
            subprocess.check_call(
                ['git', 'remote', 'add', remote_name, remote_uri])
            try:
                yield remote_uri
            finally:
                subprocess.check_call(['git', 'remote', 'remove', remote_name])

    @classmethod
    def tearDown(self):
        safe_rmtree(self.origin)
        safe_rmtree(self.gitdir)
        safe_rmtree(self.worktree)
        safe_rmtree(self.clone2)

    def test_integration(self):
        self.assertEqual(set(), self.git.changed_files())
        self.assertEqual(set(['README']),
                         self.git.changed_files(from_commit='HEAD^'))

        tip_sha = self.git.commit_id
        self.assertTrue(tip_sha)

        self.assertTrue(tip_sha in self.git.changelog())

        merge_base = self.git.merge_base()
        self.assertTrue(merge_base)

        self.assertTrue(merge_base in self.git.changelog())

        with pytest.raises(Scm.LocalException):
            self.git.server_url

        with environment_as(GIT_DIR=self.gitdir, GIT_WORK_TREE=self.worktree):
            with self.mkremote('origin') as origin_uri:
                origin_url = self.git.server_url
                self.assertEqual(origin_url, origin_uri)

        self.assertTrue(self.git.tag_name.startswith('first-'),
                        msg='un-annotated tags should be found')
        self.assertEqual('master', self.git.branch_name)

        def edit_readme():
            with open(self.readme_file, 'a') as readme:
                readme.write('More data.')

        edit_readme()
        with open(os.path.join(self.worktree, 'INSTALL'), 'w') as untracked:
            untracked.write('make install')
        self.assertEqual(set(['README']), self.git.changed_files())
        self.assertEqual(set(['README', 'INSTALL']),
                         self.git.changed_files(include_untracked=True))

        # confirm that files outside of a given relative_to path are ignored
        self.assertEqual(set(),
                         self.git.changed_files(relative_to='non-existent'))

        self.git.commit('API Changes.')
        try:
            # These changes should be rejected because our branch point from origin is 1 commit behind
            # the changes pushed there in clone 2.
            self.git.push()
        except Scm.RemoteException:
            with environment_as(GIT_DIR=self.gitdir,
                                GIT_WORK_TREE=self.worktree):
                subprocess.check_call(
                    ['git', 'reset', '--hard', 'depot/master'])
            self.git.refresh()
            edit_readme()

        self.git.commit('''API '"' " Changes.''')
        self.git.push()
        # HEAD is merged into master
        self.assertEqual(self.git.commit_date(self.git.merge_base()),
                         self.git.commit_date('HEAD'))
        self.assertEqual(self.git.commit_date('HEAD'),
                         self.git.commit_date('HEAD'))
        self.git.tag('second', message='''Tagged ' " Changes''')

        with temporary_dir() as clone:
            with pushd(clone):
                self.init_repo('origin', self.origin)
                subprocess.check_call(
                    ['git', 'pull', '--tags', 'origin', 'master:master'])

                with open(os.path.realpath('README')) as readme:
                    self.assertEqual('--More data.', readme.read())

                git = Git()

                # Check that we can pick up committed and uncommitted changes.
                with safe_open(os.path.realpath('CHANGES'), 'w') as changes:
                    changes.write('none')
                subprocess.check_call(['git', 'add', 'CHANGES'])
                self.assertEqual(set(['README', 'CHANGES']),
                                 git.changed_files(from_commit='first'))

                self.assertEqual('master', git.branch_name)
                self.assertEqual('second',
                                 git.tag_name,
                                 msg='annotated tags should be found')

    def test_detect_worktree(self):
        with temporary_dir() as _clone:
            with pushd(_clone):
                clone = os.path.realpath(_clone)

                self.init_repo('origin', self.origin)
                subprocess.check_call(
                    ['git', 'pull', '--tags', 'origin', 'master:master'])

                def worktree_relative_to(cwd, expected):
                    """Given a cwd relative to the worktree, tests that the worktree is detected as 'expected'."""
                    orig_cwd = os.getcwd()
                    try:
                        abs_cwd = os.path.join(clone, cwd)
                        if not os.path.isdir(abs_cwd):
                            os.mkdir(abs_cwd)
                        os.chdir(abs_cwd)
                        actual = Git.detect_worktree()
                        self.assertEqual(expected, actual)
                    finally:
                        os.chdir(orig_cwd)

                worktree_relative_to('..', None)
                worktree_relative_to('.', clone)
                worktree_relative_to('is', clone)
                worktree_relative_to('is/a', clone)
                worktree_relative_to('is/a/dir', clone)

    def test_refresh_with_conflict(self):
        with environment_as(GIT_DIR=self.gitdir, GIT_WORK_TREE=self.worktree):
            self.assertEqual(set(), self.git.changed_files())
            self.assertEqual(set(['README']),
                             self.git.changed_files(from_commit='HEAD^'))

            # Create a change on this branch that is incompatible with the change to master
            with open(self.readme_file, 'w') as readme:
                readme.write('Conflict')

            subprocess.check_call(['git', 'commit', '-am', 'Conflict'])

            self.assertEquals(
                set([]),
                self.git.changed_files(include_untracked=True,
                                       from_commit='HEAD'))
            with self.assertRaises(Scm.LocalException):
                self.git.refresh(leave_clean=False)
            # The repo is dirty
            self.assertEquals(
                set(['README']),
                self.git.changed_files(include_untracked=True,
                                       from_commit='HEAD'))

            with environment_as(GIT_DIR=self.gitdir,
                                GIT_WORK_TREE=self.worktree):
                subprocess.check_call(['git', 'reset', '--hard', 'HEAD'])

            # Now try with leave_clean
            with self.assertRaises(Scm.LocalException):
                self.git.refresh(leave_clean=True)
            # The repo is clean
            self.assertEquals(
                set([]),
                self.git.changed_files(include_untracked=True,
                                       from_commit='HEAD'))
Exemple #38
0
 def test_detect_worktree_invalid_executable_git(self):
     with self.executable_git() as git:
         self.assertIsNone(Git.detect_worktree())
         self.assertIsNone(Git.detect_worktree(binary=git))
 def setUp(self):
     super(ScmBuildFileTest, self).setUp()
     ScmBuildFile.set_rev('HEAD')
     ScmBuildFile.set_scm(Git(worktree=self.root_dir))
Exemple #40
0
 def test_detect_worktree_invalid_executable_git(self):
   with self.executable_git() as git:
     self.assertIsNone(Git.detect_worktree())
     self.assertIsNone(Git.detect_worktree(binary=git))
Exemple #41
0
 def test_detect_worktree_no_git(self):
   with self.empty_path():
     self.assertIsNone(Git.detect_worktree())
Exemple #42
0
 def test_detect_worktree_no_git(self):
     with self.empty_path():
         self.assertIsNone(Git.detect_worktree())
Exemple #43
0
  def generate_project(self, project):
    def create_content_root(source_set):
      root_relative_path = os.path.join(source_set.source_base, source_set.path) \
                           if source_set.path else source_set.source_base

      if self.get_options().infer_test_from_siblings:
        is_test = IdeaGen._sibling_is_test(source_set)
      else:
        is_test = source_set.is_test

      if source_set.resources_only:
        if source_set.is_test:
          content_type = 'java-test-resource'
        else:
          content_type = 'java-resource'
      else:
        content_type = ''

      sources = TemplateData(
        path=root_relative_path,
        package_prefix=source_set.path.replace('/', '.') if source_set.path else None,
        is_test=is_test,
        content_type=content_type
      )

      return TemplateData(
        path=root_relative_path,
        sources=[sources],
        exclude_paths=[os.path.join(source_set.source_base, x) for x in source_set.excludes],
      )

    content_roots = [create_content_root(source_set) for source_set in project.sources]
    if project.has_python:
      content_roots.extend(create_content_root(source_set) for source_set in project.py_sources)

    scala = None
    if project.has_scala:
      scala = TemplateData(
        language_level=self.scala_language_level,
        maximum_heap_size=self.scala_maximum_heap_size,
        fsc=self.fsc,
        compiler_classpath=project.scala_compiler_classpath
      )

    exclude_folders = []
    if self.get_options().exclude_maven_target:
      exclude_folders += IdeaGen._maven_targets_excludes(get_buildroot())

    exclude_folders += self.get_options().exclude_folders

    java_language_level = None
    for target in project.targets:
      if isinstance(target, JvmTarget):
        if java_language_level is None or java_language_level < target.platform.source_level:
          java_language_level = target.platform.source_level
    if java_language_level is not None:
      java_language_level = 'JDK_{0}_{1}'.format(*java_language_level.components[:2])

    configured_module = TemplateData(
      root_dir=get_buildroot(),
      path=self.module_filename,
      content_roots=content_roots,
      bash=self.bash,
      python=project.has_python,
      scala=scala,
      internal_jars=[cp_entry.jar for cp_entry in project.internal_jars],
      internal_source_jars=[cp_entry.source_jar for cp_entry in project.internal_jars
                            if cp_entry.source_jar],
      external_jars=[cp_entry.jar for cp_entry in project.external_jars],
      external_javadoc_jars=[cp_entry.javadoc_jar for cp_entry in project.external_jars
                             if cp_entry.javadoc_jar],
      external_source_jars=[cp_entry.source_jar for cp_entry in project.external_jars
                            if cp_entry.source_jar],
      annotation_processing=self.annotation_processing_template,
      extra_components=[],
      exclude_folders=exclude_folders,
      java_language_level=java_language_level,
    )

    outdir = os.path.abspath(self.intellij_output_dir)
    if not os.path.exists(outdir):
      os.makedirs(outdir)

    configured_project = TemplateData(
      root_dir=get_buildroot(),
      outdir=outdir,
      git_root=Git.detect_worktree(),
      modules=[configured_module],
      java=TemplateData(
        encoding=self.java_encoding,
        maximum_heap_size=self.java_maximum_heap_size,
        jdk=self.java_jdk,
        language_level='JDK_1_{}'.format(self.java_language_level)
      ),
      resource_extensions=list(project.resource_extensions),
      scala=scala,
      checkstyle_classpath=';'.join(project.checkstyle_classpath),
      debug_port=project.debug_port,
      annotation_processing=self.annotation_processing_template,
      extra_components=[],
    )

    existing_project_components = None
    existing_module_components = None
    if not self.nomerge:
      # Grab the existing components, which may include customized ones.
      existing_project_components = self._parse_xml_component_elements(self.project_filename)
      existing_module_components = self._parse_xml_component_elements(self.module_filename)

    # Generate (without merging in any extra components).
    safe_mkdir(os.path.abspath(self.intellij_output_dir))

    ipr = self._generate_to_tempfile(
        Generator(pkgutil.get_data(__name__, self.project_template), project=configured_project))
    iml = self._generate_to_tempfile(
        Generator(pkgutil.get_data(__name__, self.module_template), module=configured_module))

    if not self.nomerge:
      # Get the names of the components we generated, and then delete the
      # generated files.  Clunky, but performance is not an issue, and this
      # is an easy way to get those component names from the templates.
      extra_project_components = self._get_components_to_merge(existing_project_components, ipr)
      extra_module_components = self._get_components_to_merge(existing_module_components, iml)
      os.remove(ipr)
      os.remove(iml)

      # Generate again, with the extra components.
      ipr = self._generate_to_tempfile(Generator(pkgutil.get_data(__name__, self.project_template),
          project=configured_project.extend(extra_components=extra_project_components)))
      iml = self._generate_to_tempfile(Generator(pkgutil.get_data(__name__, self.module_template),
          module=configured_module.extend(extra_components=extra_module_components)))

    self.context.log.info('Generated IntelliJ project in {directory}'
                           .format(directory=self.gen_project_workdir))

    shutil.move(ipr, self.project_filename)
    shutil.move(iml, self.module_filename)
    return self.project_filename if self.open else None
Exemple #44
0
  def test_integration(self):
    self.assertEqual(set(), self.git.changed_files())
    self.assertEqual({'README'}, self.git.changed_files(from_commit='HEAD^'))

    tip_sha = self.git.commit_id
    self.assertTrue(tip_sha)

    self.assertTrue(tip_sha in self.git.changelog())

    merge_base = self.git.merge_base()
    self.assertTrue(merge_base)

    self.assertTrue(merge_base in self.git.changelog())

    with self.assertRaises(Scm.LocalException):
      self.git.server_url

    with environment_as(GIT_DIR=self.gitdir, GIT_WORK_TREE=self.worktree):
      with self.mkremote('origin') as origin_uri:
        # We shouldn't be fooled by remotes with origin in their name.
        with self.mkremote('temp_origin'):
          origin_url = self.git.server_url
          self.assertEqual(origin_url, origin_uri)

    self.assertTrue(self.git.tag_name.startswith('first-'), msg='un-annotated tags should be found')
    self.assertEqual('master', self.git.branch_name)

    def edit_readme():
      with open(self.readme_file, 'a') as fp:
        fp.write('More data.')

    edit_readme()
    with open(os.path.join(self.worktree, 'INSTALL'), 'w') as untracked:
      untracked.write('make install')
    self.assertEqual({'README'}, self.git.changed_files())
    self.assertEqual({'README', 'INSTALL'}, self.git.changed_files(include_untracked=True))

    # Confirm that files outside of a given relative_to path are ignored
    self.assertEqual(set(), self.git.changed_files(relative_to='non-existent'))

    self.git.commit('API Changes.')
    try:
      # These changes should be rejected because our branch point from origin is 1 commit behind
      # the changes pushed there in clone 2.
      self.git.push()
    except Scm.RemoteException:
      with environment_as(GIT_DIR=self.gitdir, GIT_WORK_TREE=self.worktree):
        subprocess.check_call(['git', 'reset', '--hard', 'depot/master'])
      self.git.refresh()
      edit_readme()

    self.git.commit('''API '"' " Changes.''')
    self.git.push()
    # HEAD is merged into master
    self.assertEqual(self.git.commit_date(self.git.merge_base()), self.git.commit_date('HEAD'))
    self.assertEqual(self.git.commit_date('HEAD'), self.git.commit_date('HEAD'))
    self.git.tag('second', message='''Tagged ' " Changes''')

    with temporary_dir() as clone:
      with pushd(clone):
        self.init_repo('origin', self.origin)
        subprocess.check_call(['git', 'pull', '--tags', 'origin', 'master:master'])

        with open(os.path.realpath('README')) as readme:
          self.assertEqual('--More data.', readme.read())

        git = Git()

        # Check that we can pick up committed and uncommitted changes.
        with safe_open(os.path.realpath('CHANGES'), 'w') as changes:
          changes.write('none')
        subprocess.check_call(['git', 'add', 'CHANGES'])
        self.assertEqual({'README', 'CHANGES'}, git.changed_files(from_commit='first'))

        self.assertEqual('master', git.branch_name)
        self.assertEqual('second', git.tag_name, msg='annotated tags should be found')
 def mk_project_tree(self, build_root, ignore_patterns=None):
     return ScmProjectTree(build_root, Git(worktree=build_root), 'HEAD',
                           ignore_patterns)
Exemple #46
0
  def generate_project(self, project):
    def create_content_root(source_set):
      root_relative_path = os.path.join(source_set.source_base, source_set.path) \
                           if source_set.path else source_set.source_base

      if self.get_options().infer_test_from_siblings:
        is_test = IdeaGen._sibling_is_test(source_set)
      else:
        is_test = source_set.is_test

      sources = TemplateData(
        path=root_relative_path,
        package_prefix=source_set.path.replace('/', '.') if source_set.path else None,
        is_test=is_test
      )

      return TemplateData(
        path=root_relative_path,
        sources=[sources],
        exclude_paths=[os.path.join(source_set.source_base, x) for x in source_set.excludes],
      )

    content_roots = [create_content_root(source_set) for source_set in project.sources]
    if project.has_python:
      content_roots.extend(create_content_root(source_set) for source_set in project.py_sources)

    scala = None
    if project.has_scala:
      scala = TemplateData(
        language_level=self.scala_language_level,
        maximum_heap_size=self.scala_maximum_heap_size,
        fsc=self.fsc,
        compiler_classpath=project.scala_compiler_classpath
      )

    exclude_folders = []
    if self.get_options().exclude_maven_target:
      exclude_folders += IdeaGen._maven_targets_excludes(get_buildroot())

    exclude_folders += self.get_options().exclude_folders

    configured_module = TemplateData(
      root_dir=get_buildroot(),
      path=self.module_filename,
      content_roots=content_roots,
      bash=self.bash,
      python=project.has_python,
      scala=scala,
      internal_jars=[cp_entry.jar for cp_entry in project.internal_jars],
      internal_source_jars=[cp_entry.source_jar for cp_entry in project.internal_jars
                            if cp_entry.source_jar],
      external_jars=[cp_entry.jar for cp_entry in project.external_jars],
      external_javadoc_jars=[cp_entry.javadoc_jar for cp_entry in project.external_jars
                             if cp_entry.javadoc_jar],
      external_source_jars=[cp_entry.source_jar for cp_entry in project.external_jars
                            if cp_entry.source_jar],
      extra_components=[],
      exclude_folders=exclude_folders,
    )

    outdir = os.path.abspath(self.intellij_output_dir)
    if not os.path.exists(outdir):
      os.makedirs(outdir)

    configured_project = TemplateData(
      root_dir=get_buildroot(),
      outdir=outdir,
      git_root=Git.detect_worktree(),
      modules=[ configured_module ],
      java=TemplateData(
        encoding=self.java_encoding,
        maximum_heap_size=self.java_maximum_heap_size,
        jdk=self.java_jdk,
        language_level = 'JDK_1_%d' % self.java_language_level
      ),
      resource_extensions=list(project.resource_extensions),
      scala=scala,
      checkstyle_classpath=';'.join(project.checkstyle_classpath),
      debug_port=project.debug_port,
      extra_components=[],
    )

    existing_project_components = None
    existing_module_components = None
    if not self.nomerge:
      # Grab the existing components, which may include customized ones.
      existing_project_components = self._parse_xml_component_elements(self.project_filename)
      existing_module_components = self._parse_xml_component_elements(self.module_filename)

    # Generate (without merging in any extra components).
    safe_mkdir(os.path.abspath(self.intellij_output_dir))

    ipr = self._generate_to_tempfile(
        Generator(pkgutil.get_data(__name__, self.project_template), project = configured_project))
    iml = self._generate_to_tempfile(
        Generator(pkgutil.get_data(__name__, self.module_template), module = configured_module))

    if not self.nomerge:
      # Get the names of the components we generated, and then delete the
      # generated files.  Clunky, but performance is not an issue, and this
      # is an easy way to get those component names from the templates.
      extra_project_components = self._get_components_to_merge(existing_project_components, ipr)
      extra_module_components =  self._get_components_to_merge(existing_module_components, iml)
      os.remove(ipr)
      os.remove(iml)

      # Generate again, with the extra components.
      ipr = self._generate_to_tempfile(Generator(pkgutil.get_data(__name__, self.project_template),
          project = configured_project.extend(extra_components = extra_project_components)))
      iml = self._generate_to_tempfile(Generator(pkgutil.get_data(__name__, self.module_template),
          module = configured_module.extend(extra_components = extra_module_components)))

    self.context.log.info('Generated IntelliJ project in {directory}'
                           .format(directory=self.gen_project_workdir))

    shutil.move(ipr, self.project_filename)
    shutil.move(iml, self.module_filename)
    return self.project_filename if self.open else None
Exemple #47
0
class GitTest(unittest.TestCase):
  @staticmethod
  def init_repo(remote_name, remote):
    subprocess.check_call(['git', 'init'])
    subprocess.check_call(['git', 'config', 'user.email', '*****@*****.**'])
    subprocess.check_call(['git', 'config', 'user.name', 'Your Name'])
    subprocess.check_call(['git', 'remote', 'add', remote_name, remote])

  @classmethod
  def setUp(self):
    self.origin = safe_mkdtemp()
    with pushd(self.origin):
      subprocess.check_call(['git', 'init', '--bare'])

    self.gitdir = safe_mkdtemp()
    self.worktree = safe_mkdtemp()

    self.readme_file = os.path.join(self.worktree, 'README')

    with environment_as(GIT_DIR=self.gitdir, GIT_WORK_TREE=self.worktree):
      self.init_repo('depot', self.origin)

      touch(self.readme_file)
      subprocess.check_call(['git', 'add', 'README'])
      subprocess.check_call(['git', 'commit', '-am', 'initial commit with decode -> \x81b'])
      subprocess.check_call(['git', 'tag', 'first'])
      subprocess.check_call(['git', 'push', '--tags', 'depot', 'master'])
      subprocess.check_call(['git', 'branch', '--set-upstream', 'master', 'depot/master'])

      with safe_open(self.readme_file, 'w') as readme:
        readme.write('Hello World.')
      subprocess.check_call(['git', 'commit', '-am', 'Update README.'])

    self.clone2 = safe_mkdtemp()
    with pushd(self.clone2):
      self.init_repo('origin', self.origin)
      subprocess.check_call(['git', 'pull', '--tags', 'origin', 'master:master'])

      with safe_open(os.path.realpath('README'), 'a') as readme:
        readme.write('--')
      subprocess.check_call(['git', 'commit', '-am', 'Update README 2.'])
      subprocess.check_call(['git', 'push', '--tags', 'origin', 'master'])

    self.git = Git(gitdir=self.gitdir, worktree=self.worktree)

  @staticmethod
  @contextmanager
  def mkremote(remote_name):
    with temporary_dir() as remote_uri:
      subprocess.check_call(['git', 'remote', 'add', remote_name, remote_uri])
      try:
        yield remote_uri
      finally:
        subprocess.check_call(['git', 'remote', 'remove', remote_name])

  @classmethod
  def tearDown(self):
    safe_rmtree(self.origin)
    safe_rmtree(self.gitdir)
    safe_rmtree(self.worktree)
    safe_rmtree(self.clone2)

  def test_integration(self):
    self.assertEqual(set(), self.git.changed_files())
    self.assertEqual(set(['README']), self.git.changed_files(from_commit='HEAD^'))

    tip_sha = self.git.commit_id
    self.assertTrue(tip_sha)

    self.assertTrue(tip_sha in self.git.changelog())

    merge_base = self.git.merge_base()
    self.assertTrue(merge_base)

    self.assertTrue(merge_base in self.git.changelog())

    with pytest.raises(Scm.LocalException):
      self.git.server_url

    with environment_as(GIT_DIR=self.gitdir, GIT_WORK_TREE=self.worktree):
      with self.mkremote('origin') as origin_uri:
        origin_url = self.git.server_url
        self.assertEqual(origin_url, origin_uri)

    self.assertTrue(self.git.tag_name.startswith('first-'), msg='un-annotated tags should be found')
    self.assertEqual('master', self.git.branch_name)

    def edit_readme():
      with open(self.readme_file, 'a') as readme:
        readme.write('More data.')

    edit_readme()
    with open(os.path.join(self.worktree, 'INSTALL'), 'w') as untracked:
      untracked.write('make install')
    self.assertEqual(set(['README']), self.git.changed_files())
    self.assertEqual(set(['README', 'INSTALL']), self.git.changed_files(include_untracked=True))

    # confirm that files outside of a given relative_to path are ignored
    self.assertEqual(set(), self.git.changed_files(relative_to='non-existent'))

    try:
      # These changes should be rejected because our branch point from origin is 1 commit behind
      # the changes pushed there in clone 2.
      self.git.commit('API Changes.')
    except Scm.RemoteException:
      with environment_as(GIT_DIR=self.gitdir, GIT_WORK_TREE=self.worktree):
        subprocess.check_call(['git', 'reset', '--hard', 'depot/master'])
      self.git.refresh()
      edit_readme()

    self.git.commit('''API '"' " Changes.''')
    # HEAD is merged into master
    self.assertEqual(self.git.commit_date(self.git.merge_base()), self.git.commit_date('HEAD'))
    self.assertEqual(self.git.commit_date('HEAD'), self.git.commit_date('HEAD'))
    self.git.tag('second', message='''Tagged ' " Changes''')

    with temporary_dir() as clone:
      with pushd(clone):
        self.init_repo('origin', self.origin)
        subprocess.check_call(['git', 'pull', '--tags', 'origin', 'master:master'])

        with open(os.path.realpath('README')) as readme:
          self.assertEqual('--More data.', readme.read())

        git = Git()

        # Check that we can pick up committed and uncommitted changes.
        with safe_open(os.path.realpath('CHANGES'), 'w') as changes:
          changes.write('none')
        subprocess.check_call(['git', 'add', 'CHANGES'])
        self.assertEqual(set(['README', 'CHANGES']), git.changed_files(from_commit='first'))

        self.assertEqual('master', git.branch_name)
        self.assertEqual('second', git.tag_name, msg='annotated tags should be found')

  def test_detect_worktree(self):
    with temporary_dir() as _clone:
      with pushd(_clone):
        clone = os.path.realpath(_clone)

        self.init_repo('origin', self.origin)
        subprocess.check_call(['git', 'pull', '--tags', 'origin', 'master:master'])

        def worktree_relative_to(cwd, expected):
          """Given a cwd relative to the worktree, tests that the worktree is detected as 'expected'."""
          orig_cwd = os.getcwd()
          try:
            abs_cwd = os.path.join(clone, cwd)
            if not os.path.isdir(abs_cwd):
              os.mkdir(abs_cwd)
            os.chdir(abs_cwd)
            actual = Git.detect_worktree()
            self.assertEqual(expected, actual)
          finally:
            os.chdir(orig_cwd)

        worktree_relative_to('..', None)
        worktree_relative_to('.', clone)
        worktree_relative_to('is', clone)
        worktree_relative_to('is/a', clone)
        worktree_relative_to('is/a/dir', clone)