def setUp(self): TitoGitTestFixture.setUp(self) # Guess based on python version. # Do not use anything based on uname in case we are in container. # Do not use `lsb_release` to avoid dependencies. if sys.version[0:3] == '2.4': raise SkipTest('git-annex is not available in epel-5') status, ga_version = getstatusoutput('rpm -q git-annex') if status != 0: raise SkipTest("git-annex is missing") # Setup test config: self.config = RawConfigParser() self.config.add_section("buildconfig") self.config.set("buildconfig", "builder", "tito.builder.GitAnnexBuilder") self.config.set("buildconfig", "offline", "true") os.chdir(self.repo_dir) spec = join(os.path.dirname(__file__), "specs/extsrc.spec") self.create_project_from_spec(PKG_NAME, self.config, spec=spec) self.source_filename = 'extsrc-0.0.2.tar.gz' # Make a fake source file, do we need something more real? run_command('touch %s' % self.source_filename) print(run_command('git-annex init')) self.output_dir = tempfile.mkdtemp("-titotestoutput")
def tag_new_version(project_path, new_version_release): """ Find the line with version number and change it to contain the new version. """ file_name = "Cargo.toml" config_file = os.path.join(project_path, file_name) if not os.path.exists(config_file): debug('Cargo.toml file not found, this is probably not a Rust project') return debug("Found Cargo.toml file, attempting to update the version.") # We probably don't want version-release in config file as # release is an RPM concept new_version = new_version_release.split('-')[0] file_buffer = [] # Read file line by line and replace version when found with open(config_file, 'r') as cfgfile: file_buffer = CargoBump.process_cargo_toml(cfgfile, new_version) # Write the buffer back into the file with open(config_file, 'w') as cfgfile: cfgfile.writelines(map(lambda x: x + "\n", file_buffer)) # Add Cargo.toml into git index run_command("git add %s" % file_name)
def test_template_version_tagger(self): """ Make sure the template is applied and results in the correct file being included in the tag. """ pkg_dir = join(self.repo_dir, 'pkg3') filename = join(pkg_dir, "tito.props") self.write_file(filename, TEMPLATE_TAGGER_TITO_PROPS) run_command('mkdir -p %s' % join(self.repo_dir, 'rel-eng/templates')) self.write_file(join(self.repo_dir, 'rel-eng/templates/version.rb'), VERSION_TEMPLATE_FILE) index = self.repo.index index.add(['pkg3/tito.props']) index.commit("Adding tito.props for pkg3.") # Create another pkg3 tag and make sure we got a generated # template file. os.chdir(os.path.join(self.repo_dir, 'pkg3')) tito('tag --debug --accept-auto-changelog') new_ver = get_latest_tagged_version(TEST_PKG_3) self.assertEquals("0.0.2-1", new_ver) dest_file = os.path.join(self.repo_dir, 'pkg3', "version.txt") self.assertTrue(os.path.exists(dest_file)) f = open(dest_file, 'r') contents = f.read() f.close() self.assertTrue("VERSION = \"0.0.2-1\"" in contents)
def _build_in_mock(self): if not self.speedup: print("Initializing mock...") run_command("mock %s -r %s --init" % (self.mock_cmd_args, self.mock_tag)) else: print("Skipping mock --init due to speedup option.") print("Installing deps in mock...") run_command("mock %s -r %s %s" % ( self.mock_cmd_args, self.mock_tag, self.srpm_location)) print("Building RPMs in mock...") run_command('mock %s -r %s --rebuild %s' % (self.mock_cmd_args, self.mock_tag, self.srpm_location)) mock_output_dir = os.path.join(self.rpmbuild_dir, "mockoutput") run_command("mock %s -r %s --copyout /builddir/build/RPMS/ %s" % (self.mock_cmd_args, self.mock_tag, mock_output_dir)) # Copy everything mock wrote out to /tmp/tito: files = os.listdir(mock_output_dir) run_command("cp -v %s/*.rpm %s" % (mock_output_dir, self.rpmbuild_basedir)) print info_out("Wrote:") for rpm in files: rpm_path = os.path.join(self.rpmbuild_basedir, rpm) print(" %s" % rpm_path) self.artifacts.append(rpm_path) print
def patch_upstream(self): """ Create one patch per each release """ ch_dir = self.git_root if self.relative_project_dir != "/": ch_dir = os.path.join(self.git_root, self.relative_project_dir) os.chdir(ch_dir) debug("Running /usr/bin/generate-patches.pl -d %s %s %s-1 %s %s" % (self.rpmbuild_gitcopy, self.project_name, self.upstream_version, self.build_version, self.git_commit_id)) output = run_command("/usr/bin/generate-patches.pl -d %s %s %s-1 %s %s" % (self.rpmbuild_gitcopy, self.project_name, self.upstream_version, self.build_version, self.git_commit_id)) self.patch_files = output.split("\n") for p_file in self.patch_files: (status, output) = getstatusoutput( "grep 'Binary files .* differ' %s/%s " % (self.rpmbuild_gitcopy, p_file)) if status == 0 and output != "": error_out("You are doomed. Diff contains binary files. You can not use this builder") run_command("cp %s/%s %s" % (self.rpmbuild_gitcopy, p_file, self.rpmbuild_sourcedir)) (patch_number, patch_insert_index, patch_apply_index, lines) = self._patch_upstream() for patch in self.patch_files: lines.insert(patch_insert_index, "Patch%s: %s\n" % (patch_number, patch)) lines.insert(patch_apply_index, "%%patch%s -p1\n" % (patch_number)) patch_number += 1 patch_insert_index += 1 patch_apply_index += 2 self._write_spec(lines)
def _git_sync_files(self, project_checkout): """ Copy files from our git into each git build branch and add them. A list of safe files is used to protect critical files both from being overwritten by a git file of the same name, as well as being deleted after. """ # Build the list of all files we will copy: debug("Searching for files to copy to build system git:") files_to_copy = self._list_files_to_copy() os.chdir(project_checkout) new, copied, old = \ self._sync_files(files_to_copy, project_checkout) os.chdir(project_checkout) # Git add everything: for add_file in (new + copied): run_command("git add %s" % add_file) # Cleanup obsolete files: for cleanup_file in old: # Can't delete via full path, must not chdir: run_command("git rm %s" % cleanup_file)
def _setup_sources(self): """ Create a copy of the git source for the project at the point in time our build tag was created. Created in the temporary rpmbuild SOURCES directory. """ self._create_build_dirs() debug("Creating %s from git tag: %s..." % (self.tgz_filename, self.git_commit_id)) create_tgz(self.git_root, self.tgz_dir, self.git_commit_id, self.relative_project_dir, os.path.join(self.rpmbuild_sourcedir, self.tgz_filename)) # Extract the source so we can get at the spec file, etc. debug("Copying git source to: %s" % self.rpmbuild_gitcopy) run_command("cd %s/ && tar xzf %s" % (self.rpmbuild_sourcedir, self.tgz_filename)) # Show contents of the directory structure we just extracted. debug('', 'ls -lR %s/' % self.rpmbuild_gitcopy) # NOTE: The spec file we actually use is the one exported by git # archive into the temp build directory. This is done so we can # modify the version/release on the fly when building test rpms # that use a git SHA1 for their version. self.spec_file_name = os.path.basename(find_spec_like_file(self.rpmbuild_gitcopy)) self.spec_file = os.path.join( self.rpmbuild_gitcopy, self.spec_file_name)
def _setup_test_specfile(self): if self.test and not self.ran_setup_test_specfile: # If making a test rpm we need to get a little crazy with the spec # file we're building off. (note that this is a temp copy of the # spec) Swap out the actual release for one that includes the git # SHA1 we're building for our test package: setup_specfile_script = get_script_path("test-setup-specfile.pl") cmd = "%s %s %s %s %s-%s %s" % \ ( setup_specfile_script, self.spec_file, self.git_commit_id[:7], self.commit_count, self.project_name, self.display_version, self.tgz_filename, ) run_command(cmd) # Custom Openshift v3 stuff follows, everything above is the standard # builder cmd = '. ./hack/common.sh ; echo $(os::build::ldflags)' ldflags = run_command('bash -c \'%s\'' % (cmd) ) update_ldflags = "sed -i 's|^%%global ldflags .*$|%%global ldflags %s|' %s" % \ (ldflags, self.spec_file) output = run_command(update_ldflags) self.build_version += ".git." + str(self.commit_count) + "." + str(self.git_commit_id[:7]) self.ran_setup_test_specfile = True
def _setup_test_specfile(self): if self.test and not self.ran_setup_test_specfile: # If making a test rpm we need to get a little crazy with the spec # file we're building off. (note that this is a temp copy of the # spec) Swap out the actual release for one that includes the git # SHA1 we're building for our test package: sha = self.git_commit_id[:7] fullname = "{0}-{1}".format(self.project_name, self.display_version) munge_specfile( self.spec_file, sha, self.commit_count, fullname, self.tgz_filename, ) # Custom Openshift v3 stuff follows, everything above is the standard # builder cmd = '. ./hack/common.sh ; echo $(os::build::ldflags)' ldflags = run_command("bash -c '{0}'".format(cmd)) print("LDFLAGS::{0}".format(ldflags)) update_ldflags = \ "sed -i 's|^%%global ldflags .*$|%%global ldflags {0}|' {1}".format( ' '.join([ldflag.strip() for ldflag in ldflags.split()]), self.spec_file ) # FIXME - output is never used output = run_command(update_ldflags) self.build_version += ".git." + \ str(self.commit_count) + \ "." + \ str(self.git_commit_id[:7]) self.ran_setup_test_specfile = True
def _setup_test_specfile(self): if self.test and not self.ran_setup_test_specfile: # If making a test rpm we need to get a little crazy with the spec # file we're building off. (note that this is a temp copy of the # spec) Swap out the actual release for one that includes the git # SHA1 we're building for our test package: sha = self.git_commit_id[:7] fullname = "{0}-{1}".format(self.project_name, self.display_version) munge_specfile(self.spec_file, sha, self.commit_count, fullname, self.tgz_filename) # Custom Openshift v3 stuff follows, everything above is the standard # builder cmd = ". ./hack/common.sh ; echo $(os::build::ldflags)" ldflags = run_command("bash -c '{0}'".format(cmd)) print("LDFLAGS::{0}".format(ldflags)) update_ldflags = "sed -i 's|^%global ldflags .*$|%global ldflags {0}|' {1}".format( " ".join([ldflag.strip() for ldflag in ldflags.split()]), self.spec_file ) # FIXME - output is never used output = run_command(update_ldflags) # Add bundled deps for Fedora Guidelines as per: # https://fedoraproject.org/wiki/Packaging:Guidelines#Bundling_and_Duplication_of_system_libraries provides_list = [] with open("./Godeps/Godeps.json") as godeps: depdict = json.load(godeps) for bdep in [(dep[u"ImportPath"], dep[u"Rev"]) for dep in depdict[u"Deps"]]: provides_list.append("Provides: bundled(golang({0})) = {1}".format(bdep[0], bdep[1])) update_provides_list = "sed -i 's|^### AUTO-BUNDLED-GEN-ENTRY-POINT|{0}|' {1}".format( "\\n".join(provides_list), self.spec_file ) print(run_command(update_provides_list)) self.build_version += ".git." + str(self.commit_count) + "." + str(self.git_commit_id[:7]) self.ran_setup_test_specfile = True
def _tag_release(self): """ Tag a new release of the package, add specfile global named commit. (ie: x.y.z-r+1) and ldflags from hack/common.sh os::build::ldflags """ self._make_changelog() new_version = self._bump_version() new_version = re.sub(r"-.*", "", new_version) git_hash = get_latest_commit() update_commit = \ "sed -i 's/^%global commit .*$/%global commit {0}/' {1}".format( git_hash, self.spec_file ) output = run_command(update_commit) cmd = '. ./hack/common.sh ; echo $(os::build::ldflags)' ldflags = run_command('bash -c \'{0}\''.format(cmd)) # hack/common.sh will tell us that the tree is dirty because tito has # already mucked with things, but lets not consider the tree to be # dirty ldflags = ldflags.replace('-dirty', '') update_ldflags = \ "sed -i 's|^%global ldflags .*$|%global ldflags {0}|' {1}".format( ldflags, self.spec_file ) # FIXME - this output is never used output = run_command(update_ldflags) self._check_tag_does_not_exist(self._get_new_tag(new_version)) self._update_changelog(new_version) self._update_package_metadata(new_version)
def _git_upload_sources(self, project_checkout): chain_file = find_mead_chain_file(self.builder.rpmbuild_gitcopy) with open(chain_file, 'r') as f: template = Template(f.read()) ref = self.builder.build_tag if self.test: ref = self.builder.git_commit_id values = { 'mead_scm': self.mead_scm, 'git_ref': ref, # Each property on its own line with a few leading spaces to indicate # that it's a continuation 'maven_properties': "\n ".join(self.builder.maven_properties), 'maven_options': " ".join(self.builder.maven_args), } rendered_chain = template.safe_substitute(values) with chdir(project_checkout): with open("mead.chain", "w") as f: f.write(rendered_chain) cmd = "git add mead.chain" if self.dry_run: self.print_dry_run_warning(cmd) info_out("Chain file contents:\n%s" % rendered_chain) else: run_command(cmd)
def _update_setup_py(self, new_version): """ If this project has a setup.py, attempt to update it's version. """ setup_file = os.path.join(self.full_project_dir, "setup.py") if not os.path.exists(setup_file): return debug("Found setup.py, attempting to update version.") # We probably don't want version-release in setup.py as release is # an rpm concept. Hopefully this assumption on py_new_version = new_version.split('-')[0] f = open(setup_file, 'r') buf = StringIO.StringIO() for line in f.readlines(): buf.write(replace_version(line, py_new_version)) f.close() # Write out the new setup.py file contents: f = open(setup_file, 'w') f.write(buf.getvalue()) f.close() buf.close() run_command("git add %s" % setup_file)
def _generate_default_changelog(self, last_tag): # Grab all the commits we are interested in commits = run_command('git log --pretty=format:%%H --relative %s..HEAD -- .' % last_tag) changelog = [] for sha in reversed(commits.split('\n')): subject = run_command('git show -s --pretty="format:%s" %s' % (self._changelog_format(), sha)) # Skip Gerrit merges if re.match(r'Merge ".*" into', subject): continue # Skip Tito version bumps if re.match(r'Automatic commit of package \[.*\] release \[.*\]', subject): continue # Tito's built-in cherry-pick cleaning subject = self._changelog_remove_cherrypick(subject) # Check for Bug: footer body = run_command('git show -s --pretty=format:%%b %s' % sha) m = re.search(r'^Bug:\s*(\d+)', body, re.IGNORECASE | re.MULTILINE) if m: bz_number = m.group(1) subject = '%s %s' % (bz_number, subject) # Escape rpm macros subject = subject.replace('%', '%%') changelog.append(subject) return '\n'.join(changelog)
def patch_upstream(self): commits = run_command('git rev-list --reverse %s..%s -- .' % (self.upstream_tag, self.git_commit_id)) patch_filenames = [] previous_tag = self.upstream_tag for commit in commits.splitlines(): tag = run_command('git describe --tags --match %s-%s\\* --exact-match %s 2>/dev/null || :' % (self.project_name, self.upstream_version, commit)) if tag and tag not in BAD_TAGS: self._generate_patch(previous_tag, tag, '%s.patch' % tag) patch_filenames.append('%s.patch' % tag) previous_tag = tag if self.test: # If this is a --test build, there will be some untagged commits at # the end which we also need to include. self._generate_patch(previous_tag, self.git_commit_id, '%s.patch' % self.display_version) patch_filenames.append('%s.patch' % self.display_version) else: assert previous_tag == self.build_tag or not commits (patch_number, patch_insert_index, patch_apply_index, lines) = self._patch_upstream() for patch in patch_filenames: lines.insert(patch_insert_index, "Patch%s: %s\n" % (patch_number, patch)) lines.insert(patch_apply_index, "%%patch%s -p1\n" % (patch_number)) patch_number += 1 patch_insert_index += 1 patch_apply_index += 2 self._write_spec(lines)
def _generate_patch(self, left, right, out): # It's nice to publish patches which show the actual commits making up # the patch, with authorship, dates, log messages, etc. # We can use git log for that. But it will only produce usable patches if # left is an ancestor of every commit in left..right. # That will normally be true for a series of hotfixes on top of a # release. But if we are doing a --test build or if there are divergent # tags then this might not be true. In that case the best we can do is # git diff left..right. if not run_command('git rev-list $(git rev-list --first-parent %s..%s | tail -n1)..%s' % (left, right, left)): print 'Generating patchset %s..%s' % (left, right) run_command('git log -p --reverse --pretty=email ' '-m --first-parent ' '--no-renames ' # patch can't handle these :-( '--relative %s..%s -- . >%s/%s' % (left, right, self.rpmbuild_gitcopy, out)) else: print 'Generating diff %s..%s' % (left, right) run_command('git diff --no-renames ' '--relative %s..%s -- . >%s/%s' % (left, right, self.rpmbuild_gitcopy, out)) shutil.copy('%s/%s' % (self.rpmbuild_gitcopy, out), self.rpmbuild_sourcedir) shutil.copy('%s/%s' % (self.rpmbuild_gitcopy, out), self.rpmbuild_basedir) print 'Wrote: %s/%s' % (self.rpmbuild_basedir, out)
def _clear_package_metadata(self): """ Remove all rel-eng/packages/ files that have a relative path matching the package we're tagging a new version of. Normally this just removes the previous package file but if we were renaming oldpackage to newpackage, this would git rm rel-eng/packages/oldpackage and add rel-eng/packages/spacewalk-newpackage. """ metadata_dir = os.path.join(self.rel_eng_dir, "packages") for filename in os.listdir(metadata_dir): metadata_file = os.path.join(metadata_dir, filename) # full path if os.path.isdir(metadata_file) or filename.startswith("."): continue temp_file = open(metadata_file, 'r') (version, relative_dir) = temp_file.readline().split(" ") relative_dir = relative_dir.strip() # sometimes has a newline if relative_dir == self.relative_project_dir: debug("Found metadata for our prefix: %s" % metadata_file) debug(" version: %s" % version) debug(" dir: %s" % relative_dir) if filename == self.project_name: debug("Updating %s with new version." % metadata_file) else: print("WARNING: %s also references %s" % (filename, self.relative_project_dir)) print("Assuming package has been renamed and removing it.") run_command("git rm %s" % metadata_file)
def _update_setup_py_in_dir(self, new_version, package_dir=None): """ If this subdir has a setup.py, attempt to update it's version. (This is a very minor tweak to the original _update_setup_py method from VersionTagger """ if package_dir is not None: full_package_dir = os.path.join(self.full_project_dir, package_dir) else: full_package_dir = self.full_project_dir setup_file = os.path.join(full_package_dir, "setup.py") if not os.path.exists(setup_file): return debug("Found setup.py in {}, attempting to update version.".format(package_dir)) # We probably don't want version-release in setup.py as release is # an rpm concept. Hopefully this assumption on py_new_version = new_version.split('-')[0] f = open(setup_file, 'r') buf = six.StringIO() for line in f.readlines(): buf.write(replace_version(line, py_new_version)) f.close() # Write out the new setup.py file contents: f = open(setup_file, 'w') f.write(buf.getvalue()) f.close() buf.close() run_command("git add %s" % setup_file)
def _bump_version(self, release=False, zstream=False): """ Bump up the package version in the spec file. Set release to True to bump the package release instead. Checks for the keep version option and if found, won't actually bump the version or release. """ old_version = get_latest_tagged_version(self.project_name) if old_version == None: old_version = "untagged" # TODO: Do this here instead of calling out to an external Perl script: if not self.keep_version: bump_type = "bump-version" if release: bump_type = "bump-release" elif zstream: bump_type = "bump-zstream" script_path = get_script_path("bump-version.pl") cmd = "%s %s --specfile %s" % \ (script_path, bump_type, self.spec_file) run_command(cmd) new_version = self._get_spec_version_and_release() if new_version.strip() == "": msg = "Error getting bumped package version, try: \n" msg = msg + " 'rpm -q --specfile %s'" % self.spec_file error_out(msg) print("Tagging new version of %s: %s -> %s" % (self.project_name, old_version, new_version)) return new_version
def setUp(self): self.repo_dir = tempfile.mkdtemp("-titocargotest") print("Testing in: %s" % self.repo_dir) os.chdir(self.repo_dir) self.full_pkg_dir = os.path.join(self.repo_dir, "hello_tito") run_command('mkdir -p %s' % self.full_pkg_dir) # Initialize the repo: os.chdir(self.full_pkg_dir) run_command('git init') # Next we tito init: tito("init") run_command('echo "offline = true" >> .tito/tito.props') run_command('git add .tito/tito.props') run_command("git commit -m 'set offline in tito.props'") # Init cargo project self.create_cargo_project() # Init RPM package self.create_rpm_package() # Run tito tag tito("tag --accept-auto-changelog")
def tgz(self): print('Fetching third-party tarballs') run_command('make -C third-party tarballs') debug('Copying third-party tarballs') for line in open('third-party/tarballs'): tarball = line.strip() shutil.copy(os.path.join('third-party', tarball), self.rpmbuild_sourcedir) self.sources.append(tarball) return super(RestraintBuilder, self).tgz()
def cleanup(self): if not self.no_cleanup: debug("Cleaning up [%s]" % self.working_dir) run_command("rm -rf %s" % self.working_dir) if self.builder: self.builder.cleanup() else: warn_out("leaving %s (--no-cleanup)" % self.working_dir)
def fetch(self): if "jenkins_job" in self.builder.args: gitrev = self._fetch_jenkins() elif "source_dir" in self.builder.args: gitrev = self._fetch_local() else: raise Exception("Specify either '--arg jenkins_job=...' or '--arg source_dir=...'") # Copy the live spec from our starting location. Unlike most builders, # we are not using a copy from a past git commit. self.spec_file = os.path.join(self.builder.rpmbuild_sourcedir, '%s.spec' % self.builder.project_name) shutil.copyfile( os.path.join(self.builder.start_dir, '%s.spec' % self.builder.project_name), self.spec_file) for s in os.listdir(self.builder.start_dir): if os.path.exists(os.path.join(self.builder.start_dir, s)): shutil.copyfile( os.path.join(self.builder.start_dir, s), os.path.join(self.builder.rpmbuild_sourcedir, os.path.basename(s))) print(" %s.spec" % self.builder.project_name) i = 0 replacements = [] src_files = run_command("find %s -type f" % os.path.join(self.builder.rpmbuild_sourcedir, 'archive')).split("\n") for s in src_files: base_name = os.path.basename(s) debug("Downloaded file %s" % base_name) if ".tar" not in base_name and ".gem" not in base_name: debug("Skipping %s as it isn't a source archive" % base_name) continue dest_filepath = os.path.join(self.builder.rpmbuild_sourcedir, base_name) shutil.move(s, dest_filepath) self.sources.append(dest_filepath) # Add a line to replace in the spec for each source: source_regex = re.compile("^(source%s:\s*)(.+)$" % i, re.IGNORECASE) new_line = "Source%s: %s\n" % (i, base_name) replacements.append((source_regex, new_line)) i += 1 # Replace version in spec: version_regex = re.compile("^(version:\s*)(.+)$", re.IGNORECASE) self.version = self._get_version() print("Building version: %s" % self.version) replacements.append((version_regex, "Version: %s\n" % self.version)) self.replace_in_spec(replacements) rel_date = datetime.utcnow().strftime("%Y%m%d%H%M") self.release = rel_date + gitrev print("Building release: %s" % self.release) run_command("sed -i '/^Release:/ s/%%/.%s%%/' %s" % (self.release, self.spec_file))
def test_with_releaser(self): yum_repo_dir = os.path.join(self.output_dir, 'yum') run_command('mkdir -p %s' % yum_repo_dir) self._setup_fetchbuilder_releaser(yum_repo_dir) tito('release --debug yum-test') self.assertEquals(1, len(glob.glob(join(yum_repo_dir, "releaseme-0.0.1-1.*noarch.rpm")))) self.assertEquals(1, len(glob.glob(join(yum_repo_dir, "repodata/repomd.xml"))))
def test_release_tagger_legacy_props_file(self): # Test that build.py.props filename is still picked up: os.chdir(os.path.join(MULTI_GIT, TEST_PKG_2_DIR)) start_ver = get_latest_tagged_version(TEST_PKG_2) run_command("git mv tito.props build.py.props") run_command('git commit -a -m "Rename to build.py.props"') run_tito('tag --debug --accept-auto-changelog') new_ver = get_latest_tagged_version(TEST_PKG_2) self.assertTrue(release_bumped(start_ver, new_ver))
def test_release_tagger_legacy_props_file(self): # Test that build.py.props filename is still picked up: os.chdir(os.path.join(self.repo_dir, 'pkg2')) start_ver = get_latest_tagged_version(TEST_PKG_2) run_command("git mv tito.props build.py.props") index = self.repo.index index.add(['pkg2/build.py.props']) index.commit("Rename to build.py.props.") tito('tag --debug --accept-auto-changelog') new_ver = get_latest_tagged_version(TEST_PKG_2) self.assertTrue(release_bumped(start_ver, new_ver))
def tgz(self): self.ran_tgz = True self._create_build_dirs() print("Fetching sources...") run_command(self.config.get('builder', 'fetch_prep_command')) manual_sources = [run_command("ls -1t *.tar.* | head -n1")] self.spec_file = self.project_name + '.spec' for s in manual_sources: base_name = basename(s) dest_filepath = join(self.rpmbuild_sourcedir, base_name) copyfile(s, dest_filepath) self.sources.append(dest_filepath)
def _get_git_user_info(self): """ Return the user.name and user.email git config values. """ try: name = run_command('git config --get user.name') except: warn_out('user.name in ~/.gitconfig not set.\n') name = 'Unknown name' try: email = run_command('git config --get user.email') except: warn_out('user.email in ~/.gitconfig not set.\n') email = None return (name, email)
def _get_git_user_info(self): """ Return the user.name and user.email git config values. """ try: name = run_command('git config --get user.name') except: sys.stderr.write('Warning: user.name in ~/.gitconfig not set.') name = 'Unknown name' try: email = run_command('git config --get user.email') except: sys.stderr.write('Warning: user.email in ~/.gitconfig not set.') email = None return (name, email)
def _rpm(self): super(CustomBuilder, self)._rpm() git_current_branch = common.run_command("git branch --list | grep ^\* | cut -c 3-300") temp_uuid = common.run_command("uuidgen") git_co_branch = common.run_command("git checkout -b %s" % temp_uuid) readme_md = os.path.join(self.git_root, "README.md") readme_md_rpm = common.run_command("rpm -qlp %s | grep README.md$" % self.artifacts[2]) readme_md_git = common.run_command("rpm2cpio %s | cpio --quiet -idmuv .%s 2>&1" % (self.artifacts[2], readme_md_rpm)) readme_mv = common.run_command("mv %s %s" % (readme_md_git, readme_md)) git_commit = common.run_command("git commit --allow-empty -m \"Updated README markdown file.\" README.md") git_checkout = common.run_command("git checkout %s" % git_current_branch) git_merge = common.run_command("git merge %s" % temp_uuid) git_del_branch = common.run_command("git branch -d %s" % temp_uuid)
def _build_in_mock(self): if not self.speedup: print("Initializing mock...") output = run_command("mock %s -r %s --init" % (self.mock_cmd_args, self.mock_tag)) else: print("Skipping mock --init due to speedup option.") print("Installing deps in mock...") output = run_command( "mock %s -r %s %s" % (self.mock_cmd_args, self.mock_tag, self.srpm_location)) print("Building RPMs in mock...") output = run_command( 'mock %s -r %s --rebuild %s' % (self.mock_cmd_args, self.mock_tag, self.srpm_location)) mock_output_dir = os.path.join(self.rpmbuild_dir, "mockoutput") output = run_command( "mock %s -r %s --copyout /builddir/build/RPMS/ %s" % (self.mock_cmd_args, self.mock_tag, mock_output_dir)) # Copy everything mock wrote out to /tmp/tito: files = os.listdir(mock_output_dir) run_command("cp -v %s/*.rpm %s" % (mock_output_dir, self.rpmbuild_basedir)) print print("Wrote:") for rpm in files: rpm_path = os.path.join(self.rpmbuild_basedir, rpm) print(" %s" % rpm_path) self.artifacts.append(rpm_path) print
def _recreate_tgz(source, target): """Recreate sdist-archive in a way that is expected by tito. Keyword arguments: source: Path to source archive. target: Path to expected output archive. """ def strip_ext(name, ext=".tar.gz"): return name[:-len(ext)] names = { "source": source, "target": target, "source_root": strip_ext(source.name), "target_root": strip_ext(target.name), } # Extract source run_command("tar -xzf {source}".format_map(names)) # Rename root if names["source_root"] != names["target_root"]: run_command("mv {source_root} {target_root}".format_map(names)) # Compress converted archive run_command("tar -czf {target} {target_root}".format_map(names))
def _tag_release(self): version = self._bump_version() self._check_tag_does_not_exist(version) self._clear_package_metadata() metadata_file = os.path.join(self.rel_eng_dir, 'packages', self.project_name) with open(metadata_file, 'w') as file: file.write('%s %s\n' % (version, self.relative_project_dir)) files = [ metadata_file, os.path.join(self.full_project_dir, self.spec_file_name), 'configure.ac', ] run_command('git add -- %s' % (' '.join([shlex.quote(file) for file in files]))) message = 'Release version %s' % (version) run_command('git commit --message="%s"' % message) run_command('git tag --message="%s" %s' % (message, version)) info_out('%s tagged' % version)
def test_template_version_tagger(self): """ Make sure the template is applied and results in the correct file being included in the tag. """ pkg_dir = join(self.repo_dir, 'pkg3') filename = join(pkg_dir, "tito.props") self.write_file(filename, TEMPLATE_TAGGER_TITO_PROPS) run_command('mkdir -p %s' % join(self.repo_dir, '.tito/templates')) self.write_file(join(self.repo_dir, '.tito/templates/version.rb'), VERSION_TEMPLATE_FILE) os.chdir(self.repo_dir) run_command('git add pkg3/tito.props') run_command("git commit -m 'add tito.props for pkg3'") # Create another pkg3 tag and make sure we got a generated # template file. os.chdir(os.path.join(self.repo_dir, 'pkg3')) tito('tag --debug --accept-auto-changelog') new_ver = get_latest_tagged_version(TEST_PKG_3) self.assertEquals("0.0.2-1", new_ver) dest_file = os.path.join(self.repo_dir, 'pkg3', "version.txt") self.assertTrue(os.path.exists(dest_file)) f = open(dest_file, 'r') contents = f.read() f.close() self.assertTrue("VERSION = \"0.0.2-1\"" in contents)
def _setup_sources(self): """ Create a copy of the git source for the project at the point in time our build tag was created. Created in the temporary rpmbuild SOURCES directory. """ self._create_build_dirs() debug("Creating %s from git tag: %s..." % (self.tgz_filename, self.git_commit_id)) create_tgz(self.git_root, self.tgz_dir, self.git_commit_id, self.relative_project_dir, os.path.join(self.rpmbuild_sourcedir, self.tgz_filename)) # Extract the source so we can get at the spec file, etc. debug("Copying git source to: %s" % self.rpmbuild_gitcopy) run_command("cd %s/ && tar xzf %s" % (self.rpmbuild_sourcedir, self.tgz_filename)) # Find the gemspec gemspec_filename = find_gemspec_file(in_dir=self.rpmbuild_gitcopy) debug("Building gem: %s in %s" % (gemspec_filename, self.rpmbuild_gitcopy)) # FIXME - this is ugly and should probably be handled better cmd = "gem_name=$(cd %s/ && gem build %s | awk '/File/ {print $2}'); \ cp %s/$gem_name %s/" % (self.rpmbuild_gitcopy, gemspec_filename, self.rpmbuild_gitcopy, self.rpmbuild_sourcedir) run_command(cmd) # NOTE: The spec file we actually use is the one exported by git # archive into the temp build directory. This is done so we can # modify the version/release on the fly when building test rpms # that use a git SHA1 for their version. self.spec_file_name = find_spec_file(in_dir=self.rpmbuild_gitcopy) self.spec_file = os.path.join(self.rpmbuild_gitcopy, self.spec_file_name)
def _auto_install(self): """ If requested, auto install the RPMs we just built. """ if self.auto_install: print print("Auto-installing packages:") print dont_install = [] if 'NO_AUTO_INSTALL' in self.user_config: dont_install = self.user_config['NO_AUTO_INSTALL'].split(" ") debug("Will not auto-install any packages matching: %s" % dont_install) do_install = [] for to_inst in self.artifacts: # Only install rpms: if not to_inst.endswith(".rpm") or to_inst.endswith( ".src.rpm"): continue install = True for skip in dont_install: if skip in to_inst: install = False print("Skipping: %s" % to_inst) break if install: do_install.append(to_inst) print cmd = "sudo rpm -Uvh --force %s" % ' '.join(do_install) print("%s" % cmd) try: run_command(cmd) print except KeyboardInterrupt: pass
def tgz(self): """ Override parent behavior, we already have a tgz. """ # TODO: Does it make sense to allow user to create a tgz for this type # of project? self._setup_sources() self.ran_tgz = True debug("Scanning for sources.") cmd = "/usr/bin/spectool --list-files '%s' | awk '{print $2}' |xargs -l1 --no-run-if-empty basename " % self.spec_file result = run_command(cmd) self.sources = map(lambda x: os.path.join(self.rpmbuild_gitcopy, x), result.split("\n")) debug(" Sources: %s" % self.sources)
def _generate_default_changelog(self, last_tag): """ Run git-log and will generate changelog, which still can be edited by user in _make_changelog. """ patch_command = "git log --pretty='format:%s'" \ " --relative %s..%s -- %s" % (self._changelog_format(), last_tag, "HEAD", ".") output = run_command(patch_command) result = [] for line in output.split('\n'): line = line.replace('%', '%%') result.extend([self._changelog_remove_cherrypick(line)]) return '\n'.join(result)
def _update_pom_xml(self, new_version): """ If this project uses Maven, attempt to update the version in pom.xml """ # Remove the release since Maven doesn't understand that really pom_file = os.path.join(self.full_project_dir, "pom.xml") if not os.path.exists(pom_file): return mvn_new_version = new_version.split('-')[0] maven_args = ['-B'] if 'MAVEN_ARGS' in self.user_config: maven_args.append(self.user_config['MAVEN_ARGS']) else: maven_args.append('-q') run_command("mvn %s versions:set -DnewVersion=%s -DgenerateBackupPoms=false" % ( " ".join(maven_args), mvn_new_version)) run_command("git add %s" % pom_file)
def inject_os_git_vars(spec_file): """ Determine the OpenShift version variables as dictated by the Origin shell utilities and overwrite the specfile to reflect them. A line with the following syntax is expected in the specfile: %global os_git_vars This line will be overwritten to add the git tree state, the full "git version", the last source commit in the release, and the major and minor versions of the current product release. """ os_git_vars = get_os_git_vars() for var_name in os_git_vars: print("{}::{}".format(var_name, os_git_vars[var_name])) update_os_git_vars = \ "sed -i 's|^%global os_git_vars .*$|%global os_git_vars {}|' {}".format( " ".join(["{}={}".format(key, value) for key, value in os_git_vars.items()]), spec_file ) run_command(update_os_git_vars)
def patch_upstream(self): """ Create one patch per each release """ ch_dir = self.git_root if self.relative_project_dir != "/": ch_dir = os.path.join(self.git_root, self.relative_project_dir) os.chdir(ch_dir) debug("Running generate-patches.pl -d %s %s %s-1 %s %s" % (self.rpmbuild_gitcopy, self.project_name, self.upstream_version, self.build_version, self.git_commit_id)) output = run_command( "generate-patches.pl -d %s %s %s-1 %s %s" % (self.rpmbuild_gitcopy, self.project_name, self.upstream_version, self.build_version, self.git_commit_id)) self.patch_files = output.split("\n") for p_file in self.patch_files: (status, output) = getstatusoutput("grep 'Binary files .* differ' %s/%s " % (self.rpmbuild_gitcopy, p_file)) if status == 0 and output != "": error_out( "You are doomed. Diff contains binary files. You can not use this builder" ) run_command( "cp %s/%s %s" % (self.rpmbuild_gitcopy, p_file, self.rpmbuild_sourcedir)) (patch_number, patch_insert_index, patch_apply_index, lines) = self._patch_upstream() for patch in self.patch_files: lines.insert(patch_insert_index, "Patch%s: %s\n" % (patch_number, patch)) lines.insert(patch_apply_index, "%%patch%s -p1\n" % (patch_number)) patch_number += 1 patch_insert_index += 1 patch_apply_index += 2 self._write_spec(lines)
def _tag_release(self): """ Tag a new release of the package, add specfile global named commit. (ie: x.y.z-r+1) """ self._make_changelog() new_version = self._bump_version(release=True) git_hash = get_latest_commit() update_commit = "sed -i 's/^%%global commit .*$/%%global commit %s/' %s" % \ (git_hash, self.spec_file) output = run_command(update_commit) self._check_tag_does_not_exist(self._get_new_tag(new_version)) self._update_changelog(new_version) self._update_package_metadata(new_version)
def _git_upload_sources(self, project_checkout): """ Upload any tarballs to the lookaside directory. (if necessary) Uses the "fedpkg new-sources" command. """ if not self.builder.sources: debug("No sources need to be uploaded.") return print("Uploading sources to lookaside:") os.chdir(project_checkout) cmd = '%s new-sources %s' % (self.cli_tool, " ".join(self.builder.sources)) debug(cmd) if self.dry_run: self.print_dry_run_warning(cmd) return output = run_command(cmd) debug(output) debug("Removing write-only permission on:") for filename in self.builder.sources: run_command("chmod u+w %s" % filename)
def _git_release(self): getoutput("mkdir -p %s" % self.working_dir) with chdir(self.working_dir): run_command("%s clone %s" % (self.cli_tool, self.project_name)) with chdir(self.package_workdir): run_command("%s switch-branch %s" % (self.cli_tool, self.git_branches[0])) # Set git user config to the distgit clone based on the current project self._git_set_user_config() # Mead builds need to be in the git_root. Other builders are agnostic. with chdir(self.git_root): self.builder.tgz() self.builder.copy_extra_sources() if self.test: self.builder._setup_test_specfile() self._git_sync_files(self.package_workdir) self._git_upload_sources(self.package_workdir) self._git_user_confirm_commit(self.package_workdir)
def _update_version_file(self, new_version): """ land this new_version in the designated file and stages that file for a git commit """ version_file = self._version_file_path() if not version_file: debug("No destination version file found, skipping.") return debug("Found version file to write: %s" % version_file) version_file_template = self._version_file_template() if version_file_template is None: error_out( "Version file specified but without corresponding template.") t = Template(version_file_template) f = open(version_file, 'w') (new_ver, new_rel) = new_version.split('-') f.write(t.safe_substitute(version=new_ver, release=new_rel)) f.close() run_command("git add %s" % version_file)
def patch_upstream(self): """ Create one patch per each release """ os.chdir(os.path.join(self.git_root, self.relative_project_dir)) debug("Running /usr/bin/generate-patches.pl -d %s %s %s-1 %s %s" \ % (self.rpmbuild_gitcopy, self.project_name, self.upstream_version, self.build_version, self.git_commit_id)) output = run_command("/usr/bin/generate-patches.pl -d %s %s %s-1 %s %s" \ % (self.rpmbuild_gitcopy, self.project_name, self.upstream_version, self.build_version, self.git_commit_id)) self.patch_files = output.split("\n") for p_file in self.patch_files: run_command( "cp %s/%s %s" % (self.rpmbuild_gitcopy, p_file, self.rpmbuild_sourcedir)) (patch_number, patch_insert_index, patch_apply_index, lines) = self._patch_upstream() for patch in self.patch_files: lines.insert(patch_insert_index, "Patch%s: %s\n" % (patch_number, patch)) lines.insert(patch_apply_index, "%%patch%s -p1\n" % (patch_number)) patch_number += 1 patch_insert_index += 1 patch_apply_index += 2 self._write_spec(lines)
def setUp(self): # Create a temporary directory for our test git repository: self.repo_dir = tempfile.mkdtemp("-titotest") print print print("Testing in: %s" % self.repo_dir) print # Initialize the repo: os.chdir(self.repo_dir) run_command('git init') # Next we tito init: tito("init") run_command('echo "offline = true" >> .tito/tito.props') run_command('git add .tito/tito.props') run_command("git commit -m 'set offline in tito.props'")
def prune_other_versions(self, temp_dir): """ Cleanout any other version of the package we just built. Both older and newer packages will be removed (can be used to downgrade the contents of a yum repo). """ os.chdir(temp_dir) rpm_ts = rpm.TransactionSet() self.new_rpm_dep_sets = {} for artifact in self.builder.artifacts: if artifact.endswith(".rpm") and not artifact.endswith(".src.rpm"): try: header = self._read_rpm_header(rpm_ts, artifact) except rpm.error: continue self.new_rpm_dep_sets[header['name']] = header.dsOfHeader() # Now cleanout any other version of the package we just built, # both older or newer. (can be used to downgrade the contents # of a yum repo) for filename in os.listdir(temp_dir): if not filename.endswith(".rpm"): continue full_path = os.path.join(temp_dir, filename) try: hdr = self._read_rpm_header(rpm_ts, full_path) except rpm.error: e = sys.exc_info()[1] print("error reading rpm header in '%s': %s" % (full_path, e)) continue if hdr['name'] in self.new_rpm_dep_sets: dep_set = hdr.dsOfHeader() if dep_set.EVR() < self.new_rpm_dep_sets[hdr['name']].EVR(): print("Deleting old package: %s" % filename) run_command("rm %s" % os.path.join(temp_dir, filename))
def _getCommitDetail(self, commit, field): """ Get specific details about the commit using git log format field specifiers. """ command = ['git', 'log', '-1', "--pretty=format:%s" % field, commit] output = run_command(" ".join(command)) ret = output.strip('\n').split('\n') if len(ret) == 1 and ret[0].find('@') != -1: ret = [ret[0].split('@')[0]] elif len(ret) == 1: ret = [ret[0]] else: ret = [x for x in ret if x != ''] return ret
def setUp(self): TitoGitTestFixture.setUp(self) self.pkg_dir = join(self.repo_dir, EXT_SRC_PKG) spec = join(os.path.dirname(__file__), "specs/extsrc.spec") # Setup test config: self.config = RawConfigParser() self.config.add_section("buildconfig") self.config.set("buildconfig", "builder", "tito.builder.FetchBuilder") self.config.add_section('builder') self.config.set('builder', 'fetch_strategy', 'tito.builder.fetch.ArgSourceStrategy') self.create_project_from_spec(EXT_SRC_PKG, self.config, pkg_dir=self.pkg_dir, spec=spec) self.source_filename = 'extsrc-0.0.2.tar.gz' os.chdir(self.pkg_dir) # Make a fake source file, do we need something more real? run_command('touch %s' % self.source_filename) self.output_dir = tempfile.mkdtemp("-titotestoutput")
def update_global_hash(spec_file): """ Update the specfile to reflect the latest commit. A line with the following syntax is expected in the specfile: %global commit This line will be overwritten to add the git commit. """ git_hash = get_latest_commit() update_commit = \ "sed -i 's/^%global commit .*$/%global commit {0}/' {1}".format( git_hash, spec_file ) output = run_command(update_commit)
def _obs_user_confirm_commit(self, project_checkout): """ Prompt user if they wish to proceed with commit. """ print("") text = "Running '%s diff' in: %s" % (self.cli_tool, project_checkout) print("#" * len(text)) print(text) print("#" * len(text)) print("") os.chdir(project_checkout) (status, diff_output) = getstatusoutput("%s diff" % self.cli_tool) if diff_output.strip() == "": print("No changes in main branch, skipping commit.") else: print(diff_output) print("") print("##### Please review the above diff #####") if not self._ask_yes_no( "Do you wish to proceed with commit? [y/n] "): print("Fine, you're on your own!") self.cleanup() sys.exit(1) print("Proceeding with commit.") commit_msg_file = self._confirm_commit_msg(diff_output) cmd = '%s commit -F %s' % (self.cli_tool, commit_msg_file) debug("obs commit command: %s" % cmd) print if self.dry_run: self.print_dry_run_warning(cmd) else: print("Proceeding with commit.") os.chdir(self.package_workdir) print(run_command(cmd)) os.unlink(commit_msg_file) if self.no_build: getstatusoutput( "%s abortbuild %s %s" % (self.cli_tool, self.obs_project_name, self.obs_package_name)) print( "Aborting automatic rebuild because --no-build has been specified." )
def _print_log(self, config, package_name, version, project_dir): """ Print the log between the most recent package tag and HEAD, if necessary. """ last_tag = "%s-%s" % (package_name, version) try: os.chdir(project_dir) patch_command = ("git log --pretty=oneline " "--relative %s..%s -- %s" % (last_tag, "HEAD", ".")) output = run_command(patch_command) if (output): print("-" * (len(last_tag) + 8)) print("%s..%s:" % (last_tag, "HEAD")) print(output) except: print("%s no longer exists" % project_dir)
def _upload(self, srpm_location): if self.srpm_submitted: srpm_location = self.srpm_submitted # e.g. "scp %(srpm)s my.web.com:public_html/my_srpm/" cmd = self.releaser_config.get(self.target, "upload_command") cmd_upload = cmd % {'srpm': srpm_location} if self.dry_run: self.print_dry_run_warning(cmd_upload) return # TODO: no error handling when run_command fails: if not self.srpm_submitted: print("Uploading src.rpm.") print(run_command(cmd_upload)) self.srpm_submitted = srpm_location
def _update_package_metadata(self, new_version): """ We track package metadata in the .tito/packages/ directory. Each file here stores the latest package version (for the git branch you are on) as well as the relative path to the project's code. (from the git root) """ self._clear_package_metadata() suffix = "" # If global config specifies a tag suffix, use it: if self.config.has_option(BUILDCONFIG_SECTION, "tag_suffix"): suffix = self.config.get(BUILDCONFIG_SECTION, "tag_suffix") new_version_w_suffix = "%s%s" % (new_version, suffix) # Write out our package metadata: metadata_file = os.path.join(self.rel_eng_dir, "packages", self.project_name) with open(metadata_file, 'w') as f: f.write("%s %s\n" % (new_version_w_suffix, self.relative_project_dir)) # Git add it (in case it's a new file): run_command("git add %s" % metadata_file) run_command("git add %s" % os.path.join(self.full_project_dir, self.spec_file_name)) run_command( 'git commit -m "Automatic commit of package ' + '[%s] %s [%s]."' % (self.project_name, self.release_type(), new_version_w_suffix)) tag_msg = "Tagging package [%s] version [%s] in directory [%s]." % \ (self.project_name, new_version_w_suffix, self.relative_project_dir) new_tag = self._get_new_tag(new_version) run_command('git tag -m "%s" %s' % (tag_msg, new_tag)) print info_out("Created tag: %s" % new_tag) print(" View: git show HEAD") print(" Undo: tito tag -u") print(" Push: git push origin && git push origin %s" % new_tag)
def rsync_to_remote(self, rsync_args, temp_dir, rsync_location): print("rsync %s --delete %s/ %s" % (rsync_args, temp_dir, rsync_location)) os.chdir(temp_dir) # TODO: configurable rsync options? cmd = "rsync %s --delete %s/ %s" % (rsync_args, temp_dir, rsync_location) if self.dry_run: self.print_dry_run_warning(cmd) else: output = run_command(cmd) debug(output) if not self.no_cleanup: debug("Cleaning up [%s]" % temp_dir) os.chdir("/") shutil.rmtree(temp_dir) else: print("WARNING: leaving %s (--no-cleanup)" % temp_dir)
def get_os_git_vars(): """ Determine the OpenShift version variables as dictated by the Origin shell utilities. The git tree state is spoofed. """ git_vars = {} for var in ["COMMIT", "VERSION", "MAJOR", "MINOR"]: var_name = "OS_GIT_{}".format(var) git_vars[var_name] = run_command( "bash -c 'source ./hack/lib/init.sh; os::build::os_version_vars; echo ${}'" .format(var_name)) # we hard-code this to a clean state as tito will have dirtied up the tree # but that will not have changed any of the source used for the product # release and we therefore don't want that reflected in the release version git_vars["OS_GIT_TREE_STATE"] = "clean" git_vars["OS_GIT_VERSION"] = git_vars["OS_GIT_VERSION"].replace( "-dirty", "") return git_vars
def _submit_build(self, executable, koji_opts, tag, srpm_location): """ Submit build to koji using the git URL from config. We will ignore srpm_location here. NOTE: overrides KojiReleaser._submit_build. """ cmd = "%s %s %s %s/#%s" % \ (executable, koji_opts, tag, self.releaser_config.get(self.target, 'git_url'), self.builder.build_tag) print("\nSubmitting build with: %s" % cmd) if self.dry_run: self.print_dry_run_warning(cmd) return output = run_command(cmd) print(output)
def get_latest_tagged_version(package_name): """ Return the latest git tag for this package in the current branch. Uses the info in .tito/packages/package-name. Returns None if file does not exist. """ git_root = find_git_root() rel_eng_dir = os.path.join(git_root, tito_config_dir()) file_path = "{0}/packages/{1}".format(rel_eng_dir, package_name) debug("Getting latest package info from: {0}".format(file_path)) if not os.path.exists(file_path): return None output = run_command("awk '{ print $1 ; exit }' {0}".format(file_path)) if output is None or output.strip() == "": error_out("Error looking up latest tagged version in: {0}".format( file_path)) return output
def _generate_default_changelog(self, last_tag): """ Run git-log and will generate changelog, which still can be edited by user in _make_changelog. """ patch_command = "git log" if self.config.has_option(BUILDCONFIG_SECTION, "keep_merge_commits"): keep = self.config.get(BUILDCONFIG_SECTION, "keep_merge_commits") if not keep or keep.strip().lower() not in ['1', 'true']: patch_command += " --no-merges" else: patch_command += " --no-merges" patch_command += " --pretty='format:%s' --relative %s..%s -- %s" % ( self._changelog_format(), last_tag, "HEAD", ".") output = run_command(patch_command) result = [] for line in output.split('\n'): line = line.replace('%', '%%') result.extend([self._changelog_remove_cherrypick(line)]) return '\n'.join(result)