Repository: cekit/cekit (python)

Issue:
CEkit builds log missing dependencies errors as DEBUG instead of ERROR
**Describe the bug**
CEkit builds log missing dependencies errors as `DEBUG`  instead of `ERROR`
```
2023-01-17 11:58:24,234 resource.py:589        DEBUG [Errno 2] No such file or directory: '/usr/bin/brew'
```

**To reproduce**
Install CEkit and run a build without installing required dependencies [1]

**Expected behavior**
Cekit should log errors related to missing dependencies or commands as `ERROR` instead of `DEBUG`.

**Logs**
```
[k@fedora operator]$ cekit -v build docker 
2023-01-17 11:58:24,115 cli.py:440        DEBUG Running version 4.5.0
2023-01-17 11:58:24,116 cli.py:453        DEBUG Configuring CEKit...
2023-01-17 11:58:24,116 cli.py:470        DEBUG Removing dirty directory: 'target/image/modules'
2023-01-17 11:58:24,117 cli.py:470        DEBUG Removing dirty directory: 'target/repo'
2023-01-17 11:58:24,117 tools.py:457        INFO  You are running on known platform: Fedora Linux 37 (Workstation Edition)
2023-01-17 11:58:24,117 builder.py:27         DEBUG Checking CEKit core dependencies...
2023-01-17 11:58:24,117 tools.py:513        DEBUG Checking if 'git' dependency is provided...
2023-01-17 11:58:24,117 tools.py:575        DEBUG CEKit dependency 'git' provided via the '/usr/bin/git' executable.
2023-01-17 11:58:24,117 tools.py:548        DEBUG All dependencies provided!
2023-01-17 11:58:24,118 tools.py:611        WARNING The certifi library (https://certifi.io/) was found, depending on the operating system configuration this may result in certificate validation issues
2023-01-17 11:58:24,118 tools.py:617        WARNING Certificate Authority (CA) bundle in use: '/etc/pki/tls/certs/ca-bundle.crt'
2023-01-17 11:58:24,180 builder.py:88         INFO  Generating files for docker engine
2023-01-17 11:58:24,180 base.py:102        INFO  Initializing image descriptor...
2023-01-17 11:58:24,181 builder.py:108        DEBUG Checking CEKit generate dependencies...
2023-01-17 11:58:24,181 tools.py:513        DEBUG Checking if 'odcs-client' dependency is provided...
2023-01-17 11:58:24,181 tools.py:519        DEBUG Required CEKit library 'odcs-client' was found as a 'odcs' module!
2023-01-17 11:58:24,181 tools.py:513        DEBUG Checking if 'brew' dependency is provided...
2023-01-17 11:58:24,181 tools.py:513        DEBUG Checking if 'skopeo' dependency is provided...
2023-01-17 11:58:24,181 tools.py:548        DEBUG All dependencies provided!
2023-01-17 11:58:24,182 base.py:127        DEBUG Removing old target directory
2023-01-17 11:58:24,182 tools.py:120        DEBUG Reading descriptor from 'image.yaml' file...
2023-01-17 11:58:24,192 base.py:265        DEBUG Downloading module repository: 'modules'
2023-01-17 11:58:24,193 resource.py:226        INFO  Copying resource 'modules'...
2023-01-17 11:58:24,193 resource.py:395        DEBUG Copying repository from '/home/k/work/prod/amqstreams-1-openshift-image/operator/modules' to 'target/repo/modules'.
2023-01-17 11:58:24,193 tools.py:120        DEBUG Reading descriptor from '/home/k/work/prod/amqstreams-1-openshift-image/operator/target/repo/modules/operator/module.yaml' file...
2023-01-17 11:58:24,198 base.py:284        DEBUG Adding module 'operator', path: 'target/repo/modules/operator'
2023-01-17 11:58:24,199 image.py:426        DEBUG Preparing module 'operator' required by 'amq7/amq-streams-rhel8-operator'.
2023-01-17 11:58:24,201 base.py:315        DEBUG Copying module 'operator' required by 'amq7/amq-streams-rhel8-operator'.
2023-01-17 11:58:24,201 base.py:324        DEBUG Copying module 'operator' to: 'target/image/modules/operator'
2023-01-17 11:58:24,205 docker.py:25         INFO  Handling artifacts for docker...
2023-01-17 11:58:24,205 resource.py:226        INFO  Copying resource 'cluster-operator-dist.zip'...
2023-01-17 11:58:24,206 resource.py:301        DEBUG Using md5 checksum to fetch artifacts from cacher
2023-01-17 11:58:24,206 resource.py:311        DEBUG Using cache url 'http://cache.host.com/fetch?md5=8eb508ff01f6073de982324eb8271e3b'
2023-01-17 11:58:24,206 tools.py:39         DEBUG Downloading from 'http://cache.host.com/fetch?md5=8eb508ff01f6073de982324eb8271e3b' as /tmp/cache/19d88ff8-bf8b-4617-b7db-17ff153e1fef
2023-01-17 11:58:24,233 resource.py:570        DEBUG <urlopen error [Errno -2] Name or service not known>
2023-01-17 11:58:24,233 resource.py:571        WARNING Could not download 'cluster-operator-dist.zip' artifact using cacher
2023-01-17 11:58:24,233 resource.py:578        DEBUG Trying to download artifact 'cluster-operator-dist.zip' from Brew directly
2023-01-17 11:58:24,233 tools.py:197        DEBUG Getting brew details for an artifact with '8eb508ff01f6073de982324eb8271e3b' md5 sum
2023-01-17 11:58:24,233 tools.py:207        DEBUG Executing '/usr/bin/brew call --json-output listArchives checksum=8eb508ff01f6073de982324eb8271e3b type=maven'.
2023-01-17 11:58:24,234 resource.py:589        DEBUG [Errno 2] No such file or directory: '/usr/bin/brew'
2023-01-17 11:58:24,234 resource.py:590        WARNING Could not download artifact 'cluster-operator-dist.zip' from Brew
2023-01-17 11:58:24,234 resource.py:251        WARNING Cekit is not able to fetch resource 'cluster-operator-dist.zip' automatically. Please use cekit-cache command to add this artifact manually.
2023-01-17 11:58:24,234 cli.py:500        ERROR ("Error copying resource: 'cluster-operator-dist.zip'. See logs for more info.", CekitError('Artifact cluster-operator-dist.zip could not be found'))
Traceback (most recent call last):
  File "/usr/lib/python3.11/site-packages/cekit/descriptor/resource.py", line 249, in guarded_copy
    self._copy_impl(target)
  File "/usr/lib/python3.11/site-packages/cekit/descriptor/resource.py", line 594, in _copy_impl
    raise CekitError("Artifact {} could not be found".format(self.name))
cekit.errors.CekitError: Artifact cluster-operator-dist.zip could not be found

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3.11/site-packages/cekit/cli.py", line 486, in run
    command.execute()
  File "/usr/lib/python3.11/site-packages/cekit/builder.py", line 69, in execute
    self.generate()
  File "/usr/lib/python3.11/site-packages/cekit/builder.py", line 114, in generate
    self.generator.generate()
  File "/usr/lib/python3.11/site-packages/cekit/generator/base.py", line 184, in generate
    self.prepare_artifacts()
  File "/usr/lib/python3.11/site-packages/cekit/generator/docker.py", line 30, in prepare_artifacts
    artifact.copy(target_dir)
  File "/usr/lib/python3.11/site-packages/cekit/descriptor/resource.py", line 240, in copy
    self.cache.add(self)
  File "/usr/lib/python3.11/site-packages/cekit/cache/artifact.py", line 63, in add
    artifact.guarded_copy(artifact_file)
  File "/usr/lib/python3.11/site-packages/cekit/descriptor/resource.py", line 262, in guarded_copy
    raise CekitError(
cekit.errors.CekitError: ("Error copying resource: 'cluster-operator-dist.zip'. See logs for more info.", CekitError('Artifact cluster-operator-dist.zip could not be found'))
```

