mirror of
https://github.com/arcan1s/ahriman.git
synced 2025-04-24 23:37:18 +00:00
fix case when no files were commited in remote push trigger
The issue appears together with --intent-to-add flag for adding new files. Original testing has been performed by having already added new files, thus it passed all checks. This commit also adds `commit_author` option which will allow to overwrite the author.
This commit is contained in:
parent
b2ed383de0
commit
cdd66ee780
@ -104,6 +104,7 @@ It supports authorization; to do so you'd need to prefix the url with authorizat
|
|||||||
Remote push trigger
|
Remote push trigger
|
||||||
^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
* ``commit_author`` - git commit author, string, optional. In case if not set, the git will generate author for you. Note, however, that in this case it will disclosure your hostname.
|
||||||
* ``push_url`` - url of the remote repository to which PKGBUILDs should be pushed after build process, string, required.
|
* ``push_url`` - url of the remote repository to which PKGBUILDs should be pushed after build process, string, required.
|
||||||
* ``push_branch`` - branch of the remote repository to which PKGBUILDs should be pushed after build process, string, optional, default is ``master``.
|
* ``push_branch`` - branch of the remote repository to which PKGBUILDs should be pushed after build process, string, optional, default is ``master``.
|
||||||
|
|
||||||
|
@ -161,12 +161,12 @@ class Sources(LazyLogging):
|
|||||||
str: patch as plain text
|
str: patch as plain text
|
||||||
"""
|
"""
|
||||||
instance = Sources()
|
instance = Sources()
|
||||||
instance.add(sources_dir, *pattern)
|
instance.add(sources_dir, *pattern, intent_to_add=True)
|
||||||
diff = instance.diff(sources_dir)
|
diff = instance.diff(sources_dir)
|
||||||
return f"{diff}\n" # otherwise, patch will be broken
|
return f"{diff}\n" # otherwise, patch will be broken
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def push(sources_dir: Path, remote: RemoteSource, *pattern: str) -> None:
|
def push(sources_dir: Path, remote: RemoteSource, *pattern: str, commit_author: Optional[str] = None) -> None:
|
||||||
"""
|
"""
|
||||||
commit selected changes and push files to the remote repository
|
commit selected changes and push files to the remote repository
|
||||||
|
|
||||||
@ -174,19 +174,21 @@ class Sources(LazyLogging):
|
|||||||
sources_dir(Path): local path to git repository
|
sources_dir(Path): local path to git repository
|
||||||
remote(RemoteSource): remote target, branch and url
|
remote(RemoteSource): remote target, branch and url
|
||||||
*pattern(str): glob patterns
|
*pattern(str): glob patterns
|
||||||
|
commit_author(Optional[str]): commit author in form of git config (i.e. ``user <user@host>``)
|
||||||
"""
|
"""
|
||||||
instance = Sources()
|
instance = Sources()
|
||||||
instance.add(sources_dir, *pattern)
|
instance.add(sources_dir, *pattern)
|
||||||
instance.commit(sources_dir)
|
instance.commit(sources_dir, author=commit_author)
|
||||||
Sources._check_output("git", "push", remote.git_url, remote.branch, cwd=sources_dir, logger=instance.logger)
|
Sources._check_output("git", "push", remote.git_url, remote.branch, cwd=sources_dir, logger=instance.logger)
|
||||||
|
|
||||||
def add(self, sources_dir: Path, *pattern: str) -> None:
|
def add(self, sources_dir: Path, *pattern: str, intent_to_add: bool = False) -> None:
|
||||||
"""
|
"""
|
||||||
track found files via git
|
track found files via git
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
sources_dir(Path): local path to git repository
|
sources_dir(Path): local path to git repository
|
||||||
*pattern(str): glob patterns
|
*pattern(str): glob patterns
|
||||||
|
intent_to_add(bool): record only the fact that it will be added later, acts as --intent-to-add git flag
|
||||||
"""
|
"""
|
||||||
# glob directory to find files which match the specified patterns
|
# glob directory to find files which match the specified patterns
|
||||||
found_files: List[Path] = []
|
found_files: List[Path] = []
|
||||||
@ -196,23 +198,26 @@ class Sources(LazyLogging):
|
|||||||
return # no additional files found
|
return # no additional files found
|
||||||
self.logger.info("found matching files %s", found_files)
|
self.logger.info("found matching files %s", found_files)
|
||||||
# add them to index
|
# add them to index
|
||||||
Sources._check_output("git", "add", "--intent-to-add",
|
args = ["--intent-to-add"] if intent_to_add else []
|
||||||
*[str(fn.relative_to(sources_dir)) for fn in found_files],
|
Sources._check_output("git", "add", *args, *[str(fn.relative_to(sources_dir)) for fn in found_files],
|
||||||
cwd=sources_dir, logger=self.logger)
|
cwd=sources_dir, logger=self.logger)
|
||||||
|
|
||||||
def commit(self, sources_dir: Path, commit_message: Optional[str] = None) -> None:
|
def commit(self, sources_dir: Path, message: Optional[str] = None, author: Optional[str] = None) -> None:
|
||||||
"""
|
"""
|
||||||
commit changes
|
commit changes
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
sources_dir(Path): local path to git repository
|
sources_dir(Path): local path to git repository
|
||||||
commit_message(Optional[str]): optional commit message if any. If none set, message will be generated
|
message(Optional[str]): optional commit message if any. If none set, message will be generated according to
|
||||||
according to the current timestamp
|
the current timestamp
|
||||||
|
author(Optional[str]): optional commit author if any
|
||||||
"""
|
"""
|
||||||
if commit_message is None:
|
if message is None:
|
||||||
commit_message = f"Autogenerated commit at {datetime.datetime.utcnow()}"
|
message = f"Autogenerated commit at {datetime.datetime.utcnow()}"
|
||||||
Sources._check_output("git", "commit", "--allow-empty", "--message", commit_message,
|
args = ["--allow-empty", "--message", message]
|
||||||
cwd=sources_dir, logger=self.logger)
|
if author is not None:
|
||||||
|
args.extend(["--author", author])
|
||||||
|
Sources._check_output("git", "commit", *args, cwd=sources_dir, logger=self.logger)
|
||||||
|
|
||||||
def diff(self, sources_dir: Path) -> str:
|
def diff(self, sources_dir: Path) -> str:
|
||||||
"""
|
"""
|
||||||
|
@ -38,6 +38,7 @@ class RemotePush(LazyLogging):
|
|||||||
sync PKGBUILDs to remote repository after actions
|
sync PKGBUILDs to remote repository after actions
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
|
commit_author(Optional[str]): optional commit author in form of git config (i.e. ``user <user@host>``)
|
||||||
remote_source(RemoteSource): repository remote source (remote pull url and branch)
|
remote_source(RemoteSource): repository remote source (remote pull url and branch)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -49,6 +50,7 @@ class RemotePush(LazyLogging):
|
|||||||
configuration(Configuration): configuration instance
|
configuration(Configuration): configuration instance
|
||||||
remote_push_trigger.py
|
remote_push_trigger.py
|
||||||
"""
|
"""
|
||||||
|
self.commit_author = configuration.get(section, "commit_author", fallback=None)
|
||||||
self.remote_source = RemoteSource(
|
self.remote_source = RemoteSource(
|
||||||
git_url=configuration.get(section, "push_url"),
|
git_url=configuration.get(section, "push_url"),
|
||||||
web_url="",
|
web_url="",
|
||||||
@ -73,10 +75,8 @@ class RemotePush(LazyLogging):
|
|||||||
# firstly, we need to remove old data to make sure that removed files are not tracked anymore...
|
# firstly, we need to remove old data to make sure that removed files are not tracked anymore...
|
||||||
package_target_dir = target_dir / package.base
|
package_target_dir = target_dir / package.base
|
||||||
shutil.rmtree(package_target_dir, ignore_errors=True)
|
shutil.rmtree(package_target_dir, ignore_errors=True)
|
||||||
# ...secondly, we copy whole tree...
|
# ...secondly, we clone whole tree...
|
||||||
with TemporaryDirectory(ignore_cleanup_errors=True) as dir_name, (clone_dir := Path(dir_name)):
|
Sources.fetch(package_target_dir, package.remote)
|
||||||
Sources.fetch(clone_dir, package.remote)
|
|
||||||
shutil.copytree(clone_dir, package_target_dir)
|
|
||||||
# ...and last, but not least, we remove the dot-git directory...
|
# ...and last, but not least, we remove the dot-git directory...
|
||||||
shutil.rmtree(package_target_dir / ".git", ignore_errors=True)
|
shutil.rmtree(package_target_dir / ".git", ignore_errors=True)
|
||||||
# ...and finally return path to the copied directory
|
# ...and finally return path to the copied directory
|
||||||
@ -107,7 +107,8 @@ class RemotePush(LazyLogging):
|
|||||||
try:
|
try:
|
||||||
with TemporaryDirectory(ignore_cleanup_errors=True) as dir_name, (clone_dir := Path(dir_name)):
|
with TemporaryDirectory(ignore_cleanup_errors=True) as dir_name, (clone_dir := Path(dir_name)):
|
||||||
Sources.fetch(clone_dir, self.remote_source)
|
Sources.fetch(clone_dir, self.remote_source)
|
||||||
Sources.push(clone_dir, self.remote_source, *RemotePush.packages_update(result, clone_dir))
|
Sources.push(clone_dir, self.remote_source, *RemotePush.packages_update(result, clone_dir),
|
||||||
|
commit_author=self.commit_author)
|
||||||
except Exception:
|
except Exception:
|
||||||
self.logger.exception("git push failed")
|
self.logger.exception("git push failed")
|
||||||
raise GitRemoteError()
|
raise GitRemoteError()
|
||||||
|
@ -193,7 +193,7 @@ def test_patch_create(mocker: MockerFixture) -> None:
|
|||||||
diff_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.diff")
|
diff_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.diff")
|
||||||
|
|
||||||
Sources.patch_create(Path("local"), "glob")
|
Sources.patch_create(Path("local"), "glob")
|
||||||
add_mock.assert_called_once_with(Path("local"), "glob")
|
add_mock.assert_called_once_with(Path("local"), "glob", intent_to_add=True)
|
||||||
diff_mock.assert_called_once_with(Path("local"))
|
diff_mock.assert_called_once_with(Path("local"))
|
||||||
|
|
||||||
|
|
||||||
@ -214,10 +214,11 @@ def test_push(package_ahriman: Package, mocker: MockerFixture) -> None:
|
|||||||
commit_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.commit")
|
commit_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.commit")
|
||||||
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.Sources._check_output")
|
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.Sources._check_output")
|
||||||
|
|
||||||
|
author = "commit author <user@host>"
|
||||||
local = Path("local")
|
local = Path("local")
|
||||||
Sources.push(Path("local"), package_ahriman.remote, "glob")
|
Sources.push(Path("local"), package_ahriman.remote, "glob", commit_author=author)
|
||||||
add_mock.assert_called_once_with(local, "glob")
|
add_mock.assert_called_once_with(local, "glob")
|
||||||
commit_mock.assert_called_once_with(local)
|
commit_mock.assert_called_once_with(local, author=author)
|
||||||
check_output_mock.assert_called_once_with(
|
check_output_mock.assert_called_once_with(
|
||||||
"git", "push", package_ahriman.remote.git_url, package_ahriman.remote.branch,
|
"git", "push", package_ahriman.remote.git_url, package_ahriman.remote.branch,
|
||||||
cwd=local, logger=pytest.helpers.anyvar(int))
|
cwd=local, logger=pytest.helpers.anyvar(int))
|
||||||
@ -233,6 +234,21 @@ def test_add(sources: Sources, mocker: MockerFixture) -> None:
|
|||||||
local = Path("local")
|
local = Path("local")
|
||||||
sources.add(local, "pattern1", "pattern2")
|
sources.add(local, "pattern1", "pattern2")
|
||||||
glob_mock.assert_has_calls([MockCall("pattern1"), MockCall("pattern2")])
|
glob_mock.assert_has_calls([MockCall("pattern1"), MockCall("pattern2")])
|
||||||
|
check_output_mock.assert_called_once_with(
|
||||||
|
"git", "add", "1", "2", "1", "2", cwd=local, logger=pytest.helpers.anyvar(int)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_add_intent_to_add(sources: Sources, mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must add files to git with --intent-to-add flag
|
||||||
|
"""
|
||||||
|
glob_mock = mocker.patch("pathlib.Path.glob", return_value=[Path("local/1"), Path("local/2")])
|
||||||
|
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.Sources._check_output")
|
||||||
|
|
||||||
|
local = Path("local")
|
||||||
|
sources.add(local, "pattern1", "pattern2", intent_to_add=True)
|
||||||
|
glob_mock.assert_has_calls([MockCall("pattern1"), MockCall("pattern2")])
|
||||||
check_output_mock.assert_called_once_with(
|
check_output_mock.assert_called_once_with(
|
||||||
"git", "add", "--intent-to-add", "1", "2", "1", "2", cwd=local, logger=pytest.helpers.anyvar(int)
|
"git", "add", "--intent-to-add", "1", "2", "1", "2", cwd=local, logger=pytest.helpers.anyvar(int)
|
||||||
)
|
)
|
||||||
@ -256,14 +272,30 @@ def test_commit(sources: Sources, mocker: MockerFixture) -> None:
|
|||||||
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.Sources._check_output")
|
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.Sources._check_output")
|
||||||
|
|
||||||
local = Path("local")
|
local = Path("local")
|
||||||
commit_message = "Commit message"
|
message = "Commit message"
|
||||||
sources.commit(local, commit_message=commit_message)
|
sources.commit(local, message=message)
|
||||||
check_output_mock.assert_called_once_with(
|
check_output_mock.assert_called_once_with(
|
||||||
"git", "commit", "--allow-empty", "--message", commit_message, cwd=local, logger=pytest.helpers.anyvar(int)
|
"git", "commit", "--allow-empty", "--message", message, cwd=local, logger=pytest.helpers.anyvar(int)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_commit_autogenerated(sources: Sources, mocker: MockerFixture) -> None:
|
def test_commit_author(sources: Sources, mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must commit changes with commit author
|
||||||
|
"""
|
||||||
|
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.Sources._check_output")
|
||||||
|
|
||||||
|
local = Path("local")
|
||||||
|
message = "Commit message"
|
||||||
|
author = "commit author <user@host>"
|
||||||
|
sources.commit(Path("local"), message=message, author=author)
|
||||||
|
check_output_mock.assert_called_once_with(
|
||||||
|
"git", "commit", "--allow-empty", "--message", message, "--author", author,
|
||||||
|
cwd=local, logger=pytest.helpers.anyvar(int)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_commit_autogenerated_message(sources: Sources, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must commit changes with autogenerated commit message
|
must commit changes with autogenerated commit message
|
||||||
"""
|
"""
|
||||||
|
@ -17,17 +17,14 @@ def test_package_update(package_ahriman: Package, mocker: MockerFixture) -> None
|
|||||||
"""
|
"""
|
||||||
rmtree_mock = mocker.patch("shutil.rmtree")
|
rmtree_mock = mocker.patch("shutil.rmtree")
|
||||||
fetch_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.fetch")
|
fetch_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.fetch")
|
||||||
copytree_mock = mocker.patch("shutil.copytree")
|
|
||||||
|
|
||||||
local = Path("local")
|
local = Path("local")
|
||||||
RemotePush.package_update(package_ahriman, local)
|
RemotePush.package_update(package_ahriman, local)
|
||||||
rmtree_mock.assert_has_calls([
|
rmtree_mock.assert_has_calls([
|
||||||
MockCall(local / package_ahriman.base, ignore_errors=True),
|
MockCall(local / package_ahriman.base, ignore_errors=True),
|
||||||
MockCall(pytest.helpers.anyvar(int), onerror=pytest.helpers.anyvar(int)), # removal of the TemporaryDirectory
|
|
||||||
MockCall(local / package_ahriman.base / ".git", ignore_errors=True),
|
MockCall(local / package_ahriman.base / ".git", ignore_errors=True),
|
||||||
])
|
])
|
||||||
fetch_mock.assert_called_once_with(pytest.helpers.anyvar(int), package_ahriman.remote)
|
fetch_mock.assert_called_once_with(pytest.helpers.anyvar(int), package_ahriman.remote)
|
||||||
copytree_mock.assert_called_once_with(pytest.helpers.anyvar(int), local / package_ahriman.base)
|
|
||||||
|
|
||||||
|
|
||||||
def test_packages_update(result: Result, package_ahriman: Package, mocker: MockerFixture) -> None:
|
def test_packages_update(result: Result, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||||
@ -53,7 +50,9 @@ def test_run(configuration: Configuration, result: Result, package_ahriman: Pack
|
|||||||
|
|
||||||
runner.run(result)
|
runner.run(result)
|
||||||
fetch_mock.assert_called_once_with(pytest.helpers.anyvar(int), runner.remote_source)
|
fetch_mock.assert_called_once_with(pytest.helpers.anyvar(int), runner.remote_source)
|
||||||
push_mock.assert_called_once_with(pytest.helpers.anyvar(int), runner.remote_source, package_ahriman.base)
|
push_mock.assert_called_once_with(
|
||||||
|
pytest.helpers.anyvar(int), runner.remote_source, package_ahriman.base, commit_author=runner.commit_author
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_run_failed(configuration: Configuration, result: Result, mocker: MockerFixture) -> None:
|
def test_run_failed(configuration: Configuration, result: Result, mocker: MockerFixture) -> None:
|
||||||
|
@ -40,6 +40,7 @@ target = gitremote
|
|||||||
target = gitremote
|
target = gitremote
|
||||||
|
|
||||||
[gitremote]
|
[gitremote]
|
||||||
|
commit_author = "user <user@host>"
|
||||||
push_url = https://github.com/arcan1s/repository.git
|
push_url = https://github.com/arcan1s/repository.git
|
||||||
pull_url = https://github.com/arcan1s/repository.git
|
pull_url = https://github.com/arcan1s/repository.git
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user