mirror of
https://github.com/containers/podman-compose.git
synced 2025-04-23 17:09:23 +02:00
Merge pull request #1171 from mokibit/fix-git-build-url-context
Fix using git URL as build context
This commit is contained in:
commit
c46ecb226b
1
newsfragments/127-fix-git-build-url-context.bugfix
Normal file
1
newsfragments/127-fix-git-build-url-context.bugfix
Normal file
@ -0,0 +1 @@
|
|||||||
|
- Fix using git URL as build context
|
@ -26,6 +26,7 @@ import signal
|
|||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import urllib.parse
|
||||||
from asyncio import Task
|
from asyncio import Task
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
@ -1663,7 +1664,9 @@ def normalize_service_final(service: dict, project_dir: str) -> dict:
|
|||||||
if "build" in service:
|
if "build" in service:
|
||||||
build = service["build"]
|
build = service["build"]
|
||||||
context = build if isinstance(build, str) else build.get("context", ".")
|
context = build if isinstance(build, str) else build.get("context", ".")
|
||||||
context = os.path.normpath(os.path.join(project_dir, context))
|
|
||||||
|
if not is_path_git_url(context):
|
||||||
|
context = os.path.normpath(os.path.join(project_dir, context))
|
||||||
if not isinstance(service["build"], dict):
|
if not isinstance(service["build"], dict):
|
||||||
service["build"] = {}
|
service["build"] = {}
|
||||||
service["build"]["context"] = context
|
service["build"]["context"] = context
|
||||||
@ -2501,12 +2504,17 @@ async def compose_push(compose, args):
|
|||||||
await compose.podman.run([], "push", [cnt["image"]])
|
await compose.podman.run([], "push", [cnt["image"]])
|
||||||
|
|
||||||
|
|
||||||
|
def is_path_git_url(path):
|
||||||
|
r = urllib.parse.urlparse(path)
|
||||||
|
return r.scheme == 'git' or r.path.endswith('.git')
|
||||||
|
|
||||||
|
|
||||||
def container_to_build_args(compose, cnt, args, path_exists, cleanup_callbacks=None):
|
def container_to_build_args(compose, cnt, args, path_exists, cleanup_callbacks=None):
|
||||||
build_desc = cnt["build"]
|
build_desc = cnt["build"]
|
||||||
if not hasattr(build_desc, "items"):
|
if not hasattr(build_desc, "items"):
|
||||||
build_desc = {"context": build_desc}
|
build_desc = {"context": build_desc}
|
||||||
ctx = build_desc.get("context", ".")
|
ctx = build_desc.get("context", ".")
|
||||||
dockerfile = build_desc.get("dockerfile")
|
dockerfile = build_desc.get("dockerfile", "")
|
||||||
dockerfile_inline = build_desc.get("dockerfile_inline")
|
dockerfile_inline = build_desc.get("dockerfile_inline")
|
||||||
if dockerfile_inline is not None:
|
if dockerfile_inline is not None:
|
||||||
dockerfile_inline = str(dockerfile_inline)
|
dockerfile_inline = str(dockerfile_inline)
|
||||||
@ -2524,7 +2532,10 @@ def container_to_build_args(compose, cnt, args, path_exists, cleanup_callbacks=N
|
|||||||
|
|
||||||
if cleanup_callbacks is not None:
|
if cleanup_callbacks is not None:
|
||||||
list.append(cleanup_callbacks, cleanup_temp_dockfile)
|
list.append(cleanup_callbacks, cleanup_temp_dockfile)
|
||||||
else:
|
|
||||||
|
build_args = []
|
||||||
|
|
||||||
|
if not is_path_git_url(ctx):
|
||||||
if dockerfile:
|
if dockerfile:
|
||||||
dockerfile = os.path.join(ctx, dockerfile)
|
dockerfile = os.path.join(ctx, dockerfile)
|
||||||
else:
|
else:
|
||||||
@ -2540,9 +2551,16 @@ def container_to_build_args(compose, cnt, args, path_exists, cleanup_callbacks=N
|
|||||||
dockerfile = os.path.join(ctx, dockerfile)
|
dockerfile = os.path.join(ctx, dockerfile)
|
||||||
if path_exists(dockerfile):
|
if path_exists(dockerfile):
|
||||||
break
|
break
|
||||||
if not path_exists(dockerfile):
|
|
||||||
raise OSError("Dockerfile not found in " + ctx)
|
if path_exists(dockerfile):
|
||||||
build_args = ["-f", dockerfile, "-t", cnt["image"]]
|
# normalize dockerfile path, as the user could have provided unpredictable file formats
|
||||||
|
dockerfile = os.path.normpath(os.path.join(ctx, dockerfile))
|
||||||
|
build_args.extend(["-f", dockerfile])
|
||||||
|
else:
|
||||||
|
raise OSError(f"Dockerfile not found in {ctx}")
|
||||||
|
|
||||||
|
build_args.extend(["-t", cnt["image"]])
|
||||||
|
|
||||||
if "platform" in cnt:
|
if "platform" in cnt:
|
||||||
build_args.extend(["--platform", cnt["platform"]])
|
build_args.extend(["--platform", cnt["platform"]])
|
||||||
for secret in build_desc.get("secrets", []):
|
for secret in build_desc.get("secrets", []):
|
||||||
|
1
tests/integration/build/git_url_context/__init__.py
Normal file
1
tests/integration/build/git_url_context/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
|
@ -0,0 +1,9 @@
|
|||||||
|
version: "3"
|
||||||
|
services:
|
||||||
|
test_context:
|
||||||
|
build:
|
||||||
|
context: https://github.com/mokibit/test-git-url-as-context.git
|
||||||
|
image: test-git-url-as-context
|
||||||
|
test_context_inline:
|
||||||
|
build: https://github.com/mokibit/test-git-url-as-context.git
|
||||||
|
image: test-git-url-as-context-inline
|
@ -0,0 +1,55 @@
|
|||||||
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
import os
|
||||||
|
from parameterized import parameterized
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from tests.integration.test_utils import podman_compose_path
|
||||||
|
from tests.integration.test_utils import test_path
|
||||||
|
from tests.integration.test_utils import RunSubprocessMixin
|
||||||
|
|
||||||
|
|
||||||
|
def compose_yaml_path():
|
||||||
|
""" "Returns the path to the compose file used for this test module"""
|
||||||
|
base_path = os.path.join(test_path(), "build/git_url_context")
|
||||||
|
return os.path.join(base_path, "docker-compose.yml")
|
||||||
|
|
||||||
|
|
||||||
|
class TestComposeBuildGitUrlAsContext(unittest.TestCase, RunSubprocessMixin):
|
||||||
|
@parameterized.expand([
|
||||||
|
("git_url_context_test_context_1", "data_1.txt", b'test1\r\n'),
|
||||||
|
("git_url_context_test_context_1", "data_2.txt", b'test2\r\n'),
|
||||||
|
("git_url_context_test_context_inline_1", "data_1.txt", b'test1\r\n'),
|
||||||
|
("git_url_context_test_context_inline_1", "data_2.txt", b'test2\r\n'),
|
||||||
|
])
|
||||||
|
def test_build_git_url_as_context(self, container_name, file_name, output):
|
||||||
|
# test if container can access specific files from git repository when git url is used as
|
||||||
|
# a build context
|
||||||
|
try:
|
||||||
|
out, _ = self.run_subprocess_assert_returncode([
|
||||||
|
podman_compose_path(),
|
||||||
|
"-f",
|
||||||
|
compose_yaml_path(),
|
||||||
|
"up",
|
||||||
|
"-d",
|
||||||
|
])
|
||||||
|
|
||||||
|
out, _ = self.run_subprocess_assert_returncode([
|
||||||
|
"podman",
|
||||||
|
"exec",
|
||||||
|
"-ti",
|
||||||
|
f"{container_name}",
|
||||||
|
"sh",
|
||||||
|
"-c",
|
||||||
|
f"cat {file_name}",
|
||||||
|
])
|
||||||
|
self.assertEqual(out, output)
|
||||||
|
finally:
|
||||||
|
out, _ = self.run_subprocess_assert_returncode([
|
||||||
|
podman_compose_path(),
|
||||||
|
"-f",
|
||||||
|
compose_yaml_path(),
|
||||||
|
"down",
|
||||||
|
"-t",
|
||||||
|
"0",
|
||||||
|
])
|
@ -46,7 +46,7 @@ class TestContainerToBuildArgs(unittest.TestCase):
|
|||||||
args,
|
args,
|
||||||
[
|
[
|
||||||
'-f',
|
'-f',
|
||||||
'./Containerfile',
|
'Containerfile',
|
||||||
'-t',
|
'-t',
|
||||||
'new-image',
|
'new-image',
|
||||||
'--no-cache',
|
'--no-cache',
|
||||||
@ -67,7 +67,7 @@ class TestContainerToBuildArgs(unittest.TestCase):
|
|||||||
args,
|
args,
|
||||||
[
|
[
|
||||||
'-f',
|
'-f',
|
||||||
'./Containerfile',
|
'Containerfile',
|
||||||
'-t',
|
'-t',
|
||||||
'new-image',
|
'new-image',
|
||||||
'--platform',
|
'--platform',
|
||||||
@ -90,7 +90,7 @@ class TestContainerToBuildArgs(unittest.TestCase):
|
|||||||
args,
|
args,
|
||||||
[
|
[
|
||||||
'-f',
|
'-f',
|
||||||
'./Containerfile',
|
'Containerfile',
|
||||||
'-t',
|
'-t',
|
||||||
'new-image',
|
'new-image',
|
||||||
'-t',
|
'-t',
|
||||||
@ -115,7 +115,7 @@ class TestContainerToBuildArgs(unittest.TestCase):
|
|||||||
args,
|
args,
|
||||||
[
|
[
|
||||||
'-f',
|
'-f',
|
||||||
'./Containerfile',
|
'Containerfile',
|
||||||
'-t',
|
'-t',
|
||||||
'new-image',
|
'new-image',
|
||||||
'--label',
|
'--label',
|
||||||
@ -141,7 +141,7 @@ class TestContainerToBuildArgs(unittest.TestCase):
|
|||||||
args,
|
args,
|
||||||
[
|
[
|
||||||
'-f',
|
'-f',
|
||||||
'./Containerfile',
|
'Containerfile',
|
||||||
'-t',
|
'-t',
|
||||||
'new-image',
|
'new-image',
|
||||||
'--no-cache',
|
'--no-cache',
|
||||||
@ -180,3 +180,42 @@ class TestContainerToBuildArgs(unittest.TestCase):
|
|||||||
for c in cleanup_callbacks:
|
for c in cleanup_callbacks:
|
||||||
c()
|
c()
|
||||||
self.assertFalse(os.path.exists(temp_dockerfile))
|
self.assertFalse(os.path.exists(temp_dockerfile))
|
||||||
|
|
||||||
|
def test_context_git_url(self):
|
||||||
|
c = create_compose_mock()
|
||||||
|
|
||||||
|
cnt = get_minimal_container()
|
||||||
|
cnt['build']['context'] = "https://github.com/test_repo.git"
|
||||||
|
args = get_minimal_args()
|
||||||
|
|
||||||
|
args = container_to_build_args(c, cnt, args, lambda path: False)
|
||||||
|
self.assertEqual(
|
||||||
|
args,
|
||||||
|
[
|
||||||
|
'-t',
|
||||||
|
'new-image',
|
||||||
|
'--no-cache',
|
||||||
|
'--pull-always',
|
||||||
|
'https://github.com/test_repo.git',
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_context_invalid_git_url_git_is_not_prefix(self):
|
||||||
|
c = create_compose_mock()
|
||||||
|
|
||||||
|
cnt = get_minimal_container()
|
||||||
|
cnt['build']['context'] = "not_prefix://github.com/test_repo"
|
||||||
|
args = get_minimal_args()
|
||||||
|
|
||||||
|
with self.assertRaises(OSError):
|
||||||
|
container_to_build_args(c, cnt, args, lambda path: False)
|
||||||
|
|
||||||
|
def test_context_invalid_git_url_git_is_not_suffix(self):
|
||||||
|
c = create_compose_mock()
|
||||||
|
|
||||||
|
cnt = get_minimal_container()
|
||||||
|
cnt['build']['context'] = "https://github.com/test_repo.git/not_suffix"
|
||||||
|
args = get_minimal_args()
|
||||||
|
|
||||||
|
with self.assertRaises(OSError):
|
||||||
|
container_to_build_args(c, cnt, args, lambda path: False)
|
||||||
|
22
tests/unit/test_is_path_git_url.py
Normal file
22
tests/unit/test_is_path_git_url.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from parameterized import parameterized
|
||||||
|
|
||||||
|
from podman_compose import is_path_git_url
|
||||||
|
|
||||||
|
|
||||||
|
class TestIsPathGitUrl(unittest.TestCase):
|
||||||
|
@parameterized.expand([
|
||||||
|
("prefix_git", "git://host.xz/path/to/repo", True),
|
||||||
|
("prefix_almost_git", "gitt://host.xz/path/to/repo", False),
|
||||||
|
("prefix_wrong", "http://host.xz/path/to/repo", False),
|
||||||
|
("suffix_git", "http://host.xz/path/to/repo.git", True),
|
||||||
|
("suffix_wrong", "http://host.xz/path/to/repo", False),
|
||||||
|
("suffix_with_url_fragment", "http://host.xz/path/to/repo.git#fragment", True),
|
||||||
|
("suffix_and_prefix", "git://host.xz/path/to/repo.git", True),
|
||||||
|
("empty_url_path", "http://#fragment", False),
|
||||||
|
])
|
||||||
|
def test_is_path_git_url(self, test_name, path, result):
|
||||||
|
self.assertEqual(is_path_git_url(path), result)
|
Loading…
Reference in New Issue
Block a user