**Environment information:**
 - Operating system: fedora 37
 - CEKit version: 4.5.0

**Additional context**
[1] https://docs.cekit.io/en/latest/handbook/installation/dependencies.html?highlight=brew#docker-builder-dependencies


Pull request:
Fix dependency checking
Fixes #835 

Patch:
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 594abea..42db6a1 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -12,6 +12,7 @@ jobs:
             source /etc/os-release
             sudo apt-get update
             sudo DEBIAN_FRONTEND=noninteractive apt-get install --assume-yes libkrb5-dev software-properties-common podman docker skopeo
+            sudo ln -s /usr/bin/true /usr/bin/brew
             #sudo sed -i -E 's/(unqualified-search-registries = [[])(.*)/\1"registry.fedoraproject.org", "registry.access.redhat.com", \2/g' /etc/containers/registries.conf
             echo "unqualified-search-registries = ['registry.fedoraproject.org', 'registry.access.redhat.com', 'registry.centos.org', 'docker.io']" | sudo tee -a /etc/containers/registries.conf > /dev/null
             echo 'export PATH=~/.local/bin:$PATH' >> $BASH_ENV
diff --git a/.github/workflows/cekit.yml b/.github/workflows/cekit.yml
index 3737e99..8d27aba 100644
--- a/.github/workflows/cekit.yml
+++ b/.github/workflows/cekit.yml
@@ -46,9 +46,10 @@ jobs:
         podman info
         pip install "tox<4.0.0"
         git config --global user.email "ci@dummy.com" && git config --global user.name "John Doe"
