Fix project root path resolution for build Dockerfiles
This commit is contained in:
@@ -167,36 +167,53 @@ def parse_dockerfile_for_image(dockerfile_path):
|
|||||||
if not os.path.exists(dockerfile_path):
|
if not os.path.exists(dockerfile_path):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
image_name = None
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
arg_defaults = {}
|
||||||
|
last_from = None
|
||||||
with open(dockerfile_path) as df:
|
with open(dockerfile_path) as df:
|
||||||
for line in df:
|
for line in df:
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
# Prefer LABEL with image if present
|
if not line or line.startswith("#"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if line.upper().startswith("ARG "):
|
||||||
|
arg_body = line[4:].strip()
|
||||||
|
if "=" in arg_body:
|
||||||
|
key, value = arg_body.split("=", 1)
|
||||||
|
arg_defaults[key.strip()] = value.strip()
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Prefer LABEL with image if present.
|
||||||
if "LABEL" in line and "image=" in line:
|
if "LABEL" in line and "image=" in line:
|
||||||
match = re.search(r'image=["\']?([^"\']+)["\']?', line)
|
match = re.search(r'image=["\']?([^"\']+)["\']?', line)
|
||||||
if match:
|
if match:
|
||||||
image_name = match.group(1)
|
image_name = normalize_image_name(substitute_dockerfile_args(match.group(1), arg_defaults))
|
||||||
logger.debug(f"Found LABEL image={image_name} in {dockerfile_path}")
|
logger.debug(f"Found LABEL image={image_name} in {dockerfile_path}")
|
||||||
return image_name
|
return image_name
|
||||||
|
|
||||||
# If no LABEL, use the last FROM line as fallback
|
|
||||||
df.seek(0)
|
|
||||||
last_from = None
|
|
||||||
for line in df:
|
|
||||||
line = line.strip()
|
|
||||||
if line.upper().startswith("FROM "):
|
if line.upper().startswith("FROM "):
|
||||||
parts = line.split()
|
from_clause = line[5:].strip()
|
||||||
if len(parts) >= 2:
|
if from_clause.startswith("--"):
|
||||||
last_from = parts[1]
|
split_clause = from_clause.split(None, 1)
|
||||||
if last_from:
|
if len(split_clause) < 2:
|
||||||
logger.debug(f"Found base FROM {last_from} in {dockerfile_path}")
|
continue
|
||||||
return last_from
|
from_clause = split_clause[1]
|
||||||
|
|
||||||
|
parts = from_clause.split()
|
||||||
|
if not parts:
|
||||||
|
continue
|
||||||
|
|
||||||
|
candidate = substitute_dockerfile_args(parts[0], arg_defaults)
|
||||||
|
if candidate and candidate.lower() != "scratch":
|
||||||
|
last_from = normalize_image_name(candidate)
|
||||||
|
|
||||||
|
if last_from:
|
||||||
|
logger.debug(f"Found base FROM {last_from} in {dockerfile_path}")
|
||||||
|
return last_from
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug(f"Error reading Dockerfile {dockerfile_path}: {e}")
|
logger.debug(f"Error reading Dockerfile {dockerfile_path}: {e}")
|
||||||
|
|
||||||
return image_name
|
return None
|
||||||
|
|
||||||
def normalize_image_name(image_name):
|
def normalize_image_name(image_name):
|
||||||
if not image_name:
|
if not image_name:
|
||||||
@@ -207,17 +224,54 @@ def normalize_image_name(image_name):
|
|||||||
return image_name
|
return image_name
|
||||||
return f"{image_name}:latest"
|
return f"{image_name}:latest"
|
||||||
|
|
||||||
|
def is_compose_build_placeholder(image_name, project_name):
|
||||||
|
if not image_name:
|
||||||
|
return False
|
||||||
|
candidate = str(image_name)
|
||||||
|
project_prefix = f"{project_name}-"
|
||||||
|
if candidate.startswith(project_prefix):
|
||||||
|
return True
|
||||||
|
# Keep backward-compatible behavior for historical default project prefix.
|
||||||
|
return candidate.startswith("core-")
|
||||||
|
|
||||||
|
def substitute_dockerfile_args(value, arg_defaults):
|
||||||
|
if not value:
|
||||||
|
return value
|
||||||
|
|
||||||
|
pattern = re.compile(r"\$\{([^}]+)\}|\$([A-Za-z_][A-Za-z0-9_]*)")
|
||||||
|
|
||||||
|
def replacer(match):
|
||||||
|
expr = match.group(1)
|
||||||
|
simple = match.group(2)
|
||||||
|
if simple:
|
||||||
|
return arg_defaults.get(simple, "")
|
||||||
|
|
||||||
|
if ":-" in expr:
|
||||||
|
var_name, default_value = expr.split(":-", 1)
|
||||||
|
return arg_defaults.get(var_name, default_value)
|
||||||
|
if "-" in expr:
|
||||||
|
var_name, default_value = expr.split("-", 1)
|
||||||
|
return arg_defaults.get(var_name, default_value)
|
||||||
|
return arg_defaults.get(expr, "")
|
||||||
|
|
||||||
|
return pattern.sub(replacer, value)
|
||||||
|
|
||||||
def expand_compose_path(path_value, project_root):
|
def expand_compose_path(path_value, project_root):
|
||||||
raw = str(path_value)
|
raw = str(path_value)
|
||||||
raw = raw.replace("${PROJECT_ROOT}", project_root).replace("$PROJECT_ROOT", project_root)
|
raw = raw.replace("${PROJECT_ROOT}", project_root).replace("$PROJECT_ROOT", project_root)
|
||||||
return os.path.expandvars(raw)
|
return os.path.expandvars(raw)
|
||||||
|
|
||||||
|
def get_project_root_from_script(script_path):
|
||||||
|
if not script_path:
|
||||||
|
return os.getcwd()
|
||||||
|
return os.path.dirname(os.path.abspath(script_path))
|
||||||
|
|
||||||
# --- Compose parsing ---
|
# --- Compose parsing ---
|
||||||
def get_compose_files_from_script(script_path):
|
def get_compose_files_from_script(script_path):
|
||||||
files = []
|
files = []
|
||||||
if not os.path.exists(script_path):
|
if not os.path.exists(script_path):
|
||||||
return files
|
return files
|
||||||
base_dir = os.path.dirname(script_path)
|
base_dir = get_project_root_from_script(script_path)
|
||||||
try:
|
try:
|
||||||
with open(script_path) as f:
|
with open(script_path) as f:
|
||||||
content = f.read()
|
content = f.read()
|
||||||
@@ -302,7 +356,11 @@ def parse_compose_services(compose_files, project_name, project_root):
|
|||||||
from_dockerfile = normalize_image_name(parse_dockerfile_for_image(dockerfile_path))
|
from_dockerfile = normalize_image_name(parse_dockerfile_for_image(dockerfile_path))
|
||||||
local_built_image = resolve_local_build_image(svc_name, project_name)
|
local_built_image = resolve_local_build_image(svc_name, project_name)
|
||||||
|
|
||||||
resolved_image = image or local_built_image or from_dockerfile or f"{project_name}-{svc_name}:latest"
|
placeholder_image = is_compose_build_placeholder(image, project_name) or is_compose_build_placeholder(local_built_image, project_name)
|
||||||
|
if placeholder_image:
|
||||||
|
resolved_image = from_dockerfile or image or local_built_image or f"{project_name}-{svc_name}:latest"
|
||||||
|
else:
|
||||||
|
resolved_image = image or local_built_image or from_dockerfile or f"{project_name}-{svc_name}:latest"
|
||||||
|
|
||||||
svc_map[svc_name] = {
|
svc_map[svc_name] = {
|
||||||
"image": resolved_image,
|
"image": resolved_image,
|
||||||
@@ -325,7 +383,7 @@ def check_containers():
|
|||||||
CONTAINER_UPDATE.clear()
|
CONTAINER_UPDATE.clear()
|
||||||
|
|
||||||
project_name = parse_project_name_from_script(SERVICES_UP_SCRIPT)
|
project_name = parse_project_name_from_script(SERVICES_UP_SCRIPT)
|
||||||
project_root = os.path.dirname(SERVICES_UP_SCRIPT)
|
project_root = get_project_root_from_script(SERVICES_UP_SCRIPT)
|
||||||
compose_files = get_compose_files_from_script(SERVICES_UP_SCRIPT)
|
compose_files = get_compose_files_from_script(SERVICES_UP_SCRIPT)
|
||||||
svc_map = parse_compose_services(compose_files, project_name, project_root)
|
svc_map = parse_compose_services(compose_files, project_name, project_root)
|
||||||
|
|
||||||
@@ -360,7 +418,7 @@ def check_containers():
|
|||||||
|
|
||||||
def dump_service_image_mapping():
|
def dump_service_image_mapping():
|
||||||
project_name = parse_project_name_from_script(SERVICES_UP_SCRIPT)
|
project_name = parse_project_name_from_script(SERVICES_UP_SCRIPT)
|
||||||
project_root = os.path.dirname(SERVICES_UP_SCRIPT)
|
project_root = get_project_root_from_script(SERVICES_UP_SCRIPT)
|
||||||
compose_files = get_compose_files_from_script(SERVICES_UP_SCRIPT)
|
compose_files = get_compose_files_from_script(SERVICES_UP_SCRIPT)
|
||||||
svc_map = parse_compose_services(compose_files, project_name, project_root)
|
svc_map = parse_compose_services(compose_files, project_name, project_root)
|
||||||
mapping = {name: data["image"] for name, data in sorted(svc_map.items())}
|
mapping = {name: data["image"] for name, data in sorted(svc_map.items())}
|
||||||
|
|||||||
Reference in New Issue
Block a user