def post_rebase(self, destination): name = destination.split('/')[-1] # Retrieve the package package = self.packages[name] # Handle differently if this is an arch vs distro branch if destination in self.arch_branches: info("Placing Arch template files into '{0}' branch." .format(destination)) # Then this is an arch branch # Place the raw template files self.place_template_files() else: # This is a distro specific arch branch # Determine the current package being generated distro = destination.split('/')[-2] # Create Arch packages for each distro with inbranch(destination): # To fit Arch Linux philosophy a bit better, we move all the source files into a subdirectory. # Arch Linux doesn't support source distribution through a subdirectory; therefore we should ideally compress the sources or provide a URL. # At this point in the generator, it is tricky to get the release URL. Furthermore it wouldn't fit bloom's patch mechanism very well. # To work around, we copy the sources to the $srcdir at the beginning inside the prepare() function. temp_dir = mkdtemp(dir='.') for item in os.listdir("."): itemsrc = os.path.abspath(item) if os.path.basename(itemsrc) in ['.', '..', '.git', '.svn', 'arch',os.path.basename(temp_dir)]: continue itemdst = os.path.abspath(os.path.join(temp_dir,item)) execute_command('git mv ' + itemsrc + ' ' + itemdst) execute_command('git mv ' + temp_dir + ' ' + name) execute_command('git commit --amend --no-edit') # Then generate the PKGBUILD data = self.generate_arch(package, distro) # And finally move the PKGBUILD to the root directory of the package. for item in os.listdir("arch"): itemsrc = os.path.abspath(os.path.join("arch",item)) if os.path.basename(itemsrc) in ['.', '..', '.git', '.svn']: continue itemdst = os.path.abspath(item) execute_command('git mv ' + itemsrc + ' ' + itemdst) execute_command('git commit --amend --no-edit') # Create the tag name for later self.tag_names[destination] = self.generate_tag_name(data) # Update the patch configs patches_branch = 'patches/' + destination config = get_patch_config(patches_branch) # Store it self.store_original_config(config, patches_branch) # Modify the base so import/export patch works current_branch = get_current_branch() if current_branch is None: error("Could not determine current branch.", exit=True) config['base'] = get_commit_hash(current_branch) # Set it set_patch_config(patches_branch, config)
def create_from_template(self, template_name, data, directory, chmod=None, outfile=None): # Configure template name extention = '.em' if not template_name.endswith(extention): template_file = template_name + extention else: template_file = template_name template_name = template_name[:len(extention)] # Open the template with change_directory(directory): with open(template_file, 'r') as f: template = f.read() execute_command('git rm ' + template_file) # Expand template outfile = outfile if outfile is not None else template_name info("Expanding template: '" + template_file + "' to '" + outfile + "'") result = em.expand(template, **data) # Write the template out with change_directory(directory): with open(outfile, 'w+') as f: f.write(result) # Set permissions if needed if chmod is not None: os.chmod(outfile, chmod)
def post_patch(self, destination, color='bluef'): if destination in self.rpm_branches: return # Tag after patches have been applied with inbranch(destination): # Tag tag_name = self.tag_names[destination] if tag_exists(tag_name): if self.interactive: warning("Tag exists: " + tag_name) warning("Do you wish to overwrite it?") if not maybe_continue('y'): error("Answered no to continue, aborting.", exit=True) else: warning("Overwriting tag: " + tag_name) else: info("Creating tag: " + tag_name) execute_command('git tag -f ' + tag_name) # Report of success name = destination.split('/')[-1] package = self.packages[name] distro = destination.split('/')[-2] info(ansi(color) + "####" + ansi('reset'), use_prefix=False) info( ansi(color) + "#### " + ansi('greenf') + "Successfully" + ansi(color) + " generated '" + ansi('boldon') + distro + ansi('boldoff') + "' RPM for package" " '" + ansi('boldon') + package.name + ansi('boldoff') + "'" + " at version '" + ansi('boldon') + package.version + "-" + str(self.rpm_inc) + ansi('boldoff') + "'" + ansi('reset'), use_prefix=False ) info(ansi(color) + "####\n" + ansi('reset'), use_prefix=False)
def store_original_config(self, config, patches_branch): with inbranch(patches_branch): with open('rpm.store', 'w+') as f: f.write(json.dumps(config)) execute_command('git add rpm.store') if has_changes(): execute_command('git commit -m "Store original patch config"')
def create_branch(branch, orphaned=False, changeto=False, directory=None): """ Creates a new branch in the current, or given, git repository. If the specified branch already exists git will fail and a subprocess.CalledProcessError will be raised. :param branch: name of the new branch :param orphaned: if True creates an orphaned branch :param changeto: if True changes to the new branch after creation :param directory: directory in which to preform this action :raises: subprocess.CalledProcessError if any git calls fail """ current_branch = get_current_branch(directory) try: if orphaned: execute_command('git symbolic-ref HEAD refs/heads/' + branch, cwd=directory) execute_command('rm -f .git/index', cwd=directory) execute_command('git clean -fdx', cwd=directory) cmd = 'git commit --allow-empty -m "Created orphaned branch '\ '{0}"'.format(branch) execute_command(cmd, cwd=directory) if changeto: current_branch = None else: execute_command('git branch {0}'.format(branch), cwd=directory) if changeto: checkout(branch, directory=directory) current_branch = None finally: if current_branch is not None: checkout(current_branch, directory=directory)
def post_patch(self, destination, color='bluef'): # Tag after patches have been applied with inbranch(destination): # Tag tag_name = self.tag_names[destination] if tag_exists(tag_name): if self.interactive: warning("Tag exists: " + tag_name) warning("Do you wish to overwrite it?") if not maybe_continue('y'): error("Answered no to continue, aborting.") return code.ANSWERED_NO_TO_CONTINUE else: warning("Overwriting tag: " + tag_name) else: info("Creating tag: " + tag_name) execute_command('git tag -f ' + tag_name) # Report of success name = destination.split('/')[-1] stackage, kind = self.packages[name] distro = destination.split('/')[-2] info(ansi(color) + "####" + ansi('reset'), use_prefix=False) info( ansi(color) + "#### " + ansi('greenf') + "Successfully" + \ ansi(color) + " generated '" + ansi('boldon') + distro + \ ansi('boldoff') + "' debian for " + kind + \ " '" + ansi('boldon') + stackage.name + ansi('boldoff') + "'" + \ " at version '" + ansi('boldon') + stackage.version + \ "-" + str(self.debian_inc) + ansi('boldoff') + "'" + \ ansi('reset'), use_prefix=False ) info(ansi(color) + "####\n" + ansi('reset'), use_prefix=False)
def commit_summary(): global _summary_file if get_root() is None: return if _summary_file is None: return if not os.path.exists(_summary_file.name): return try: with inbranch('master'): readme_name = 'README.md' readme = '' if os.path.isfile(readme_name): with open(readme_name, 'r') as f: readme = f.read() _summary_file.close() with open(_summary_file.name, 'r') as f: readme = f.read() + "\n\n" + readme with open(readme_name, 'w') as f: f.write(readme) execute_command('git add ' + readme_name) if has_changes(): execute_command('git commit -m "Updating README.md"') finally: if _summary_file is not None: _summary_file.close() if os.path.exists(_summary_file.name): os.remove(_summary_file.name)
def set_upstream(upstream_repo, upstream_repo_type, upstream_repo_branch): # Check for a bloom branch if branch_exists('bloom', False): # Found a bloom branch debug("Found a bloom branch, checking out.") # Check out the bloom branch checkout('bloom') else: # No bloom branch found, create one create_branch('bloom', changeto=True) # Now set the upstream using the bloom config cmd = 'git config -f bloom.conf bloom.upstream "{0}"'.format(upstream_repo) execute_command(cmd) cmd = 'git config -f bloom.conf ' \ + 'bloom.upstreamtype "{0}"'.format(upstream_repo_type) execute_command(cmd) cmd = 'git config -f bloom.conf ' \ + 'bloom.upstreambranch "{0}"'.format(upstream_repo_branch) execute_command(cmd) execute_command('git add bloom.conf') if has_changes(): cmd = 'git commit -m "bloom branch update by git-bloom-config"' execute_command(cmd) else: debug("No chages, nothing to commit.")
def generate_rpm(self, package, rpm_distro, rpm_dir='rpm'): info("Generating RPM for {0} {1}...".format(self.os_name, rpm_distro)) # Try to retrieve the releaser_history releaser_history = self.get_releaser_history() # Generate substitution values subs = self.get_subs(package, rpm_distro, releaser_history) # Use subs to create and store releaser history self.set_releaser_history(dict(subs['changelogs'])) # Template files template_files = process_template_files('.', subs) # Remove any residual template files execute_command('git rm -rf ' + ' '.join("'{}'".format(t) for t in template_files)) # Add marker file to tell mock to archive the sources open('.write_tar', 'a').close() # Add marker file changes to the rpm folder execute_command('git add .write_tar ' + rpm_dir) # Commit changes execute_command('git commit -m "Generated RPM files for ' + rpm_distro + '"') # Rename the template spec file execute_command('git mv ' + rpm_dir + '/template.spec ' + rpm_dir + '/' + subs['Package'] + '.spec') # Commit changes execute_command('git commit -m "Renamed RPM spec file for ' + rpm_distro + '"') # Return the subs for other use return subs
def post_patch(self, destination, color='bluef'): if destination in self.debian_branches: return # Tag after patches have been applied with inbranch(destination): # Tag tag_name = self.tag_names[destination] if tag_exists(tag_name): if self.interactive: warning("Tag exists: " + tag_name) warning("Do you wish to overwrite it?") if not maybe_continue('y'): error("Answered no to continue, aborting.", exit=True) else: warning("Overwriting tag: " + tag_name) else: info("Creating tag: " + tag_name) execute_command('git tag -f ' + tag_name) # Report of success name = destination.split('/')[-1] package = self.packages[name] distro = destination.split('/')[-2] info(ansi(color) + "####" + ansi('reset'), use_prefix=False) info(ansi(color) + "#### " + ansi('greenf') + "Successfully" + ansi(color) + " generated '" + ansi('boldon') + distro + ansi('boldoff') + "' debian for package" " '" + ansi('boldon') + package.name + ansi('boldoff') + "'" + " at version '" + ansi('boldon') + package.version + "-" + str(self.debian_inc) + ansi('boldoff') + "'" + ansi('reset'), use_prefix=False) info(ansi(color) + "####\n" + ansi('reset'), use_prefix=False)
def store_original_config(self, config, patches_branch): with inbranch(patches_branch): with open('debian.store', 'w+') as f: f.write(json.dumps(config)) execute_command('git add debian.store') if has_changes(): execute_command('git commit -m "Store original patch config"')
def remove_patches(directory=None): # Get the current branch current_branch = get_current_branch(directory) # Ensure the current branch is valid if current_branch is None: error("Could not determine current branch, are you in a git repo?") return 1 # Construct the patches branch patches_branch = 'patches/' + current_branch try: # See if the patches branch exists if branch_exists(patches_branch, False, directory=directory): if not branch_exists(patches_branch, True, directory=directory): track_branches(patches_branch, directory) else: error("No patches branch (" + patches_branch + ") found, cannot " "remove patches.") return 1 # Get the parent branch from the patches branch config = get_patch_config(patches_branch, directory=directory) parent, spec = config['parent'], config['base'] if None in [parent, spec]: error("Could not retrieve patches info.") return 1 debug("Removing patches from " + current_branch + " back to base " "commit " + spec) # Reset this branch using git reset --hard spec execute_command('git reset --hard ' + spec, cwd=directory) finally: if current_branch: checkout(current_branch, directory=directory) return 0
def handle_tree(tree, directory, root_path, version): for path, kind in tree.items(): if kind == 'directory': # Path relative to start path rel_path = os.path.join(directory, path) # If it is a file, error if os.path.isfile(rel_path): error("In patches path '{0}' is a directory".format(rel_path) + ", but it exists in the upstream branch as a file.", exit=True) # If it is not already a directory, create it if not os.path.isdir(rel_path): info(" Createing directory... '{0}'".format(rel_path)) os.mkdir(rel_path) # Recurse on the directory handle_tree( ls_tree(BLOOM_CONFIG_BRANCH, os.path.join(root_path, rel_path)), rel_path, root_path, version) if kind == 'file': # Path relative to start path rel_path = os.path.join(directory, path) # If the local version is a directory, error if os.path.isdir(rel_path): error("In patches path '{0}' is a file, ".format(rel_path) + "but it exists in the upstream branch as a directory.", exit=True) # If the file already exists, warn if os.path.isfile(rel_path): warning(" File '{0}' already exists, overwriting...".format( rel_path)) execute_command('git rm {0}'.format(rel_path), shell=True) # If package.xml tempalte in version, else grab data if path in ['stack.xml']: warning( " Skipping '{0}' templating, fuerte not supported".format( rel_path)) if path in ['package.xml']: info(" Templating '{0}' into upstream branch...".format( rel_path)) file_data = show(BLOOM_CONFIG_BRANCH, os.path.join(root_path, rel_path)) file_data = file_data.replace(':{version}', version) else: info(" Overlaying '{0}' into upstream branch...".format( rel_path)) file_data = show(BLOOM_CONFIG_BRANCH, os.path.join(root_path, rel_path)) # Write file with open(rel_path, 'wb') as f: # Python 2 will treat this as an ascii string but # Python 3 will not re-decode a utf-8 string. if sys.version_info.major == 2: file_data = file_data.decode('utf-8').encode('utf-8') else: file_data = file_data.encode('utf-8') f.write(file_data) # Add it with git execute_command('git add {0}'.format(rel_path), shell=True)
def import_patches(patches_path, patches_path_dict, target_branch, version): info("Overlaying files from patched folder '{0}' on the '{2}' branch into the '{1}' branch..." .format(patches_path, target_branch, BLOOM_CONFIG_BRANCH)) with inbranch(target_branch): handle_tree(patches_path_dict, '', patches_path, version) cmd = ('git commit --allow-empty -m "Overlaid patches from \'{0}\'"' .format(patches_path)) execute_command(cmd, shell=True)
def write_tracks_dict_raw(tracks_dict, cmt_msg=None, directory=None): cmt_msg = cmt_msg if cmt_msg is not None else 'Modified tracks.yaml' with inbranch('bloom'): with open('tracks.yaml', 'w') as f: f.write(yaml.dump(tracks_dict, indent=2, default_flow_style=False)) execute_command('git add tracks.yaml', cwd=directory) execute_command('git commit --allow-empty -m "{0}"'.format(cmt_msg), cwd=directory)
def write_tracks_dict_raw(tracks_dict, cmt_msg=None, directory=None): upconvert_bloom_to_config_branch() cmt_msg = cmt_msg if cmt_msg is not None else 'Modified tracks.yaml' with inbranch(BLOOM_CONFIG_BRANCH): with open('tracks.yaml', 'w') as f: f.write(yaml.safe_dump(tracks_dict, indent=2, default_flow_style=False)) execute_command('git add tracks.yaml', cwd=directory) execute_command('git commit --allow-empty -m "{0}"'.format(cmt_msg), cwd=directory)
def write_tracks_dict_raw(tracks_dict, cmt_msg=None, directory=None): upconvert_bloom_to_config_branch() cmt_msg = cmt_msg if cmt_msg is not None else 'Modified tracks.yaml' with inbranch(BLOOM_CONFIG_BRANCH): with open('tracks.yaml', 'w') as f: f.write(yaml.dump(tracks_dict, indent=2, default_flow_style=False)) execute_command('git add tracks.yaml', cwd=directory) execute_command('git commit --allow-empty -m "{0}"'.format(cmt_msg), cwd=directory)
def checkout(reference, raise_exc=False, directory=None, show_git_status=True): """ Returns True if the checkout to a the reference was successful, else False :param reference: branch, tag, or commit hash to checkout to :param directory: directory in which to run this command :returns: True if the checkout was successful, else False """ def checkout_summarize(fail_msg, branch, directory): branch = "(no branch)" if branch is None else branch directory = os.getcwd() if directory is None else directory error( "Failed to checkout to '{0}'".format(str(reference)) + " because the working directory {0}".format(str(fail_msg)) ) debug(" Working directory: '{0}'".format(str(directory))) debug(" Working branch: '{0}'".format(str(branch))) debug(" Has local changes: '{0}'".format(str(changes))) debug(" Has untrakced files: '{0}'".format(str(untracked))) pdb_hook() if not bloom.util._quiet and show_git_status: print("\n++ git status:\n") os.system("git status") return 1 debug("Checking out to " + str(reference)) if reference == get_current_branch(directory): debug("Requested checkout reference is the same as the current branch") return 0 fail_msg = "" git_root = get_root(directory) if git_root is not None: changes = has_changes(directory) untracked = has_untracked_files(directory) branch = get_current_branch(directory) else: fail_msg = "is not a git repository" if fail_msg == "" and changes: fail_msg = "has local changes" if fail_msg == "" and untracked: fail_msg = "has untracked files" try: if not changes and not untracked: execute_command('git checkout "{0}"'.format(str(reference)), cwd=directory) except CalledProcessError as err: fail_msg = "CalledProcessError: " + str(err) if raise_exc: checkout_summarize(fail_msg, branch, directory) raise if fail_msg != "": return checkout_summarize(fail_msg, branch, directory) else: return 0
def set_releaser_history(self, history): # Assumes that this is called in the target branch patches_branch = 'patches/' + get_current_branch() debug("Writing release history to '{0}' branch".format(patches_branch)) with inbranch(patches_branch): with open('releaser_history.json', 'w') as f: f.write(json.dumps(history)) execute_command('git add releaser_history.json') if has_changes(): execute_command('git commit -m "Store releaser history"')
def delete_tag(tag, directory=None): """ Deletes a given local tag. :param tag: local tag to delete :param directory: directory in which to preform this action :raises: subprocess.CalledProcessError if any git calls fail """ execute_command('git tag -d {0}'.format(tag), shell=True, cwd=directory)
def __init__(self, directory=None, track_all=True): self.tmp_dir = None self.directory = directory if directory is not None else os.getcwd() if get_root(directory) is None: raise RuntimeError("Provided directory, '" + str(directory) + "', is not a git repository") self.tmp_dir = tempfile.mkdtemp() self.clone_dir = os.path.join(self.tmp_dir, "clone") self.repo_url = "file://" + os.path.abspath(self.directory) execute_command("git clone " + self.repo_url + " " + self.clone_dir) track_branches(self.clone_dir)
def import_patches(patches_path, patches_path_dict, target_branch, version): info( "Overlaying files from patched folder '{0}' on the 'bloom' branch into the '{1}' branch...".format( patches_path, target_branch ) ) with inbranch(target_branch): handle_tree(patches_path_dict, "", patches_path, version) cmd = "git commit --allow-empty -m \"Overlaid patches from '{0}'\"".format(patches_path) execute_command(cmd, shell=True)
def create_tag(tag, directory=None): """ Creates a given tag :param tag: tag to create :param directory: directory in which to preform this action :raises: subprocess.CalledProcessError if any git calls fail """ execute_command("git tag {0}".format(tag), shell=True, cwd=directory)
def post_patch(self, destination): # Figure out the version of the given package with inbranch(destination): package_data = get_package_data(destination) if type(package_data) not in [list, tuple]: return package_data name, version, packages = package_data # Execute git tag execute_command('git tag -f ' + destination + '/' + version) return 0
def place_template_files(self, build_type, debian_dir='debian'): # Create/Clean the debian folder if os.path.exists(debian_dir): if self.interactive: warning("debian directory exists: " + debian_dir) warning("Do you wish to overwrite it?") if not maybe_continue('y'): error("Answered no to continue, aborting.", exit=True) elif 'BLOOM_CLEAR_DEBIAN_ON_GENERATION' in os.environ: warning("Overwriting debian directory: " + debian_dir) execute_command('git rm -rf ' + debian_dir) execute_command( 'git commit -m "Clearing previous debian folder"') if os.path.exists(debian_dir): shutil.rmtree(debian_dir) else: warning("Not overwriting debian directory.") # Use generic place template files command place_template_files('.', build_type, gbp=True) # Commit results execute_command('git add ' + debian_dir) _, has_files, _ = execute_command('git diff --cached --name-only', return_io=True) if has_files: execute_command('git commit -m "Placing debian template files"')
def delete_remote_tag(tag, remote="origin", directory=None): """ Deletes a given remote tag. :param tag: remote tag to delete :param remote: git remote to delete tag from (defaults to 'origin') :param directory: directory in which to preform this action :raises: subprocess.CalledProcessError if any git calls fail """ execute_command("git push {0} :{1}".format(remote, tag), shell=True, cwd=directory)
def upconvert_bloom_to_config_branch(): global _has_checked_bloom_branch if _has_checked_bloom_branch: return # Assert that this repository does not have multiple remotes check_for_multiple_remotes() if get_root() is None: # Not a git repository return track_branches(['bloom', BLOOM_CONFIG_BRANCH]) if show('bloom', PLACEHOLDER_FILE) is not None: return if show('bloom', 'bloom.conf') is not None: # Wait for the bloom.conf upconvert... return if not branch_exists('bloom'): return _has_checked_bloom_branch = True info("Moving configurations from deprecated 'bloom' branch " "to the '{0}' branch.".format(BLOOM_CONFIG_BRANCH)) tmp_dir = mkdtemp() git_root = get_root() try: # Copy the new upstream source into the temporary directory with inbranch('bloom'): ignores = ('.git', '.gitignore', '.svn', '.hgignore', '.hg', 'CVS') configs = os.path.join(tmp_dir, 'configs') my_copytree(git_root, configs, ignores) if [x for x in os.listdir(os.getcwd()) if x not in ignores]: execute_command('git rm -rf ./*') with open(PLACEHOLDER_FILE, 'w') as f: f.write("""\ This branch ('bloom') has been deprecated in favor of storing settings and overlay files in the master branch. Please goto the master branch for anything which referenced the bloom branch. You can delete this branch at your convenience. """) execute_command('git add ' + PLACEHOLDER_FILE) if has_changes(): execute_command('git commit -m "DEPRECATING BRANCH"') if not branch_exists(BLOOM_CONFIG_BRANCH): info("Creating '{0}' branch.".format(BLOOM_CONFIG_BRANCH)) create_branch(BLOOM_CONFIG_BRANCH, orphaned=True) with inbranch(BLOOM_CONFIG_BRANCH): my_copytree(configs, git_root) execute_command('git add ./*') if has_changes(): execute_command('git commit -m ' '"Moving configs from bloom branch"') finally: # Clean up if os.path.exists(tmp_dir): shutil.rmtree(tmp_dir)
def checkout(reference, raise_exc=False, directory=None, show_git_status=True): """ Returns True if the checkout to a the reference was successful, else False :param reference: branch, tag, or commit hash to checkout to :param directory: directory in which to run this command :returns: True if the checkout was successful, else False """ def checkout_summarize(fail_msg, branch, directory): branch = '(no branch)' if branch is None else branch directory = os.getcwd() if directory is None else directory error("Failed to checkout to '{0}'".format(str(reference)) + " because the working directory {0}".format(str(fail_msg))) debug(" Working directory: '{0}'".format(str(directory))) debug(" Working branch: '{0}'".format(str(branch))) debug(" Has local changes: '{0}'".format(str(changes))) debug(" Has untrakced files: '{0}'".format(str(untracked))) pdb_hook() if not bloom.util._quiet and show_git_status: info('\n++ git status:\n', use_prefix=False) os.system('git status') return False debug("Checking out to " + str(reference)) if reference == get_current_branch(directory): debug("Requested checkout reference is the same as the current branch") return True fail_msg = '' git_root = get_root(directory) if git_root is not None: changes = has_changes(directory) untracked = has_untracked_files(directory) branch = get_current_branch(directory) or 'could not determine branch' else: fail_msg = "is not a git repository" if fail_msg == '' and changes: fail_msg = "has local changes" if fail_msg == '' and untracked: fail_msg = "has untracked files" try: if not changes and not untracked: execute_command('git checkout "{0}"'.format(str(reference)), cwd=directory) except CalledProcessError as err: fail_msg = "CalledProcessError: " + str(err) if raise_exc: checkout_summarize(fail_msg, branch, directory) raise if fail_msg != '': return checkout_summarize(fail_msg, branch, directory) else: return True
def __enter__(self): if self.disabled: return current_branch = get_current_branch() if current_branch is None: warning("Could not determine current branch, changing to the bloom branch") execute_command('git checkout bloom') self.orig_cwd = os.getcwd() os.chdir(self.clone_dir) if self.track_all: track_branches(directory=self.clone_dir) return os.getcwd()
def _undo(config, directory): debug("_undo(" + str(config) + ", " + str(directory) + ")") # TODO: handle repo with changes # TODO: handle repo with patches applied if config['trimbase'] == '': debug("Branch has not been trimmed previously, undo not required.") return None # Reset with git-reset execute_command('git reset --hard ' + config['trimbase'], cwd=directory) # Unset the trimbase config['trimbase'] = '' return config
def delete_remote_tag(tag, remote='origin', directory=None): """ Deletes a given remote tag. :param tag: remote tag to delete :param remote: git remote to delete tag from (defaults to 'origin') :param directory: directory in which to preform this action :raises: subprocess.CalledProcessError if any git calls fail """ execute_command('git push {0} :{1}'.format(remote, tag), shell=True, cwd=directory)
def check_git_init(): if get_root() is None: error("Not in a valid git repository", exit=True) cmd = 'git show-ref --heads' result = execute_command(cmd, autofail=False, silent_error=True) if result != 0: info("Freshly initialized git repository detected.") info("An initial empty commit is going to be made.") if not maybe_continue(): error("Answered no to continue, exiting.", exit=True) # Make an initial empty commit execute_command('git commit --allow-empty -m "Initial commit"', silent=True)
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 commit(self): if self.disabled: return info(fmt("@{bf}<==@| Command successful, committing changes to working copy")) current_branch = get_current_branch() if current_branch is None: error("Could not determine current branch.", exit=True) with inbranch(get_commit_hash(get_current_branch())): with change_directory(self.clone_dir): new_branches = get_branches() for branch in self.current_branches: if branch in new_branches: new_branches.remove(branch) for branch in get_branches(local_only=True): if branch not in new_branches: with inbranch(branch): cmd = 'git pull --rebase origin ' + branch execute_command(cmd) execute_command('git push --all', silent=False) try: execute_command('git push --tags', silent=False) except subprocess.CalledProcessError: warning("Force pushing tags from clone to working repository, " "you will have to force push back to origin...") execute_command('git push --force --tags', silent=False) self.clean_up()
def _undo(config, directory): debug("_undo(" + str(config) + ", " + str(directory) + ")") # TODO: handle repo with changes # TODO: handle repo with patches applied if config['trimbase'] == '': debug("Branch has not been trimmed previously, undo not required.") return None # Reset with git-revert cmt = get_commit_hash(get_current_branch(directory), directory) cmd = 'git revert --no-edit -Xtheirs ' + config['trimbase'] + '..' + cmt execute_command(cmd, cwd=directory) # Unset the trimbase config['trimbase'] = '' return config
def commit(self): if self.disabled: return info( fmt("@{bf}<==@| Command successful, committing changes to working copy" )) current_branch = get_current_branch() if current_branch is None: error("Could not determine current branch.", exit=True) with inbranch(get_commit_hash(get_current_branch())): with change_directory(self.clone_dir): new_branches = get_branches() for branch in self.current_branches: if branch in new_branches: new_branches.remove(branch) for branch in get_branches(local_only=True): if branch not in new_branches: with inbranch(branch): cmd = 'git pull --rebase origin ' + branch execute_command(cmd) execute_command('git push --all', silent=False) try: execute_command('git push --tags', silent=False) except subprocess.CalledProcessError: warning( "Force pushing tags from clone to working repository, " "you will have to force push back to origin...") execute_command('git push --force --tags', silent=False) self.clean_up()
def import_patches(directory=None): # Get current branch current_branch = get_current_branch(directory) # Construct the patches branch name patches_branch = 'patches/' + current_branch # Ensure the patches branch exists and is tracked if branch_exists(patches_branch, False, directory=directory): if not branch_exists(patches_branch, True, directory=directory): track_branches(patches_branch, directory) else: error("The patches branch ({0}) does not ".format(patches_branch) + \ "exist, did you use git-bloom-branch?") return code.BRANCH_DOES_NOT_EXIST # Create a swap space tmp_dir = tempfile.mkdtemp() try: # Get parent branch and base commit from patches branch config = get_patch_config(patches_branch, directory) parent_branch, commit = config['parent'], config['base'] if commit != get_commit_hash(current_branch, directory): warning("The current commit is not the same as the most recent " "rebase commit. This might mean that you have committed " "since the last time you did 'git-bloom-patch export'.") return code.PATCHES_NOT_EXPORTED # Checkout to the patches branch checkout(patches_branch, directory=directory) # Copy the patches to a temp location patches = list_patches(directory) if len(patches) == 0: debug("No patches in the patches branch, nothing to do") return code.NOTHING_TO_DO tmp_dir_patches = [] for patch in patches: tmp_dir_patches.append(os.path.join(tmp_dir, patch)) if directory is not None: patch = os.path.join(directory, patch) shutil.copy(patch, tmp_dir) # Now checkout back to the original branch and import them checkout(current_branch, directory=directory) cmd = 'git am {0}*.patch'.format(tmp_dir + os.sep) execute_command(cmd, cwd=directory) # Notify the user info("Applied {0} patches".format(len(patches))) finally: if current_branch: checkout(current_branch, directory=directory) if os.path.exists(tmp_dir): shutil.rmtree(tmp_dir) return 0
def post_patch(self, destination): # Figure out the version of the given package if self.name is not None: warning("Cannot automatically tag the release because this is " "not a catkin project. Please create a tag manually with:") warning(" git tag -f release/" + str(self.name) + "/<version>") return 0 with inbranch(destination): package_data = get_package_data(destination) if type(package_data) not in [list, tuple]: return package_data name, version, packages = package_data # Execute git tag execute_command('git tag -f ' + destination + '/' + version) return 0
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 post_patch(self, destination): # Figure out the version of the given package if self.name is not None: warning("""\ Cannot automatically tag the release because this is not a catkin project.""") warning("""\ Please checkout the release branch and then create a tag manually with:""") warning(" git checkout release/" + str(self.name)) warning(" git tag -f release/" + str(self.name) + "/<version>") return with inbranch(destination): name, version, packages = get_package_data(destination) # Execute git tag release_tag = destination + '/' + version + '-' + self.release_inc execute_command('git tag ' + release_tag)
def show(reference, path, directory=None): """ Interface to the git show command. If path is a file that exists, a string will be returned which is the contents of that file. If the path is a directory that exists, then a dictionary is returned where the keys are items in the folder and the value is either the string 'file' or 'directory'. If the path does not exist then this returns None. :param reference: git reference to pull from (branch, tag, or commit) :param path: path to show or list :param directory: directory in which to run this command :returns: string if a file, dict if a directory, None if it does not exist :raises: subprocess.CalledProcessError if any git calls fail """ # Check to see if this is a directory dirs = ls_tree(reference, path, directory) if dirs is not None: return dirs # Otherwise a file or does not exist, check for the file cmd = 'git show {0}:{1}'.format(reference, path) # Check to see if it is a directory retcode, out, err = execute_command(cmd, autofail=False, silent_error=True, cwd=directory, return_io=True) if retcode != 0: # Does not exist return None # It is a file that exists, return the output return out
def _undo(config, directory): debug("_undo(" + str(config) + ", " + str(directory) + ")") # TODO: handle repo with changes # TODO: handle repo with patches applied if config['trimbase'] == '': debug("Branch has not been trimmed previously, undo not required.") return None # Reset with git-revert current_branch = get_current_branch(directory) if current_branch is None: error("Could not determine current branch.", exit=True) cmt = get_commit_hash(current_branch, directory) cmd = 'git revert --no-edit -Xtheirs ' + config['trimbase'] + '..' + cmt execute_command(cmd, cwd=directory) # Unset the trimbase config['trimbase'] = '' return config
def __init__(self, directory=None, track_all=True): self.disabled = get_git_clone_state() self.disabled_quiet = get_git_clone_state_quiet() if self.disabled: if not self.disabled_quiet: warning('Skipping transactional safety mechanism, be careful...') return self.tmp_dir = None self.directory = directory if directory is not None else os.getcwd() if get_root(directory) is None: raise RuntimeError("Provided directory, '" + str(directory) + "', is not a git repository") self.track_all = track_all if self.track_all: track_branches(directory=directory) self.current_branches = get_branches() self.tmp_dir = tempfile.mkdtemp() self.clone_dir = os.path.join(self.tmp_dir, 'clone') self.repo_url = 'file://' + os.path.abspath(self.directory) info(fmt("@!@{gf}+++@| Cloning working copy for safety")) execute_command('git clone ' + self.repo_url + ' ' + self.clone_dir)
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 fn(config): global _patch_config_keys conf_path = 'patches.conf' if directory is not None: conf_path = os.path.join(directory, conf_path) config_keys = list(config.keys()) config_keys.sort() if _patch_config_keys != config_keys: raise RuntimeError("Invalid config passed to set_patch_config") cmd = 'git config -f {0} patches.'.format(conf_path) try: for key in config: _cmd = cmd + key + ' "' + config[key] + '"' execute_command(_cmd, cwd=directory) # Stage the patches.conf file cmd = 'git add ' + conf_path execute_command(cmd, cwd=directory) if has_changes(directory): # Commit the changed config file cmd = 'git commit -m "Updated patches.conf"' execute_command(cmd, cwd=directory) except subprocess.CalledProcessError as err: print_exc(traceback.format_exc()) error("Failed to set patches info: " + str(err)) raise
def test_parse_bloom_conf(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, 'bloom.conf'), 'w+') f.write('[bloom]\n\tupstream = git://github.com/ros/langs.git' '\n\tupstreamtype = git\n') f.close() execute_command('git add bloom.conf', cwd=self.git_repo) execute_command('git commit -m "Init"', cwd=self.git_repo) execute_command('git branch bloom', cwd=self.git_repo) execute_command('git checkout bloom', cwd=self.git_repo) # Parse the config file from bloom.import_upstream import parse_bloom_conf config = parse_bloom_conf(self.git_repo) # Assert correct behavior assert config[0] == 'git://github.com/ros/langs.git', config assert config[1] == 'git', config assert config[2] == '', config # Clean up from shutil import rmtree rmtree(self.git_repo)
def remove_patches(directory=None): # Get the current branch current_branch = get_current_branch(directory) if current_branch is None: error("Could not determine current branch.", exit=True) # Ensure the current branch is valid if current_branch is None: error("Could not determine current branch, are you in a git repo?", exit=True) # Construct the patches branch patches_branch = 'patches/' + current_branch try: # See if the patches branch exists if branch_exists(patches_branch, False, directory=directory): if not branch_exists(patches_branch, True, directory=directory): track_branches(patches_branch, directory) else: error("No patches branch (" + patches_branch + ") found, cannot " "remove patches.", exit=True) # Get the parent branch from the patches branch config = get_patch_config(patches_branch, directory=directory) parent, spec = config['parent'], config['base'] if None in [parent, spec]: error("Could not retrieve patches info.", exit=True) debug("Removing patches from " + current_branch + " back to base " "commit " + spec) # Reset this branch using git revert --no-edit spec current_commit = get_commit_hash(current_branch, directory) command_spec = spec + '..' + current_commit execute_command('git revert --no-edit -Xtheirs ' + command_spec, cwd=directory) # Update the base config['base'] = get_commit_hash(current_branch, directory) set_patch_config(patches_branch, config, directory=directory) finally: if current_branch: checkout(current_branch, directory=directory)
def export_patches(directory=None): ### Ensure a clean/valid working environment ensure_clean_working_env(git_status=True, directory=directory) # Get current branch current_branch = get_current_branch(directory) if current_branch is None: error("Could not determine current branch.", exit=True) # Construct the patches branch name patches_branch = 'patches/' + current_branch # Ensure the patches branch exists if not branch_exists(patches_branch, False, directory=directory): error("The patches branch ({0}) does not ".format(patches_branch) + "exist, did you use git-bloom-branch?", exit=True) try: # Get parent branch and base commit from patches branch config = get_patch_config(patches_branch, directory) if config is None: error("Failed to get patches information.", exit=True) # Checkout to the patches branch checkout(patches_branch, directory=directory) # Notify the user debug("Exporting patches from " "{0}...{1}".format(config['base'], current_branch)) # Remove all the old patches if len(list_patches(directory)) > 0: cmd = 'git rm ./*.patch' execute_command(cmd, cwd=directory) # Create the patches using git format-patch cmd = "git format-patch -M -B " \ "{0}...{1}".format(config['base'], current_branch) execute_command(cmd, cwd=directory) # Report of the number of patches created patches_list = list_patches(directory) debug("Created {0} patches".format(len(patches_list))) # Clean up and commit if len(patches_list) > 0: cmd = 'git add ./*.patch' execute_command(cmd, cwd=directory) if has_changes(directory): cmd = 'git commit -m "Updating patches."' execute_command(cmd, cwd=directory) finally: if current_branch: checkout(current_branch, directory=directory)
def place_template_files(self, build_type, rpm_dir='rpm'): # Create/Clean the rpm folder if os.path.exists(rpm_dir): if self.interactive: warning("rpm directory exists: " + rpm_dir) warning("Do you wish to overwrite it?") if not maybe_continue('y'): error("Answered no to continue, aborting.", exit=True) else: warning("Overwriting rpm directory: " + rpm_dir) execute_command('git rm -rf ' + rpm_dir) execute_command('git commit -m "Clearing previous rpm folder"') if os.path.exists(rpm_dir): shutil.rmtree(rpm_dir) # Use generic place template files command place_template_files('.', build_type, gbp=True) # Commit results execute_command('git add ' + rpm_dir) execute_command('git commit -m "Placing rpm template files"')