-        echo "$HOME/.local/bin" >> $GITHUB_PATH
-        mkdir -p $HOME/.local/bin
-        cd $HOME/.local/bin && curl -sL https://github.com/openshift/source-to-image/releases/download/v1.1.13/source-to-image-v1.1.13-b54d75d3-linux-amd64.tar.gz | tar xvz
+        echo "$HOME/bin" >> $GITHUB_PATH
+        mkdir $HOME/bin
+        ln -s /usr/bin/true $HOME/bin/brew
+        cd $HOME/bin && curl -sL https://github.com/openshift/source-to-image/releases/download/v1.1.13/source-to-image-v1.1.13-b54d75d3-linux-amd64.tar.gz | tar xvz
         PV=${{ matrix.python-version }}
     - name: Setup MacOS
       if: startsWith(matrix.os,'macos')
@@ -58,6 +59,10 @@ jobs:
         podman machine init
         podman machine start
         podman info
+        echo "$HOME/bin" >> $GITHUB_PATH
+        mkdir $HOME/bin
+        ln -s /usr/bin/true $HOME/bin/brew
+        ln -s /usr/bin/true $HOME/bin/skopeo
     - name: Run tests
       run: |
         PV=${{ matrix.python-version }}
diff --git a/cekit/builders/buildah.py b/cekit/builders/buildah.py
index 9da2a93..d966a1a 100644
--- a/cekit/builders/buildah.py
+++ b/cekit/builders/buildah.py
@@ -25,7 +25,7 @@ class BuildahBuilder(Builder):
     def run(self) -> None:
         """Build container image using buildah."""
         tags = self.params.tags
-        cmd = ["/usr/bin/buildah", "build-using-dockerfile"]
+        cmd = ["buildah", "build-using-dockerfile"]
 
         if not tags:
             tags = self.generator.get_tags()
diff --git a/cekit/builders/osbs.py b/cekit/builders/osbs.py
index 5af9d3c..e11b457 100644
--- a/cekit/builders/osbs.py
+++ b/cekit/builders/osbs.py
@@ -45,16 +45,16 @@ class OSBSBuilder(Builder):
 
         if CONFIG.get("common", "redhat"):
             if self.params.get("stage"):
-                self._fedpkg = "/usr/bin/rhpkg-stage"
-                self._koji = "/usr/bin/brew-stage"
+                self._fedpkg = "rhpkg-stage"
+                self._koji = "brew-stage"
                 self._koji_url = "https://brewweb.stage.engineering.redhat.com/brew"
             else:
-                self._fedpkg = "/usr/bin/rhpkg"
-                self._koji = "/usr/bin/brew"
+                self._fedpkg = "rhpkg"
+                self._koji = "brew"
                 self._koji_url = "https://brewweb.engineering.redhat.com/brew"
         else:
-            self._fedpkg = "/usr/bin/fedpkg"
-            self._koji = "/usr/bin/koji"
+            self._fedpkg = "fedpkg"
+            self._koji = "koji"
             self._koji_url = "https://koji.fedoraproject.org/koji"
 
     @staticmethod
