def test_create_initial_upstream_branch(self): os.makedirs(self.git_repo) # Setup the repo execute_command('git init .', cwd=self.git_repo) # Execute from bloom.import_upstream import create_initial_upstream_branch cmd = 'git branch --no-color' out = check_output(cmd, shell=True, cwd=self.git_repo) assert out.count('upstream') == 0 create_initial_upstream_branch(self.git_repo) out = check_output(cmd, shell=True, cwd=self.git_repo) assert out.count('upstream') == 1 # Clean up from shutil import rmtree rmtree(self.git_repo)
def test_convert_catkin_to_bloom(self): os.makedirs(self.git_repo) # Setup the repo execute_command('git init .', cwd=self.git_repo) f = open(os.path.join(self.git_repo, 'catkin.conf'), 'w+') f.write('[catkin]\n\tupstream = git://github.com/ros/langs.git' '\n\tupstreamtype = git\n') f.close() execute_command('git add ./*', cwd=self.git_repo) execute_command('git commit -m "Init"', cwd=self.git_repo) execute_command('git branch catkin', cwd=self.git_repo) # Execute the converter from bloom.import_upstream import convert_catkin_to_bloom convert_catkin_to_bloom(self.git_repo) # Assert correct behavior output = check_output('git branch --no-color', shell=True, cwd=self.git_repo) assert output.count('catkin') == 0 assert output.count('* bloom') == 1, output expected_str = '[bloom]\n\tupstream = git://github.com/ros/langs.git' \ '\n\tupstreamtype = git\n' conf_path = os.path.join(self.git_repo, 'bloom.conf') assert os.path.exists(conf_path) assert open(conf_path, 'r').read() == expected_str from shutil import rmtree rmtree(self.git_repo)
def parse_bloom_conf(cwd=None): """ Parses the bloom.conf file in the current directory and returns info in it. """ bloom_conf = show('bloom', 'bloom.conf', directory=cwd) with open('.bloom.conf', 'w+') as f: f.write(bloom_conf) cmd = 'git config -f .bloom.conf bloom.upstream' upstream_repo = check_output(cmd, shell=True, cwd=cwd).strip() cmd = 'git config -f .bloom.conf bloom.upstreamtype' upstream_type = check_output(cmd, shell=True, cwd=cwd).strip() try: cmd = 'git config -f .bloom.conf bloom.upstreambranch' upstream_branch = check_output(cmd, shell=True, cwd=cwd).strip() except CalledProcessError: upstream_branch = '' os.remove('.bloom.conf') return upstream_repo, upstream_type, upstream_branch
def get_tags(directory=None): """ Returns a list of tags in the git repository. :param directory: directory in which to preform this action :returns: list of tags :raises: subprocess.CalledProcessError if any git calls fail """ out = check_output('git tag -l', shell=True, cwd=directory) return [l.strip() for l in out.splitlines()]
def convert_old_bloom_conf(prefix=None): prefix = prefix if prefix is not None else 'convert' tracks_dict = get_tracks_dict_raw() track = prefix track_count = 0 while track in tracks_dict['tracks']: track_count += 1 track = prefix + str(track_count) track_dict = copy.copy(DEFAULT_TEMPLATE) cmd = 'git config -f bloom.conf bloom.upstream' upstream_repo = check_output(cmd, shell=True).strip() cmd = 'git config -f bloom.conf bloom.upstreamtype' upstream_type = check_output(cmd, shell=True).strip() try: cmd = 'git config -f bloom.conf bloom.upstreambranch' upstream_branch = check_output(cmd, shell=True).strip() except subprocess.CalledProcessError: upstream_branch = '' for key in template_entry_order: if key == 'vcs_uri': track_dict[key] = upstream_repo continue if key == 'vcs_type': track_dict[key] = upstream_type continue if key == 'vcs_uri': track_dict[key] = upstream_branch or None continue track_dict[key] = track_dict[key].default debug('Converted bloom.conf:') with open('bloom.conf', 'r') as f: debug(f.read()) debug('To this track:') debug(str({track: track_dict})) tracks_dict['tracks'][track] = track_dict write_tracks_dict_raw(tracks_dict) execute_command('git rm bloom.conf', shell=True) execute_command('git commit -m "Removed bloom.conf"', shell=True) # Now move the old bloom branch into master upconvert_bloom_to_config_branch()
def has_untracked_files(directory=None): """ Returns True is the working branch has untracked files, False otherwise. :param directory: directory in which to preform this action :returns: True if there are untracked files (or dirs), otherwise False :raises: subprocess.CalledProcessError if any git calls fail """ out = check_output('git status', shell=True, cwd=directory) if '# Untracked files:' in out: return True return False
def get_root(directory=None): """ Returns the git root directory above the given dir. If the given dir is not in a git repository, None is returned. :param directory: directory to query from, if None the cwd is used :returns: root of git repository or None if not a git repository """ cmd = 'git rev-parse --show-toplevel' try: output = check_output(cmd, shell=True, cwd=directory, stderr=PIPE) except CalledProcessError: return None return output.strip()
def get_last_tag_by_date(directory=None): """ Returns the most recent, by date, tag in the given local git repository. :param directory: the directory in which to run the query :returns: the most recent tag by date, else '' if there are no tags :raises: subprocess.CalledProcessError if git command fails """ cmd = "git for-each-ref --sort='*authordate' " "--format='%(refname:short)' refs/tags/upstream" output = check_output(cmd, shell=True, cwd=directory, stderr=PIPE) output = output.splitlines() if len(output) == 0: return "" return output[-1]
def has_changes(directory=None): """ Returns True if the working branch has local changes, False otherwise. :param directory: directory in which to preform this action :returns: True if there are local changes, otherwise False :raises: subprocess.CalledProcessError if any git calls fail """ out = check_output('git status', shell=True, cwd=directory) if 'nothing to commit (working directory clean)' in out: return False if 'nothing added to commit' in out: return False return True
def get_remotes(directory=None): """ Returns a list of remote names. :param directory: directory to list remotes from :returns: a list of git remotes :raises: RuntimeError if directory is not a git repository """ root = get_root(directory) checked_dir = directory or os.getcwd() if root is None: raise RuntimeError("Directory '{0}' is not in a git repository.".format(checked_dir)) cmd = "git remote -v" output = check_output(cmd, shell=True, cwd=root, stderr=PIPE) return list(set([x.split()[0].strip() for x in output.splitlines() if x.strip()]))
def get_last_tag_by_date(directory=None): """ Returns the most recent, by date, tag in the given local git repository. :param directory: the directory in which to run the query :returns: the most recent tag by date, else '' if there are no tags :raises: subprocess.CalledProcessError if git command fails """ cmd = "git for-each-ref --sort='*authordate' " \ "--format='%(refname:short)' refs/tags/upstream" output = check_output(cmd, shell=True, cwd=directory, stderr=PIPE) output = output.splitlines() if len(output) == 0: return '' return output[-1]
def get_last_tag_by_version(directory=None): """ Returns the most recent, by date, tag in the given local git repository. :param directory: the directory in which to run the query :returns: the most recent tag by date, else '' if there are no tags :raises: subprocess.CalledProcessError if git command fails """ cmd = "git for-each-ref --sort='*authordate' " "--format='%(refname:short)' refs/tags/upstream" output = check_output(cmd, shell=True, cwd=directory, stderr=PIPE) tags = [] versions = [] for line in output.splitlines(): tags.append(line.strip()) versions.append(parse_version(line.strip())) return tags[versions.index(max(versions))] if versions else ""
def has_changes(directory=None): """ Returns True if the working branch has local changes, False otherwise. :param directory: directory in which to preform this action :returns: True if there are local changes, otherwise False :raises: subprocess.CalledProcessError if any git calls fail """ out = check_output('git status', shell=True, cwd=directory) if 'nothing to commit (working directory clean)' in out: return False if 'nothing to commit, working directory clean' in out: return False if 'nothing added to commit' in out: return False return True
def get_commit_hash(reference, directory=None): """ Returns the SHA-1 commit hash for the given reference. :param reference: any git reference (branch or tag) to resolve to SHA-1 :param directory: directory in which to preform this action :returns: SHA-1 commit hash for the given reference :raises: subprocess.CalledProcessError if any git calls fail """ # Track remote branch if branch_exists(reference, local_only=False, directory=directory): if not branch_exists(reference, local_only=True, directory=directory): track_branches(reference, directory) cmd = 'git show-branch --sha1-name ' + reference out = check_output(cmd, shell=True, cwd=directory) return out.split('[')[1].split(']')[0]
def has_submodules(directory=None): """ Returns True if the git repository at this directory has submodules :param directory: directory to check for submodules in :returns: True if there are submodules, False otherwise :raises: RuntimeError if directory is not a git repository """ root = get_root(directory) checked_dir = directory or os.getcwd() if root is None: raise RuntimeError("Directory '{0}' is not in a git repository.".format(checked_dir)) cmd = "git submodule status" output = check_output(cmd, shell=True, cwd=root, stderr=PIPE) if not output.strip(): return False return True
def get_last_tag_by_version(directory=None): """ Returns the most recent, by date, tag in the given local git repository. :param directory: the directory in which to run the query :returns: the most recent tag by date, else '' if there are no tags :raises: subprocess.CalledProcessError if git command fails """ cmd = "git for-each-ref --sort='*authordate' " \ "--format='%(refname:short)' refs/tags/upstream" output = check_output(cmd, shell=True, cwd=directory, stderr=PIPE) tags = [] versions = [] for line in output.splitlines(): tags.append(line.strip()) versions.append(parse_version(line.strip())) return tags[versions.index(max(versions))] if versions else ''
def get_current_branch(directory=None): """ Returns the current git branch by parsing the output of `git branch` This will raise a RuntimeError if the current working directory is not a git repository. If no branch could be determined it will return None, i.e. (no branch) will return None. :param directory: directory in which to run the command :returns: current git branch or None if none can be determined, (no branch) :raises: subprocess.CalledProcessError if git command fails """ cmd = 'git branch --no-color' output = check_output(cmd, shell=True, cwd=directory) output = output.splitlines() for token in output: if token.strip().startswith('*'): token = token[2:] if token == '(no branch)': return None return token return None
def get_branches(local_only=False, directory=None): """ Returns a list of branches in the git repository. :param local_only: if True, do not return remote branches, False by default :param directory: directory in which to preform this action :returns: list of branches :raises: subprocess.CalledProcessError if any git calls fail """ cmd = 'git branch --no-color' if not local_only: cmd += ' -a' out = check_output(cmd, shell=True, cwd=directory) branches = [] for line in out.splitlines(): if line.count('HEAD -> ') > 0: continue if line.count('(no branch)') > 0: continue line = line.strip('*').strip() branches.append(line) return branches
def perform_release(repository, track, distro, new_track, interactive): release_repo = get_release_repo(repository, distro) with change_directory(release_repo.get_path()): # Check for push permissions try: info(fmt("@{gf}@!==> @|Testing for push permission on release repository")) check_output('git push', shell=True) except subprocess.CalledProcessError: error("Cannot push to remote release repository.", exit=True) # Check to see if the old bloom.conf exists if check_for_bloom_conf(repository): # Convert to a track info("Old bloom.conf file detected.") info(fmt("@{gf}@!==> @|Converting to bloom.conf to track")) convert_old_bloom_conf(None if new_track else distro) # Check that the track is valid tracks_dict = get_tracks_dict_raw() # If new_track, create the new track first if new_track: if not track: error("You must specify a track when creating a new one.", exit=True) overrides = {'ros_distro': distro} if track in tracks_dict['tracks']: warning("Track '{0}' exists, editing instead...".format(track)) edit_track_cmd(track) else: # Create a new track called <track>, # copying an existing track if possible, # and overriding the ros_distro new_track_cmd(track, copy_track='', overrides=overrides) tracks_dict = get_tracks_dict_raw() if track and track not in tracks_dict['tracks']: error("Given track '{0}' does not exist in release repository." .format(track)) error("Available tracks: " + str(tracks_dict['tracks'].keys()), exit=True) elif not track: tracks = tracks_dict['tracks'].keys() # Error out if there are no tracks if len(tracks) == 0: error("Release repository has no tracks.") info("Manually clone the repository:") info(" git clone {0}".format(release_repo.get_url())) info("And then create a new track:") info(" git-bloom-config new <track name>") error("Run again after creating a track.", exit=True) # Error out if there is more than one track if len(tracks) != 1: error("No track specified and there is not just one track.") error("Please specify one of the available tracks: " + str(tracks), exit=True) # Get the only track track = tracks[0] # Ensure the track is complete track_dict = tracks_dict['tracks'][track] update_track(track_dict) tracks_dict['tracks'][track] = track_dict write_tracks_dict_raw(tracks_dict) # Run the release info(fmt("@{gf}@!==> @|") + "Releasing '{0}' using release track '{1}'" .format(repository, track)) cmd = 'git-bloom-release ' + str(track) info(fmt("@{bf}@!==> @|@!" + str(cmd))) try: subprocess.check_call(cmd, shell=True) except subprocess.CalledProcessError: error("Release failed, exiting.", exit=True) info(fmt(_success) + "Released '{0}' using release track '{1}' successfully" .format(repository, track)) # Check for pushing if interactive: info("Releasing complete, push?") if not maybe_continue(): error("User answered no to continue prompt, aborting.", exit=True) # Push changes to the repository info(fmt("@{gf}@!==> @|") + "Pushing changes to release repository for '{0}'" .format(repository)) cmd = 'git push --all' info(fmt("@{bf}@!==> @|@!" + str(cmd))) try: subprocess.check_call(cmd, shell=True) except subprocess.CalledProcessError: error("Pushing changes failed, would you like to add '--force' to 'git push --all'?") if not maybe_continue(): error("Pushing changes failed, exiting.", exit=True) cmd += ' --force' info(fmt("@{bf}@!==> @|@!" + str(cmd))) try: subprocess.check_call(cmd, shell=True) except subprocess.CalledProcessError: error("Pushing changes failed, exiting.", exit=True) info(fmt(_success) + "Pushed changes successfully") # Push tags to the repository info(fmt("@{gf}@!==> @|") + "Pushing tags to release repository for '{0}'" .format(repository)) cmd = 'git push --tags' info(fmt("@{bf}@!==> @|@!" + str(cmd))) try: subprocess.check_call(cmd, shell=True) except subprocess.CalledProcessError: error("Pushing changes failed, would you like to add '--force' to 'git push --tags'?") if not maybe_continue(): error("Pushing tags failed, exiting.", exit=True) cmd += ' --force' info(fmt("@{bf}@!==> @|@!" + str(cmd))) try: subprocess.check_call(cmd, shell=True) except subprocess.CalledProcessError: error("Pushing tags failed, exiting.", exit=True) info(fmt(_success) + "Pushed tags successfully") # Propose github pull request info(fmt("@{gf}@!==> @|") + "Generating pull request to distro file located at '{0}'" .format(ROS_DISTRO_FILE).format(distro)) generate_ros_distro_diff(track, repository, distro) info("In the future this will create a pull request for you, done for now...") info(fmt(_success) + "Pull request opened at: '{0}'".format('Not yet Implemented'))
def _my_run(cmd): info(fmt("@{bf}@!==> @|@!" + str(cmd))) out = check_output(cmd, stderr=subprocess.STDOUT, shell=True) if out: info(out, use_prefix=False)