def test_basic_local_update_direct_push_no_dg_spec( cwd_upstream, api_instance, distgit_and_remote, mock_remote_functionality_upstream): u, d, api = api_instance d.joinpath("beer.spec").unlink() subprocess.check_call( ["git", "commit", "-m", "remove spec", "-a"], cwd=str(d), ) _, distgit_remote = distgit_and_remote mock_spec_download_remote_s(d) api.sync_release(dist_git_branch="master", version="0.1.0", create_pr=False) remote_dir_clone = Path(f"{distgit_remote}-clone") subprocess.check_call( ["git", "clone", distgit_remote, str(remote_dir_clone)], cwd=str(remote_dir_clone.parent), ) spec = Specfile(remote_dir_clone / "beer.spec") assert spec.get_version() == "0.1.0" assert (remote_dir_clone / "README.packit").is_file()
def test_local_update_generated_spec(cwd_upstream, api_instance, mock_remote_functionality_upstream): """Check that specfile can be generated on clone.""" u, d, api = api_instance mock_spec_download_remote_s(d) flexmock(api).should_receive("init_kerberos_ticket").at_least().once() # Simulate generation by moving the spec to a different location current_spec_location = u / "beer.spec" new_spec_location = u / "tmp.spec" shutil.move(current_spec_location, new_spec_location) api.up.package_config.actions = { ActionName.post_upstream_clone: [f"mv {new_spec_location} {current_spec_location}"] } api.sync_release(dist_git_branch="main") assert (d / TARBALL_NAME).is_file() spec = Specfile(d / "beer.spec") assert spec.get_version() == "0.1.0" assert (d / "README.packit").is_file() # assert that we have changelog entries for both versions changelog = "\n".join(spec.spec_content.section("%changelog")) assert "0.0.0" in changelog assert "0.1.0" in changelog
def test_basic_local_update_empty_patch( distgit_and_remote, mock_remote_functionality_sourcegit, api_instance_source_git, ref, ): """ propose-update for sourcegit test: mock remote API, use local upstream and dist-git """ distgit, _ = distgit_and_remote mock_spec_download_remote_s(distgit) api_instance_source_git.sync_release( dist_git_branch="main", version="0.1.0", upstream_ref=ref, ) assert (distgit / TARBALL_NAME).is_file() spec = Specfile(distgit / "beer.spec") assert spec.get_version() == "0.1.0" spec_package_section = "" for section in spec.spec_content.sections: if "%package" in section[0]: spec_package_section += "\n".join(section[1]) assert not spec.patches["applied"] assert not spec.patches["not_applied"]
def test_basic_local_update_copy_upstream_release_description( cwd_upstream, api_instance, mock_remote_functionality_upstream): """basic propose-update test: mock remote API, use local upstream and dist-git, set copy_upstream_release_description in package config to True""" u, d, api = api_instance mock_spec_download_remote_s(d) flexmock(api).should_receive("init_kerberos_ticket").at_least().once() release = flexmock(body="Some description of the upstream release") api.up.local_project.git_project = flexmock( get_release=lambda name: release) api.package_config.copy_upstream_release_description = True api.sync_release(dist_git_branch="master", version="0.1.0") assert (d / TARBALL_NAME).is_file() spec = Specfile(d / "beer.spec") assert spec.get_version() == "0.1.0" assert (d / "README.packit").is_file() # assert that we have changelog entries for both versions changelog = "\n".join(spec.spec_content.section("%changelog")) assert ("""- 0.1.0-1 Some description of the upstream release """ in changelog) assert "0.0.0" in changelog assert "0.1.0" in changelog
def test_basic_local_update_using_distgit(cwd_upstream, api_instance, mock_remote_functionality_upstream): """ basic propose-update test: mock remote API, use local upstream and dist-git """ u, d, api = api_instance mock_spec_download_remote_s(d) api.sync_release("master", "0.1.0") assert (d / TARBALL_NAME).is_file() spec = Specfile(d / "beer.spec") assert spec.get_version() == "0.1.0" package_section = spec.spec_content.section("%package") assert package_section[2] == "# some change" assert package_section[4] == "Name: beer" assert package_section[5] == "Version: 0.1.0" assert package_section[6] == "Release: 1%{?dist}" assert package_section[7] == "Summary: A tool to make you happy" assert (d / "README.packit").is_file() # assert that we have changelog entries for both versions changelog = "\n".join(spec.spec_content.section("%changelog")) assert "0.0.0" in changelog assert "0.1.0" in changelog
def test_basic_local_update_from_downstream( cwd_upstream, api_instance, mock_remote_functionality_upstream): flexmock(LocalProject, _parse_namespace_from_git_url=lambda: None) u, d, api = api_instance api.sync_from_downstream("master", "master", True) new_upstream = api.up.local_project.working_dir assert (new_upstream / "beer.spec").is_file() spec = Specfile(new_upstream / "beer.spec") assert spec.get_version() == "0.0.0"
def test_create_srcgit_requre_populated(api_instance_source_git, tmp_path: Path): """ use requre to create a source-git out of it in a branch with upstream git history - this should only layer downstream changes on top """ # clone dist-git pkg = "python-requre" dist_git_ref = "6b27ffacda06289ca2d546e15b3c96845243005f" dist_git_path = tmp_path.joinpath(pkg) source_git_path = tmp_path.joinpath("requre-sg") FedPKG().clone(pkg, str(dist_git_path), anonymous=True) dg_lp = LocalProject(working_dir=dist_git_path) # check out specific ref subprocess.check_call(["git", "reset", "--hard", dist_git_ref], cwd=dist_git_path) # add a patch in there spec = Specfile(dist_git_path / f"{pkg}.spec", sources_dir=dist_git_path) patch_name = "hello.patch" patch_path = dist_git_path.joinpath(patch_name) patch_path.write_text(REQURE_PATCH) patch = PatchMetadata(name=patch_name, path=patch_path, present_in_specfile=False) spec.add_patch(patch) dg_lp.stage() dg_lp.commit("add the hello patch") subprocess.check_call(["fedpkg", "prep"], cwd=dist_git_path) # create src-git source_git_path.mkdir() subprocess.check_call([ "git", "clone", "https://github.com/packit/requre", str(source_git_path) ]) subprocess.check_call( ["git", "checkout", "-B", "source-git-0.4.0", "0.4.0"], cwd=source_git_path) sgg = SourceGitGenerator( LocalProject(working_dir=source_git_path), api_instance_source_git.config, dist_git_path=dist_git_path, ) sgg.create_from_upstream() # verify it subprocess.check_call(["packit", "srpm"], cwd=source_git_path) srpm_path = list( source_git_path.glob("python-requre-0.4.0-2.*.src.rpm"))[0] assert srpm_path.is_file()
def test_basic_local_update_using_distgit(cwd_upstream, api_instance, mock_remote_functionality_upstream): """ basic propose-update test: mock remote API, use local upstream and dist-git """ u, d, api = api_instance api.sync_release("master", "0.1.0") assert (d / TARBALL_NAME).is_file() spec = Specfile(str(d / "beer.spec")) assert spec.get_version() == "0.1.0" assert (d / "README.packit").is_file() # assert that we have changelog entries for both versions changelog = "\n".join(spec.spec_content.section("%changelog")) assert "0.0.0" in changelog assert "0.1.0" in changelog
def test_ensure_pnum(tmp_path, input_spec): spec = Path(copy(input_spec, tmp_path)) Specfile(spec).ensure_pnum() text = spec.read_text() assert "%autosetup -p1" in text # %autosetup does not accept -q assert "-q" not in text
def test_download_remote_sources(source, package_config, expected_url, tmp_path: Path): specfile_content = ("Name: rsync\n" "Version: 3.1.3\n" "Release: 1\n" f"Source0: {source}\n" "License: GPLv3+\n" "Summary: rsync\n" "%description\nrsync\n") spec_path = tmp_path / "rsync.spec" spec_path.write_text(specfile_content) specfile = Specfile(spec_path, sources_dir=tmp_path) base_git = PackitRepositoryBase(config=flexmock(), package_config=package_config) flexmock(base_git).should_receive("specfile").and_return(specfile) expected_path = tmp_path / "rsync-3.1.3.tar.gz" # sadly we can't mock os.path.is_file, b/c the function is defined in posixpath.py # and flexmock is not able to mock that def mocked_download_file(url, destination_path, blocksize=8192): assert url == expected_url Path(destination_path).write_text("1") flexmock(DownloadHelper, download_file=mocked_download_file) base_git.download_remote_sources() assert expected_path.exists()
def dist_git_specfile(self) -> Specfile: if not self._dist_git_specfile: path = str(Path(self.dist_git.working_dir, f"{self.pkg_name}.spec")) self._dist_git_specfile = Specfile( path, sources_dir=self.dist_git.working_dir) return self._dist_git_specfile
def test_basic_local_update(cwd_upstream, api_instance, mock_remote_functionality_upstream): """ basic propose-update test: mock remote API, use local upstream and dist-git """ u, d, api = api_instance mock_spec_download_remote_s(d) flexmock(api).should_receive("init_kerberos_ticket").at_least().once() api.sync_release("master", "0.1.0") assert (d / TARBALL_NAME).is_file() spec = Specfile(d / "beer.spec") assert spec.get_version() == "0.1.0" assert (d / "README.packit").is_file() # assert that we have changelog entries for both versions changelog = "\n".join(spec.spec_content.section("%changelog")) assert "0.0.0" in changelog assert "0.1.0" in changelog
def test_set_spec_content(tmp_path): distgit_spec_contents = ( "Name: bring-me-to-the-life\n" "Version: 1.0\n" "Release: 1\n" "Source0: foo.bar\n" "License: GPLv3+\n" "Summary: evanescence\n" "%description\n-\n\n" "%changelog\n" "* Mon Mar 04 2019 Foo Bor <*****@*****.**> - 1.0-1\n" "- Initial package.\n") distgit_spec_path = tmp_path / "life.spec" distgit_spec_path.write_text(distgit_spec_contents) upstream_spec_contents = ( "Name: bring-me-to-the-life\n" "Version: 1.0\n" "Release: 1\n" "Source0: foo.bor\n" "License: MIT\n" "Summary: evanescence, I was brought to life\n" "%description\n-\n" "%changelog\n" "* Mon Mar 04 2019 Foo Bor <*****@*****.**> - 1.0-1\n" "- Initial package.\n") upstream_spec_path = tmp_path / "e-life.spec" upstream_spec_path.write_text(upstream_spec_contents) upstream_specfile = Specfile(upstream_spec_path, sources_dir=tmp_path) dist_git = PackitRepositoryBase(config=flexmock(), package_config=flexmock()) dist_git._specfile_path = distgit_spec_path dist_git.set_specfile_content(upstream_specfile, None, None) assert [ "* Mon Mar 04 2019 Foo Bor <*****@*****.**> - 1.0-1", "- Initial package.", ] == dist_git.specfile.spec_content.section("%changelog") assert "1.0" == dist_git.specfile.get_version() assert "License: MIT" in dist_git.specfile.spec_content.section("%package") assert ("Summary: evanescence, I was brought to life" in dist_git.specfile.spec_content.section("%package")) new_log = [ "* Wed Jun 02 2021 John Fou <*****@*****.**> - 1.1-1", "- 1.1 upstream release", ] flexmock(SpecFile).should_receive("get_new_log").with_args( "1.1 upstream release").and_return(new_log) dist_git.set_specfile_content(upstream_specfile, "1.1", "1.1 upstream release") assert new_log + [ "* Mon Mar 04 2019 Foo Bor <*****@*****.**> - 1.0-1", "- Initial package.", ] == dist_git.specfile.spec_content.section("%changelog") assert "1.1" == dist_git.specfile.get_version()
def test_basic_local_update_without_patching( sourcegit_and_remote, distgit_and_remote, mock_patching, mock_remote_functionality_sourcegit, api_instance_source_git, ): """ propose-update for sourcegit test: mock remote API, use local upstream and dist-git """ sourcegit, _ = sourcegit_and_remote distgit, _ = distgit_and_remote mock_spec_download_remote_s(distgit) api_instance_source_git.sync_release("master", "0.1.0", upstream_ref="0.1.0") assert (distgit / TARBALL_NAME).is_file() spec = Specfile(distgit / "beer.spec") assert spec.get_version() == "0.1.0"
def test_basic_local_update_direct_push( cwd_upstream, api_instance, distgit_and_remote, mock_remote_functionality_upstream ): """ basic propose-update test: mock remote API, use local upstream and dist-git """ u, d, api = api_instance _, distgit_remote = distgit_and_remote api.sync_release("master", "0.1.0", create_pr=False) remote_dir_clone = Path(f"{distgit_remote}-clone") subprocess.check_call( ["git", "clone", distgit_remote, str(remote_dir_clone)], cwd=str(remote_dir_clone.parent), ) spec = Specfile(str(remote_dir_clone / "beer.spec")) assert spec.get_version() == "0.1.0" assert (remote_dir_clone / "README.packit").is_file()
def create_from_upstream(self): """Create a source-git repo, by transforming downstream patches in Git commits applied on top of the selected upstream ref. """ upstream_ref_sha = self.source_git.git.rev_list( "-1", self.upstream_ref) if upstream_ref_sha != self.source_git.head.commit.hexsha: raise PackitException( f"{self.upstream_ref!r} is not pointing to the current HEAD " f"in {self.source_git.working_dir!r}.") if not self.dist_git_specfile.uses_autosetup: if not self.ignore_missing_autosetup: raise PackitException( "Initializing source-git repos for packages " "not using %autosetup is not allowed by default. " "You can use --ignore-missing-autosetup option to enforce " "running the command without %autosetup.") logger.warning( "Source-git repos for packages not using %autosetup may be not initialized" "properly or may not work with other packit commands.") self._populate_distro_dir() self._reset_gitignore() self._configure_syncing() spec = Specfile( f"{self.distro_dir}/{self.pkg_name}.spec", sources_dir=self.dist_git.working_dir, ) patch_comments = spec.read_patch_comments() spec.remove_patches() self.source_git.git.stage(DISTRO_DIR, force=True) self.source_git.git.commit( message="Initialize as a source-git repository") pkg_tool = PkgTool( fas_username=self.config.fas_user, directory=self.dist_git.working_dir, tool=self.pkg_tool or self.config.pkg_tool, ) pkg_tool.sources() self._run_prep() self._rebase_patches(patch_comments)
def dist_git_spec(self): if self._dist_git_spec: return self._dist_git_spec if not self.dist_git_path: raise RuntimeError("dist_git_path not defined") self._dist_git_spec = Specfile( self.dist_git_path / self.relative_specfile_path, sources_dir=self.dist_git_path / "SOURCES/", ) return self._dist_git_spec
def get_latest_released_version(self) -> str: """ Return version of the upstream project for the latest official release :return: the version string (e.g. "1.0.0") """ version = Specfile.get_upstream_version( versioneer=None, package_name=self.package_config.downstream_package_name, category=None, ) logger.info(f"Version in upstream registries is {version!r}.") return version
def test_write_spec_content(): with open(SPECFILE) as spec: content = spec.read() data = "new line 1\n" spec = Specfile(SPECFILE) spec.spec_content.replace_section("%description", [data]) spec.write_spec_content() with open(SPECFILE) as specfile: assert "new line 1" in specfile.read() spec.spec_content = SpecContent(content) spec.write_spec_content()
def test_download_remote_sources_via_spec(self): """ Use case: package_config.sources and Source0 are out of sync, make sure packit downloads correct archive specifiec in the spec file """ # we should use an actual git.centos.org url but we'd store the tarball in our history # which we don't want I'd say # "https://git.centos.org/sources/rsync/c8s/82e7829c0b3cefbd33c233005341e2073c425629" git_centos_org_url = "https://example.org/" package_config = PackageConfig( specfile_path="rsync.spec", sources=[ SourcesItem( path="rsync-3.1.2.tar.gz", url=git_centos_org_url, ), ], jobs=[], ) # same drill here, let's not store tarballs in our git-history # source = "https://download.samba.org/pub/rsync/src/rsync-3.1.3.tar.gz" source = "https://httpbin.org/anything/rsync-3.1.3.tar.gz" base_git = PackitRepositoryBase( config=flexmock(), package_config=package_config ) specfile_content = ( "Name: rsync\n" "Version: 3.1.3\n" "Release: 1\n" f"Source0: {source}\n" "License: GPLv3+\n" "Summary: rsync\n" "%description\nrsync\n" ) tmp = Path(self.static_tmp) spec_path = tmp / "rsync.spec" spec_path.write_text(specfile_content) specfile = Specfile(spec_path, sources_dir=tmp) flexmock(base_git).should_receive("specfile").and_return(specfile) flexmock(Path).should_receive("is_file").and_return(False) base_git.download_remote_sources() expected_path = tmp / "rsync-3.1.3.tar.gz" assert Path(expected_path).exists()
def test_basic_local_update_patch_content( sourcegit_and_remote, distgit_and_remote, mock_remote_functionality_sourcegit, api_instance_source_git, ): """ propose-update for sourcegit test: mock remote API, use local upstream and dist-git """ sourcegit, _ = sourcegit_and_remote distgit, _ = distgit_and_remote mock_spec_download_remote_s(distgit) source_file = sourcegit / "big-source-file.txt" source_file.write_text("new changes") git_add_and_commit(directory=sourcegit, message="source change") source_file = sourcegit / "ignored_file.txt" source_file.write_text(" And I am sad.") git_add_and_commit(directory=sourcegit, message="make a file sad") api_instance_source_git.sync_release("master", "0.1.0", upstream_ref="0.1.0") spec = Specfile(str(distgit / "beer.spec")) spec_package_section = "" for section in spec.spec_content.sections: if "%package" in section[0]: spec_package_section += "\n".join(section[1]) assert "Patch0001: 0001" in spec_package_section assert "Patch0002: 0002" not in spec_package_section # no empty patches git_diff = subprocess.check_output(["git", "diff", "HEAD~", "HEAD"], cwd=distgit).decode() assert "-Version: 0.0.0\n+Version: 0.1.0" in git_diff assert "+# PATCHES FROM SOURCE GIT:" in git_diff assert ( " - 0.1.0-1\n" "+- new upstream release: 0.1.0\n" "+\n" " * Sun Feb 24 2019 Tomas Tomecek <*****@*****.**> - 0.0.0-1\n" " - No brewing, yet.\n" in git_diff) # direct diff in the synced file assert ("diff --git a/.packit.yaml b/.packit.yaml\n" "new file mode 100644" in git_diff) assert "--- /dev/null\n" "+++ b/.packit.yaml" in git_diff # diff of the synced file should not be in the patch assert ("+diff --git a/.packit.yaml b/.packit.yaml\n" "+new file mode 100644\n" not in git_diff) # diff of the source file (not synced) has to be in the patch assert ("patch\n" "@@ -0,0 +1,9 @@\n" "+diff --git a/big-source-file.txt b/big-source-file.txt\n" in git_diff) assert ("+--- a/big-source-file.txt\n" "++++ b/big-source-file.txt\n" "+@@ -1,2 +1 @@\n" "+-This is a testing file\n" "+-containing some text.\n" "++new changes\n" in git_diff) # diff of the source files (not synced) should not be directly in the git diff assert ("--- a/big-source-file.txt\n" "+++ b/big-source-file.txt\n" "@@ -1,2 +1 @@\n" "-This is a testing file\n" "-containing some text.\n" "+new changes\n" not in git_diff) # ignored file should not be in the diff assert "--- a/ignored_file.txt\n" not in git_diff
def test_ensure_pnum(tmp_path, input_spec): spec = Path(copy(input_spec, tmp_path)) Specfile(spec).ensure_pnum() assert "%autosetup -p1" in spec.read_text()
def test_set_spec_has_autochangelog(spec_content, has_autochangelog, tmp_path): spec_path = tmp_path / "life.spec" spec_path.write_text(spec_content) specfile = Specfile(spec_path, sources_dir=tmp_path) assert specfile.has_autochangelog() == has_autochangelog
def test_basic_local_update_patch_content( sourcegit_and_remote, distgit_and_remote, mock_remote_functionality_sourcegit, api_instance_source_git, ): """ propose-update for sourcegit test: mock remote API, use local upstream and dist-git """ sourcegit, _ = sourcegit_and_remote distgit, _ = distgit_and_remote mock_spec_download_remote_s(distgit) source_file = sourcegit / "big-source-file.txt" source_file.write_text("new changes") git_add_and_commit(directory=sourcegit, message="source change") source_file = sourcegit / "ignored_file.txt" source_file.write_text(" And I am sad.") git_add_and_commit(directory=sourcegit, message="make a file sad") api_instance_source_git.sync_release("master", "0.1.0", upstream_ref="0.1.0") git_diff = subprocess.check_output(["git", "diff", "HEAD~", "HEAD"], cwd=distgit).decode() assert (""" -Version: 0.0.0 +Version: 0.1.0""" in git_diff) assert "+# PATCHES FROM SOURCE GIT:" in git_diff spec_package_section = "" for section in Specfile(distgit / "beer.spec").spec_content.sections: if "%package" in section[0]: spec_package_section += "\n".join(section[1]) assert "Patch0001: 0001-source-change.patch" in spec_package_section assert "Patch0002:" not in spec_package_section # no empty patches assert (""" %prep -%autosetup -n %{upstream_name}-%{version} +%autosetup -p1 -n %{upstream_name}-%{version}""" in git_diff) assert (""" - 0.1.0-1 +- new upstream release: 0.1.0 + * Sun Feb 24 2019 Tomas Tomecek <*****@*****.**> - 0.0.0-1 - No brewing, yet.""" in git_diff) # direct diff in the synced file assert ("""diff --git a/.packit.yaml b/.packit.yaml new file mode 100644""" in git_diff) assert (""" --- /dev/null +++ b/.packit.yaml""" in git_diff) # diff of the synced file should not be in the patch assert (""" +diff --git a/.packit.yaml b/.packit.yaml +new file mode 100644""" not in git_diff) # diff of the source file (not synced) has to be in the patch assert (""" +Subject: [PATCH] source change + +--- + big-source-file.txt | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/big-source-file.txt b/big-source-file.txt""" in git_diff) assert (""" +--- a/big-source-file.txt ++++ b/big-source-file.txt +@@ -1,2 +1 @@ +-This is a testing file +-containing some text. ++new changes""" in git_diff) # diff of the source files (not synced) should not be directly in the git diff assert (""" --- a/big-source-file.txt +++ b/big-source-file.txt @@ -1,2 +1 @@ -This is a testing file -containing some text. +new changes""" not in git_diff) # ignored file should not be in the diff assert "--- a/ignored_file.txt\n" not in git_diff
def specfile(self) -> Specfile: if self._specfile is None: self._specfile = Specfile(self.absolute_specfile_path, self.absolute_specfile_dir) return self._specfile