@@ -65,15 +65,15 @@ class OSBSBuilder(Builder):
             if CONFIG.get("common", "stage"):
                 fedpkg = "rhpkg-stage"
                 koji = "brewkoji-stage"
-                koji_executable = "/usr/bin/brew-stage"
+                koji_executable = "brew-stage"
             else:
                 fedpkg = "rhpkg"
                 koji = "brewkoji"
-                koji_executable = "/usr/bin/brew"
+                koji_executable = "brew"
         else:
             fedpkg = "fedpkg"
             koji = "koji"
-            koji_executable = "/usr/bin/koji"
+            koji_executable = "koji"
 
         deps[fedpkg] = {"package": fedpkg, "executable": fedpkg}
 
diff --git a/cekit/generator/base.py b/cekit/generator/base.py
index 7fbd81e..9b98b0b 100644
--- a/cekit/generator/base.py
+++ b/cekit/generator/base.py
@@ -109,11 +109,11 @@ class Generator(object):
         }
 
         if CONFIG.get("common", "redhat"):
-            deps["brew"] = {"package": "brewkoji", "executable": "/usr/bin/brew"}
+            deps["brew"] = {"package": "brewkoji", "executable": "brew"}
             # FOLLOW_TAG requires both version and release labels ; these are generally
             # only added consistently within RH and Fedora repos so currently this annotation
             # is only supported under the RH flag.
-            deps["skopeo"] = {"package": "skopeo", "executable": "/usr/bin/skopeo"}
+            deps["skopeo"] = {"package": "skopeo", "executable": "skopeo"}
 
         return deps
 
diff --git a/cekit/tools.py b/cekit/tools.py
index 1c51904..3c3d91d 100644
--- a/cekit/tools.py
+++ b/cekit/tools.py
@@ -5,7 +5,7 @@ import shutil
 import ssl
 import subprocess
 import sys
-from typing import Any, Mapping, Optional, Sequence
+from typing import Any, Mapping, Sequence
 from urllib.parse import urlparse
 from urllib.request import urlopen
 
