Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: use "craft.git" for git where possible #925

Merged
merged 4 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 21 additions & 11 deletions craft_parts/sources/git_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@
import subprocess
import sys
from pathlib import Path
from typing import Any, ClassVar, Literal, cast
from typing import Any, Literal, cast

import pydantic
from overrides import overrides
from typing_extensions import Self

from craft_parts.utils.git import get_git_command

from . import errors
from .base import (
BaseSourceModel,
Expand Down Expand Up @@ -81,13 +83,14 @@ class GitSource(SourceHandler):
"""

source_model = GitSourceModel
command: ClassVar[str] = "git"
tigarmo marked this conversation as resolved.
Show resolved Hide resolved

@classmethod
def version(cls) -> str:
"""Get git version information."""
return subprocess.check_output(
["git", "version"], universal_newlines=True, stderr=subprocess.DEVNULL
[get_git_command(), "version"],
universal_newlines=True,
stderr=subprocess.DEVNULL,
).strip()

@classmethod
Expand Down Expand Up @@ -118,7 +121,7 @@ def generate_version(cls, *, part_src_dir: Path | None = None) -> str:
try:
output = (
subprocess.check_output(
["git", "-C", str(part_src_dir), "describe", "--dirty"],
[get_git_command(), "-C", str(part_src_dir), "describe", "--dirty"],
stderr=subprocess.DEVNULL,
)
.decode(encoding)
Expand All @@ -128,7 +131,14 @@ def generate_version(cls, *, part_src_dir: Path | None = None) -> str:
# If we fall into this exception it is because the repo is not
# tagged at all.
proc = subprocess.Popen( # pylint: disable=consider-using-with
["git", "-C", str(part_src_dir), "describe", "--dirty", "--always"],
[
get_git_command(),
"-C",
str(part_src_dir),
"describe",
"--dirty",
"--always",
],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
Expand Down Expand Up @@ -166,7 +176,7 @@ def __init__( # pylint: disable=too-many-arguments

def _fetch_origin_commit(self) -> None:
"""Fetch from origin, using source-commit if defined."""
command = [self.command, "-C", str(self.part_src_dir), "fetch", "origin"]
command = [get_git_command(), "-C", str(self.part_src_dir), "fetch", "origin"]
if self.source_commit:
command.append(self.source_commit)

Expand All @@ -175,7 +185,7 @@ def _fetch_origin_commit(self) -> None:
def _get_current_branch(self) -> str:
"""Get current git branch."""
command = [
self.command,
get_git_command(),
"-C",
str(self.part_src_dir),
"branch",
Expand Down Expand Up @@ -206,7 +216,7 @@ def _pull_existing(self) -> None:
else:
refspec = "refs/remotes/origin/" + self._get_current_branch()

command_prefix = [self.command, "-C", str(self.part_src_dir)]
command_prefix = [get_git_command(), "-C", str(self.part_src_dir)]
command = [*command_prefix, "fetch", "--prune"]

if self.source_submodules is None or len(self.source_submodules) > 0:
Expand All @@ -226,7 +236,7 @@ def _pull_existing(self) -> None:

def _clone_new(self) -> None:
"""Clone a git repository, using submodules, branch, and depth if defined."""
command = [self.command, "clone"]
command = [get_git_command(), "clone"]
if self.source_submodules is None:
command.append("--recursive")
else:
Expand All @@ -249,7 +259,7 @@ def _clone_new(self) -> None:
if self.source_commit:
self._fetch_origin_commit()
command = [
self.command,
get_git_command(),
"-C",
str(self.part_src_dir),
"checkout",
Expand Down Expand Up @@ -296,7 +306,7 @@ def _get_source_details(self) -> dict[str, str | None]:

if not tag and not branch and not commit:
commit = self._run_output(
["git", "-C", str(self.part_src_dir), "rev-parse", "HEAD"]
[get_git_command(), "-C", str(self.part_src_dir), "rev-parse", "HEAD"]
)

return {
Expand Down
32 changes: 32 additions & 0 deletions craft_parts/utils/git.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
#
# Copyright 2024 Canonical Ltd.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License version 3 as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

"""Helpers to use git."""
import shutil
from functools import cache


@cache
def get_git_command() -> str:
tigarmo marked this conversation as resolved.
Show resolved Hide resolved
"""Get the git command to use.

This function will prefer the "craft.git" binary if available on the system,
returning its full path. Otherwise, it will always return "git", without
checking for availability.
tigarmo marked this conversation as resolved.
Show resolved Hide resolved
"""
if craft_git := shutil.which("craft.git"):
return craft_git
return "git"
2 changes: 2 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ X.Y.Z (2024-MM-DD)
- Set JAVA_HOME environment variable in Java-based plugins. The plugin will
try to detect the latest available JDK.
- Add an API for :ref:`registering custom source types <how_to_add_a_source_handler>`.
- Prefer "craft.git" as the binary to handle git sources, in environments where
it is available.

2.1.4 (2024-12-04)
------------------
Expand Down
38 changes: 38 additions & 0 deletions tests/unit/utils/test_git.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
#
# Copyright 2024 Canonical Ltd.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License version 3 as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import shutil

import pytest
from craft_parts.utils.git import get_git_command


@pytest.mark.parametrize(
("which_result", "expected_command"),
[
(None, "git"),
("/usr/bin/craft.git", "/usr/bin/craft.git"),
(
"/snap/snapcraft/current/libexec/snapcraft/craft.git",
"/snap/snapcraft/current/libexec/snapcraft/craft.git",
),
],
)
def test_get_git_command(which_result, expected_command, mocker):
mocker.patch.object(shutil, "which", return_value=which_result)
get_git_command.cache_clear()

git_command = get_git_command()
assert git_command == expected_command
Loading