Skip to content

Commit

Permalink
Allow filtering tags via regex configuration options
Browse files Browse the repository at this point in the history
Introduce 2 configuration options, upstream_tag_include and
upstream_tag_exclude to allow filtering the tags. This will be used
in the service to allow filtering the propose_downstream runs.

Fixes packit/packit-service#2118
  • Loading branch information
lbarcziova committed Aug 7, 2023
1 parent 81828af commit 1b2f0b1
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 48 deletions.
4 changes: 4 additions & 0 deletions packit/config/common_package_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@ def __init__(
image_customizations: Optional[Dict] = None,
copr_chroot: Optional[str] = None,
follow_fedora_branching: bool = False,
upstream_tag_include: str = "",
upstream_tag_exclude: str = "",
):
self.config_file_path: Optional[str] = config_file_path
self.specfile_path: Optional[str] = specfile_path
Expand Down Expand Up @@ -238,6 +240,8 @@ def __init__(
self.issue_repository = issue_repository
self.release_suffix = release_suffix
self.update_release = update_release
self.upstream_tag_include = upstream_tag_include
self.upstream_tag_exclude = upstream_tag_exclude

# from deprecated JobMetadataConfig
self._targets: Dict[str, Dict[str, Any]]
Expand Down
2 changes: 2 additions & 0 deletions packit/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,8 @@ class CommonConfigSchema(Schema):
issue_repository = fields.String(missing=None)
release_suffix = fields.String(missing=None)
update_release = fields.Bool(default=True)
upstream_tag_include = fields.String()
upstream_tag_exclude = fields.String()

# Former 'metadata' keys
_targets = TargetsListOrDict(missing=None, data_key="targets")
Expand Down
117 changes: 85 additions & 32 deletions packit/upstream.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
from packit.utils.versions import compare_versions
from packit.sync import iter_srcs


logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -305,41 +304,95 @@ def create_archive(
) -> str:
return Archive(self, version).create(create_symlink=create_symlink)

def get_last_tag(self, before: str = None) -> Optional[str]:
"""
Get last git-tag from the repo.
:param before: get last tag before the given tag
def list_tags(self) -> List[str]:
"""
List tags in the repository sorted by created date from the most recent.
logger.debug(
f"We're about to `git-describe` the upstream repository "
f"{self.local_project.working_dir}."
)

Returns:
List of tags.
"""
try:
version_tag_glob = self.package_config.upstream_tag_template.replace(
"{version}", "?*"
)
cmd = [
"git",
"describe",
"--tags",
"--abbrev=0",
f"--match={version_tag_glob}",
"tag",
"--list",
"--sort=-creatordate",
]
if before:
cmd += [f"{before}^"]
last_tag = run_command(
tags = run_command(
cmd,
output=True,
cwd=self.local_project.working_dir,
).stdout.strip()
).stdout.split()
except PackitCommandFailedError as ex:
logger.debug(f"{ex!r}")
logger.info("Can't describe this repository, are there any git tags?")
# no tags in the git repo
logger.info("Can't list the tags in this repository.")
return []

return tags

def get_last_tag(
self, matching_only: bool = False, before: str = None
) -> Optional[str]:
"""
Get last git-tag (matching the configuration) from the repo.
Args:
matching_only: whether to consider the `upstream_tag_include` and
`upstream_tag_exclude` regexes to filter out matching tags.
before: get the last tag before this tag
Returns:
Last (matching) tag.
"""

logger.debug(
f"We're about to get latest {'matching' if matching_only else ''} tag in "
f"the upstream repository {self.local_project.working_dir}."
)

tags = self.list_tags()

if not tags:
logger.info("No tags found in the repository.")
return None
return last_tag

if before:
if before not in tags:
logger.debug(f"{before} not present in the obtained list of tags.")
return None
index = tags.index(before)
tags = tags[(index + 1) :] # noqa

matching_tags = self.filter_tags(tags) if matching_only else tags
return matching_tags[0] if matching_tags else None

def filter_tags(self, tags: List[str]):
"""
Filter the given tags using `upstream_tag_include` and
`upstream_tag_exclude` if they are present.
Args:
tags: list of tags that should be filtered
Returns:
Tags that match with `upstream_tag_include` and
`upstream_tag_exclude`.
"""
if self.package_config.upstream_tag_include:
include_pattern = re.compile(self.package_config.upstream_tag_include)
tags = [tag for tag in tags if include_pattern.match(tag)]

if self.package_config.upstream_tag_exclude:
exclude_pattern = re.compile(self.package_config.upstream_tag_exclude)
tags = [tag for tag in tags if not exclude_pattern.match(tag)]

if not tags:
logger.info(
f"No tags matching include pattern {self.package_config.upstream_tag_include}"
f"and exclude pattern {self.package_config.upstream_tag_exclude} found. "
)
logger.debug(f"Matching tags: {tags}")
return tags

def get_commit_messages(
self, after: Optional[str] = None, before: str = "HEAD"
Expand Down Expand Up @@ -858,7 +911,7 @@ def __init__(
self.srpm_path = srpm_path
self.__ref = ref

self._current_git_describe_version = None
self._current_version = None
self._upstream_ref = None

if self.upstream.running_in_service():
Expand All @@ -871,10 +924,10 @@ def __init__(
self.rpmbuild_dir = self.upstream.absolute_specfile_dir

@property
def current_git_describe_version(self):
if self._current_git_describe_version is None:
self._current_git_describe_version = self.upstream.get_current_version()
return self._current_git_describe_version
def current_version(self):
if self._current_version is None:
self._current_version = self.upstream.get_current_version()
return self._current_version

@property
def upstream_ref(self):
Expand Down Expand Up @@ -1018,7 +1071,7 @@ def _fix_specfile_to_use_local_archive(
# * PACKIT_RPMSPEC - data for the project's specfile assembled by us
# - RPMSPEC is more descriptive than just SPEC
env = {
"PACKIT_PROJECT_VERSION": self.current_git_describe_version,
"PACKIT_PROJECT_VERSION": self.current_version,
# Spec file %release field which packit sets by default
"PACKIT_RPMSPEC_RELEASE": self.upstream.get_spec_release(release_suffix),
"PACKIT_PROJECT_COMMIT": current_commit,
Expand Down Expand Up @@ -1048,7 +1101,7 @@ def _fix_specfile_to_use_local_archive(
if self.upstream.with_action(action=ActionName.fix_spec, env=env):
self.upstream.fix_spec(
archive=archive,
version=self.current_git_describe_version,
version=self.current_version,
commit=current_commit,
update_release=update_release,
release=new_release,
Expand All @@ -1065,7 +1118,7 @@ def prepare(
self._prepare_upstream_using_source_git(update_release, release_suffix)
else:
created_archive = self.upstream.create_archive(
version=self.current_git_describe_version,
version=self.current_version,
create_symlink=create_symlinks,
)
self._fix_specfile_to_use_local_archive(
Expand Down
9 changes: 8 additions & 1 deletion packit/utils/changelog_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,20 @@ def update_dist_git(self, full_version: str, upstream_tag: str) -> None:
upstream_tag: The commit messages after last tag and before this tag are used
to update the changelog in the spec-file.
"""
# could we do the changelog diff based on the last version in spec file?
# previous_version = self.dg.specfile.expanded_version
# previous_tag = self.package_config.upstream_tag_template.format(
# version=previous_version
# )

comment = self.get_entry_from_action(version=full_version) or (
self.up.local_project.git_project.get_release(
tag_name=upstream_tag, name=full_version
).body
if self.package_config.copy_upstream_release_description
else self.up.get_commit_messages(
after=self.up.get_last_tag(upstream_tag), before=upstream_tag
after=self.up.get_last_tag(matching_only=True, before=upstream_tag),
before=upstream_tag,
)
)
comment = self.sanitize_entry(comment)
Expand Down
96 changes: 81 additions & 15 deletions tests/integration/test_upstream.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,24 @@
"""
import os
import re
import sys
import shutil
import subprocess
import sys
from datetime import datetime, timedelta
from pathlib import Path

import pytest
from flexmock import flexmock
from ogr import GithubService

import packit
from ogr import GithubService
from packit.actions import ActionName
from packit.config import Config, get_local_package_config
from packit.exceptions import PackitSRPMException
from packit.local_project import LocalProjectBuilder
from packit.upstream import Archive, Upstream, SRPMBuilder
from packit.utils.commands import cwd
from packit.utils.repo import create_new_repo

from tests.spellbook import (
EMPTY_CHANGELOG,
UPSTREAM_MACRO_IN_SOURCE,
Expand Down Expand Up @@ -426,28 +426,94 @@ def test_github_app(upstream_instance, tmp_path):
)


def create_git_tag(u, tag_name, time_to_set):
timestamp = time_to_set.strftime("%Y-%m-%dT%H:%M:%S")
# Set the environment variable to ensure the tag's timestamp
env = {"GIT_COMMITTER_DATE": timestamp}

Path(u, "tags").write_text(tag_name)
subprocess.check_call(["git", "add", "tags"], cwd=u, env=env)
subprocess.check_call(
["git", "commit", "-m", f"Tag with {tag_name}"], cwd=u, env=env
)
subprocess.check_call(
["git", "tag", "-a", "-m", f"Tag with {tag_name}", tag_name], cwd=u, env=env
)


@pytest.mark.parametrize(
"tags, upstream_tag_template, last_tag",
"tags, before, last_tag",
[
(["0.2.0"], None, "0.2.0"),
(["muse/0.3.0", "0.2.0"], "muse/{version}", "muse/0.3.0"),
(["0.2.0", "0.3.0"], None, "0.3.0"),
(["0.2.0", "0.3.0"], "0.3.0", "0.2.0"),
(["0.2.0", "0.3.0", "0.4.0"], "0.4.0", "0.3.0"),
],
)
def test_get_last_tag(upstream_instance, tags, upstream_tag_template, last_tag):
# u is the local path of the upstream repository
def test_get_last_tag(upstream_instance, tags, before, last_tag):
u, ups = upstream_instance
now_time = datetime.now()

# Tag more commits in the history
for tag in tags:
Path(u, "tags").write_text(tag)
subprocess.check_call(["git", "add", "tags"], cwd=u)
subprocess.check_call(["git", "commit", "-m", f"Tag with {tag}"], cwd=u)
subprocess.check_call(["git", "tag", "-a", "-m", f"Tag with {tag}", tag], cwd=u)
for i, tag in enumerate(tags):
create_git_tag(u, tag, now_time + timedelta(minutes=i + 1))

assert ups.get_last_tag(before=before) == last_tag


@pytest.mark.parametrize(
"tags, upstream_tag_include, upstream_tag_exclude, before, last_tag",
[
(["2.0.0", "3.0.0"], r"^2\..+", None, None, "2.0.0"),
(["2.0.0", "2.1.0", "2.1.1", "2.2.0"], r"^.+\.1\..+", None, None, "2.1.1"),
(
[
"2.0.0",
"2.1.0",
"2.2.0",
"2.1.1",
],
None,
r"^.+\.1\..+",
None,
"2.2.0",
),
(
["2.0.0", "2.1.0", "2.2.0", "2.1.1", "2.2.1"],
r"^.+\.1\..+",
None,
"2.1.1",
"2.1.0",
),
(
["2.0.0", "2.1.0", "2.2.0", "2.1.1", "3.0.0"],
r"^2\..+",
r"^.+\.1\..+",
None,
"2.2.0",
),
],
)
def test_get_last_tag_matching_only(
upstream_instance,
tags,
upstream_tag_include,
upstream_tag_exclude,
before,
last_tag,
):
u, ups = upstream_instance

if upstream_tag_template:
ups.package_config.upstream_tag_template = upstream_tag_template
ups.package_config.upstream_tag_include = upstream_tag_include
ups.package_config.upstream_tag_exclude = upstream_tag_exclude

now_time = datetime.now()

# Tag more commits in the history
for i, tag in enumerate(tags):
create_git_tag(u, tag, now_time + timedelta(minutes=i + 1))

assert ups.get_last_tag() == last_tag
assert ups.get_last_tag(before=before, matching_only=True) == last_tag


@pytest.mark.parametrize(
Expand Down

0 comments on commit 1b2f0b1

Please sign in to comment.