@@ -141,7 +141,7 @@ def decision(question: str) -> bool:
 
 def get_latest_image_version(image: str) -> str:
     inspect_cmd = [
-        "/usr/bin/skopeo",
+        "skopeo",
         "inspect",
         "--config",
         f"docker://{image}",
@@ -196,7 +196,7 @@ def get_tag_from_inspect_struct(struct: Mapping) -> str:
 def get_brew_url(md5: str) -> str:
     logger.debug("Getting brew details for an artifact with '{}' md5 sum".format(md5))
     list_archives_cmd = [
-        "/usr/bin/brew",
+        "brew",
         "call",
         "--json-output",
         "listArchives",
@@ -299,7 +299,7 @@ def copy_recursively(
     The destination directory tree will be created if it does not exist.
     """
 
-    # If the source directory does not exists, return
+    # If the source directory does not exist, return
     if not os.path.isdir(source_directory):
         return
 
@@ -335,7 +335,7 @@ def run_wrapper(
     check: bool = True,
 ) -> subprocess.CompletedProcess:
     """
-    Useful wrapper around subprocess.run
+    Useful wrapper around 'subprocess.run'.
 
     :param cmd: The command to execute
     :param capture_output: Whether to capture the output or not
@@ -355,6 +355,9 @@ def run_wrapper(
         if capture_output:
             stdout_capture = subprocess.PIPE
             stderr_capture = subprocess.PIPE
+        # subprocess.run uses Popen: https://docs.python.org/3/library/subprocess.html#subprocess.Popen
+        # which uses https://docs.python.org/3/library/os.html#os.execvpe like behaviour
+        # to locate the executable if its relative on POSIX.
         result = subprocess.run(
             cmd,
             stdout=stdout_capture,
@@ -465,7 +468,7 @@ class DependencyHandler(object):
         The dependencies provided is expected to be a dict in following format:
 
         {
-            PACKAGE_ID: { 'package': PACKAGE_NAME, 'command': COMMAND_TO_TEST_FOR_PACKACGE_EXISTENCE },
+            PACKAGE_ID: { 'package': PACKAGE_NAME, 'command': COMMAND_TO_TEST_FOR_PACKAGE_EXISTENCE },
         }
 
         Additionally, every package can contain platform specific information, for example:
@@ -558,12 +561,14 @@ class DependencyHandler(object):
 
     def _check_for_executable(
         self, dependency: str, executable: str, package: None = None
-    ) -> Optional[bool]:
-        if os.path.isabs(executable):
-            if self._is_program(executable):
-                return True
-            else:
-                return False
+    ) -> None:
+        if os.path.isabs(executable) and self._is_program(executable):
+            logger.debug(
+                "CEKit dependency '{}' provided via the explicit '{}' executable.".format(
+                    dependency, executable
+                )
+            )
+            return
 
         path = os.environ.get("PATH", os.defpath)
         path = path.split(os.pathsep)
@@ -592,7 +597,8 @@ class DependencyHandler(object):
 
         raise CekitError(msg)
 
-    def _is_program(self, path: str) -> bool:
+    @staticmethod
+    def _is_program(path: str) -> bool:
         if (
             os.path.exists(path)
             and os.access(path, os.F_OK | os.X_OK)
@@ -608,11 +614,17 @@ class DependencyHandler(object):
         try:
             import certifi
 
+            # If the certificate bundle ends up resolving e.g. /usr/local/lib/python3.7/site-packages/certifi/cacert.pem
+            # rather than /etc/pki/tls/certs/ca-bundle.crt or /etc/ssl/certs/ca-bundle.crt this can cause issues
+            # hence printing the warning. A user workaround is to export REQUESTS_CA_BUNDLE to point to the correct
+            # certificates
             logger.warning(
-                (
-                    "The certifi library (https://certifi.io/) was found, depending on the operating "
-                    + "system configuration this may result in certificate validation issues"
-                )
+                "The certifi library (https://certifi.io/) was found, depending on the operating system configuration"
+                " this may result in certificate validation issues. "
+            )
+            logger.warning(
+                "You can use REQUESTS_CA_BUNDLE environment variable to point to a different certificate bundle if "
+                "using the certifi provided bundle doesn't work."
             )
             logger.warning(
                 "Certificate Authority (CA) bundle in use: '{}'".format(certifi.where())
diff --git a/docs/conf.py b/docs/conf.py
index ab393a6..9db4f3e 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -12,7 +12,7 @@ from cekit.version import __version__ as cekit_version  # noqa: E402
 
 
 def setup(app):
-    app.add_stylesheet("css/custom.css")
+    app.add_css_file("css/custom.css")
 
 
 # Add any Sphinx extension module names here, as strings. They can be


Test patch:
diff --git a/tests/test_builder.py b/tests/test_builder.py
index 86b9972..9469caa 100644
--- a/tests/test_builder.py
+++ b/tests/test_builder.py
@@ -56,8 +56,8 @@ def test_osbs_builder_defaults(mocker):
 
     builder = create_builder_object(mocker, "osbs", {})
 
-    assert builder._fedpkg == "/usr/bin/fedpkg"
-    assert builder._koji == "/usr/bin/koji"
+    assert builder._fedpkg == "fedpkg"
+    assert builder._koji == "koji"
     assert builder._koji_url == "https://koji.fedoraproject.org/koji"
 
 
@@ -67,8 +67,8 @@ def test_osbs_builder_redhat(mocker):
 
     builder = create_builder_object(mocker, "osbs", {})
 
-    assert builder._fedpkg == "/usr/bin/rhpkg"
-    assert builder._koji == "/usr/bin/brew"
+    assert builder._fedpkg == "rhpkg"
+    assert builder._koji == "brew"
     assert builder._koji_url == "https://brewweb.engineering.redhat.com/brew"
 
 
@@ -78,8 +78,8 @@ def test_osbs_builder_use_rhpkg_stage(mocker):
 
     builder = create_builder_object(mocker, "osbs", {"stage": True})
 
-    assert builder._fedpkg == "/usr/bin/rhpkg-stage"
-    assert builder._koji == "/usr/bin/brew-stage"
+    assert builder._fedpkg == "rhpkg-stage"
+    assert builder._koji == "brew-stage"
     assert builder._koji_url == "https://brewweb.stage.engineering.redhat.com/brew"
 
 
@@ -238,7 +238,7 @@ def test_osbs_builder_run_brew_stage(mocker):
             ),
             call(
                 [
-                    "/usr/bin/brew-stage",
+                    "brew-stage",
                     "call",
                     "--python",
                     "buildContainer",
@@ -297,7 +297,7 @@ def test_osbs_builder_run_brew(mocker):
             ),
             call(
                 [
-                    "/usr/bin/brew",
+                    "brew",
                     "call",
                     "--python",
                     "buildContainer",
@@ -356,7 +356,7 @@ def test_osbs_builder_run_koji(mocker):
             ),
             call(
                 [
-                    "/usr/bin/koji",
+                    "koji",
                     "call",
                     "--python",
                     "buildContainer",
@@ -426,7 +426,7 @@ def test_osbs_builder_run_brew_user(mocker):
 
     run.assert_called_with(
         [
-            "/usr/bin/brew",
+            "brew",
             "--user",
             "Foo",
             "call",
@@ -469,7 +469,7 @@ def test_osbs_builder_run_brew_target_defined_in_descriptor(mocker):
 
     run.assert_called_with(
         [
-            "/usr/bin/brew",
+            "brew",
             "call",
             "--python",
             "buildContainer",
@@ -513,7 +513,7 @@ def test_osbs_wait_for_osbs_task_finished_successfully(mocker):
     assert builder._wait_for_osbs_task("12345") is True
 
     run.assert_called_with(
-        ["/usr/bin/brew", "call", "--json-output", "getTaskInfo", "12345"],
+        ["brew", "call", "--json-output", "getTaskInfo", "12345"],
         stderr=-1,
         stdout=-1,
         check=True,
@@ -569,14 +569,14 @@ def test_osbs_wait_for_osbs_task_in_progress(mocker):
     run.assert_has_calls(
         [
             call(
-                ["/usr/bin/brew", "call", "--json-output", "getTaskInfo", "12345"],
+                ["brew", "call", "--json-output", "getTaskInfo", "12345"],
                 stderr=-1,
                 stdout=-1,
                 check=True,
                 universal_newlines=True,
             ),
             call(
-                ["/usr/bin/brew", "call", "--json-output", "getTaskInfo", "12345"],
+                ["brew", "call", "--json-output", "getTaskInfo", "12345"],
                 stderr=-1,
                 stdout=-1,
                 check=True,
@@ -621,7 +621,7 @@ def test_osbs_wait_for_osbs_task_failed(mocker):
         builder._wait_for_osbs_task("12345")
 
     run.assert_called_with(
-        ["/usr/bin/brew", "call", "--json-output", "getTaskInfo", "12345"],
+        ["brew", "call", "--json-output", "getTaskInfo", "12345"],
         stderr=-1,
         stdout=-1,
         check=True,
@@ -895,7 +895,7 @@ def test_buildah_builder_run(mocker):
 
     run.assert_called_once_with(
         [
-            "/usr/bin/buildah",
+            "buildah",
             "build-using-dockerfile",
             "--squash",
             "-t",
@@ -919,7 +919,7 @@ def test_buildah_builder_run_platform(mocker):
 
     run.assert_called_once_with(
         [
-            "/usr/bin/buildah",
+            "buildah",
             "build-using-dockerfile",
             "--squash",
             "--platform",
@@ -945,7 +945,7 @@ def test_buildah_builder_run_pull(mocker):
 
     run.assert_called_once_with(
         [
-            "/usr/bin/buildah",
+            "buildah",
             "build-using-dockerfile",
             "--squash",
             "--pull-always",
@@ -1110,7 +1110,7 @@ def test_buildah_builder_run_with_generator(mocker):
 
     run.assert_called_once_with(
         [
-            "/usr/bin/buildah",
+            "buildah",
             "build-using-dockerfile",
             "--squash",
             "-t",
@@ -1134,7 +1134,7 @@ def test_buildah_builder_with_squashing_disabled(mocker):
 
     run.assert_called_once_with(
         [
-            "/usr/bin/buildah",
+            "buildah",
             "build-using-dockerfile",
             "-t",
             "foo",
diff --git a/tests/test_integ_builder_osbs.py b/tests/test_integ_builder_osbs.py
index 6d3e793..6601f21 100644
--- a/tests/test_integ_builder_osbs.py
+++ b/tests/test_integ_builder_osbs.py
@@ -1131,7 +1131,7 @@ def test_osbs_builder_with_koji_target_based_on_branch(tmpdir, mocker, caplog):
     run_osbs(descriptor, str(tmpdir), mocker)
 
     assert (
-        "About to execute '/usr/bin/brew call --python buildContainer --kwargs {'src': 'git://somehost.com/containers/somerepo#3b9283cb26b35511517ff5c0c3e11f490cba8feb', 'target': 'branch-containers-candidate', 'opts': {'scratch': True, 'git_branch': 'branch', 'yum_repourls': []}}'."
+        "About to execute 'brew call --python buildContainer --kwargs {'src': 'git://somehost.com/containers/somerepo#3b9283cb26b35511517ff5c0c3e11f490cba8feb', 'target': 'branch-containers-candidate', 'opts': {'scratch': True, 'git_branch': 'branch', 'yum_repourls': []}}'."
         in caplog.text
     )
 
@@ -1151,7 +1151,7 @@ def test_osbs_builder_with_koji_target_in_descriptor(tmpdir, mocker, caplog):
     run_osbs(descriptor, str(tmpdir), mocker)
 
     assert (
-        "About to execute '/usr/bin/brew call --python buildContainer --kwargs {'src': 'git://somehost.com/containers/somerepo#3b9283cb26b35511517ff5c0c3e11f490cba8feb', 'target': 'some-target', 'opts': {'scratch': True, 'git_branch': 'branch', 'yum_repourls': []}}'."
+        "About to execute 'brew call --python buildContainer --kwargs {'src': 'git://somehost.com/containers/somerepo#3b9283cb26b35511517ff5c0c3e11f490cba8feb', 'target': 'some-target', 'opts': {'scratch': True, 'git_branch': 'branch', 'yum_repourls': []}}'."
         in caplog.text
     )
 
@@ -1445,7 +1445,7 @@ ssl_verify = False
         "Ignoring http://another.domain/wrong.jar as restricted to ['https://foo.domain', 'http://another.domain/path/name']"
         in caplog.text
     )
-    assert "Executing '/usr/bin/rhpkg new-sources not_allowed_in_fetch'" in caplog.text
+    assert "Executing 'rhpkg new-sources not_allowed_in_fetch'" in caplog.text
     assert (
         "Artifact 'artifact_name' (as URL) added to fetch-artifacts-url.yaml"
         in caplog.text
@@ -2413,7 +2413,7 @@ def test_osbs_builder_with_fetch_artifacts_pnc_file_creation_1(tmpdir, mocker, c
     ) as _file:
         fetch_artifacts = yaml.safe_load(_file)
 
-    assert "Executing '['/usr/bin/rhpkg', 'new-sources', 'foo.jar']'" not in caplog.text
+    assert "Executing '['rhpkg', 'new-sources', 'foo.jar']'" not in caplog.text
     assert fetch_artifacts["builds"] == [
         {"build_id": "123456", "artifacts": [{"id": "54321", "target": "foo.jar"}]}
     ]
diff --git a/tests/test_unit_tools.py b/tests/test_unit_tools.py
index a2d5779..8de227c 100644
--- a/tests/test_unit_tools.py
+++ b/tests/test_unit_tools.py
@@ -843,6 +843,24 @@ def test_dependency_handler_check_for_executable_with_executable_only(
     )
 
 
+def test_dependency_handler_check_for_executable_with_explicit_executable_only(
+    mocker, caplog, monkeypatch
+):
+    caplog.set_level(logging.DEBUG, logger="cekit")
+
+    with mocked_dependency_handler(mocker) as handler:
+        monkeypatch.setenv("PATH", "/abc:/def")
+        mocker.patch("os.path.exists").side_effect = [True]
+        mocker.patch("os.access").return_value = True
+        mocker.patch("os.path.isdir").return_value = False
+        handler._check_for_executable("xyz", "/def/xyz-aaa")
+
+    assert (
+        "CEKit dependency 'xyz' provided via the explicit '/def/xyz-aaa' executable."
+        in caplog.text
+    )
+
+
 def test_dependency_handler_check_for_executable_with_executable_fail(
     mocker, monkeypatch
 ):