From 48a708adcfc334f6475cb45082b94692a79a8511 Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Sun, 24 May 2026 22:32:02 +0530 Subject: [PATCH 01/13] feat(build): Quick diff builds with persistance --- agent/builder.py | 94 ++++++++++++++++++++++++++++++++++++++++++++++++ agent/web.py | 18 +++++++++- 2 files changed, 111 insertions(+), 1 deletion(-) diff --git a/agent/builder.py b/agent/builder.py index 06133c01..0cf10d3d 100644 --- a/agent/builder.py +++ b/agent/builder.py @@ -1,5 +1,6 @@ from __future__ import annotations +import contextlib import os import shlex import shutil @@ -760,6 +761,99 @@ def _cleanup_context(self, context_tar_filepath: str): return {"cleanup": True} +class InstantImageBuilder(Base, JobMixin): + def __init__( + self, + base_image: str, + image_repository: str, + image_tag: str, + no_push: bool, + registry: dict, + clone_instructions: List[AppInfo], + build_name: str, + ) -> None: + super().__init__() + self._job_context = JobContext() + self.base_image = base_image + self.image_repository = image_repository + self.image_tag = image_tag + self.no_push = no_push + self.registry = registry + self.clone_instructions = clone_instructions + self.container_name = f"instant-build-{build_name}" + self.output: Output = {"build": [], "push": []} + + def _get_image_name(self) -> str: + return f"{self.image_repository}:{self.image_tag}" + + @job("Run Instant Build") + def run_instant_build(self): + try: + self._start_base_container() + self._pull_app_updates() + self._commit_instant_image() + if not self.no_push: + self._push_instant_image() + finally: + self._cleanup_container() + return self.data + + @step("Start Base Container") + def _start_base_container(self): + self.execute(f"docker pull {self.base_image}") + self.execute( + f"docker run -d --name {self.container_name} {self.base_image} tail -f /dev/null" + ) + + @step("Pull App Updates") + def _pull_app_updates(self): + for app_info in self.clone_instructions: + self._pull_app(app_info) + + def _pull_app(self, app_info: AppInfo): + app = app_info["app"] + url = app_info["url"] + new_hash = app_info["hash"] + app_path = f"/home/frappe/frappe-bench/apps/{app}" + old_hash = self._docker_exec(f"git -C {app_path} rev-parse HEAD").strip() + if old_hash == new_hash: + return + self._docker_exec(f"git -C {app_path} fetch --depth 1 {url} {new_hash}") + self._docker_exec(f"git -C {app_path} reset --hard {old_hash}") + self._docker_exec(f"git -C {app_path} clean -fd") + self._docker_exec(f"git -C {app_path} checkout {new_hash}") + + def _docker_exec(self, command: str) -> str: + result = self.execute(f"docker exec {self.container_name} bash -c {shlex.quote(command)}") + return result.get("output", "") if isinstance(result, dict) else "" + + @step("Commit Image") + def _commit_instant_image(self): + self.execute(f"docker commit {self.container_name} {self._get_image_name()}") + + @step("Push Docker Image") + def _push_instant_image(self): + environment = os.environ.copy() + client = docker.from_env(environment=environment, timeout=5 * 60) + auth_config = { + "username": self.registry["username"], + "password": self.registry["password"], + "serveraddress": self.registry["url"], + } + for line in client.images.push( + self.image_repository, + self.image_tag, + stream=True, + decode=True, + auth_config=auth_config, + ): + self.output["push"].append(line) + + def _cleanup_container(self): + with contextlib.suppress(Exception): + self.execute(f"docker rm -f {self.container_name}") + + def get_clone_directory(): path = os.path.join(os.getcwd(), ".clones") if not os.path.exists(path): diff --git a/agent/web.py b/agent/web.py index c2a3bbff..22e9395c 100644 --- a/agent/web.py +++ b/agent/web.py @@ -17,7 +17,7 @@ from rq.job import JobStatus from agent.base import AgentException -from agent.builder import ImageBuilder +from agent.builder import ImageBuilder, InstantImageBuilder from agent.database import JSONEncoderForSQLQueryResult from agent.database_physical_backup import DatabasePhysicalBackup from agent.database_physical_restore import DatabasePhysicalRestore @@ -217,6 +217,22 @@ def build_image(): return {"job": job} +@application.route("/builder/instant_build", methods=["POST"]) +def instant_build_image(): + data = request.json + builder = InstantImageBuilder( + base_image=data.get("base_image"), + image_repository=data.get("image_repository"), + image_tag=data.get("image_tag"), + no_push=data.get("no_push", False), + registry=data.get("registry"), + clone_instructions=data.get("clone_instructions"), + build_name=data.get("deploy_candidate_build"), + ) + job = builder.run_instant_build() + return {"job": job} + + @application.route("/server") def get_server(): return Server().dump() From ce26fb05b19af4fd6cdc666a6bd80d639259db46 Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Sun, 24 May 2026 22:32:34 +0530 Subject: [PATCH 02/13] fix(build): Lint --- agent/builder.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/agent/builder.py b/agent/builder.py index 0cf10d3d..eb9f2666 100644 --- a/agent/builder.py +++ b/agent/builder.py @@ -801,9 +801,7 @@ def run_instant_build(self): @step("Start Base Container") def _start_base_container(self): self.execute(f"docker pull {self.base_image}") - self.execute( - f"docker run -d --name {self.container_name} {self.base_image} tail -f /dev/null" - ) + self.execute(f"docker run -d --name {self.container_name} {self.base_image} tail -f /dev/null") @step("Pull App Updates") def _pull_app_updates(self): From bf527c7e02d5108e4849004516e3f5cacef60311 Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Mon, 25 May 2026 00:18:57 +0530 Subject: [PATCH 03/13] feat(build): Auto detect UI changes and bench build --- agent/builder.py | 37 ++++++++++++++++++++++++++----------- agent/web.py | 2 +- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/agent/builder.py b/agent/builder.py index eb9f2666..119d9f3e 100644 --- a/agent/builder.py +++ b/agent/builder.py @@ -43,6 +43,12 @@ class AppInfo(TypedDict): branch: str +class InstantBuildAppInfo(TypedDict): + app: str + url: str + hash: str + + class CloneError(AgentException): pass @@ -769,7 +775,7 @@ def __init__( image_tag: str, no_push: bool, registry: dict, - clone_instructions: List[AppInfo], + instant_build_app_instructions: List[InstantBuildAppInfo], build_name: str, ) -> None: super().__init__() @@ -779,7 +785,7 @@ def __init__( self.image_tag = image_tag self.no_push = no_push self.registry = registry - self.clone_instructions = clone_instructions + self.instant_build_app_instructions = instant_build_app_instructions self.container_name = f"instant-build-{build_name}" self.output: Output = {"build": [], "push": []} @@ -805,21 +811,30 @@ def _start_base_container(self): @step("Pull App Updates") def _pull_app_updates(self): - for app_info in self.clone_instructions: - self._pull_app(app_info) + for instant_build_app_info in self.instant_build_app_instructions: + self._pull_app(instant_build_app_info) - def _pull_app(self, app_info: AppInfo): - app = app_info["app"] - url = app_info["url"] - new_hash = app_info["hash"] + def _pull_app(self, instant_build_app_info: InstantBuildAppInfo): + app = instant_build_app_info["app"] + url = instant_build_app_info["url"] + new_hash = instant_build_app_info["hash"] app_path = f"/home/frappe/frappe-bench/apps/{app}" old_hash = self._docker_exec(f"git -C {app_path} rev-parse HEAD").strip() - if old_hash == new_hash: - return self._docker_exec(f"git -C {app_path} fetch --depth 1 {url} {new_hash}") - self._docker_exec(f"git -C {app_path} reset --hard {old_hash}") + self._docker_exec(f"git -C {app_path} reset --hard HEAD") self._docker_exec(f"git -C {app_path} clean -fd") self._docker_exec(f"git -C {app_path} checkout {new_hash}") + if self._has_ui_changes(app_path, old_hash, new_hash): + self._bench_build_app(app) + + def _has_ui_changes(self, app_path, old_hash, new_hash) -> bool: + out = self._docker_exec( + f"git -C {app_path} diff --name-only {old_hash} {new_hash} -- '*.vue' '*.js' '*.jsx'" + ) + return bool(out.strip()) + + def _bench_build_app(self, app): + self._docker_exec(f"cd /home/frappe/frappe-bench && bench build --app {app} --hardlink") def _docker_exec(self, command: str) -> str: result = self.execute(f"docker exec {self.container_name} bash -c {shlex.quote(command)}") diff --git a/agent/web.py b/agent/web.py index 22e9395c..ae5b437a 100644 --- a/agent/web.py +++ b/agent/web.py @@ -226,7 +226,7 @@ def instant_build_image(): image_tag=data.get("image_tag"), no_push=data.get("no_push", False), registry=data.get("registry"), - clone_instructions=data.get("clone_instructions"), + instant_build_app_instructions=data.get("instant_build_app_instructions"), build_name=data.get("deploy_candidate_build"), ) job = builder.run_instant_build() From 67f6f57972a29ce8377f0ab871f0d88cbe9ba3e3 Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Mon, 25 May 2026 11:06:22 +0530 Subject: [PATCH 04/13] feat(build): Add output from steps --- agent/builder.py | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/agent/builder.py b/agent/builder.py index 119d9f3e..a1387b0f 100644 --- a/agent/builder.py +++ b/agent/builder.py @@ -788,6 +788,7 @@ def __init__( self.instant_build_app_instructions = instant_build_app_instructions self.container_name = f"instant-build-{build_name}" self.output: Output = {"build": [], "push": []} + self.last_published = datetime.now() def _get_image_name(self) -> str: return f"{self.image_repository}:{self.image_tag}" @@ -819,7 +820,7 @@ def _pull_app(self, instant_build_app_info: InstantBuildAppInfo): url = instant_build_app_info["url"] new_hash = instant_build_app_info["hash"] app_path = f"/home/frappe/frappe-bench/apps/{app}" - old_hash = self._docker_exec(f"git -C {app_path} rev-parse HEAD").strip() + old_hash = self._docker_exec(f"git -C {app_path} rev-parse HEAD", publish=False).strip() self._docker_exec(f"git -C {app_path} fetch --depth 1 {url} {new_hash}") self._docker_exec(f"git -C {app_path} reset --hard HEAD") self._docker_exec(f"git -C {app_path} clean -fd") @@ -828,17 +829,37 @@ def _pull_app(self, instant_build_app_info: InstantBuildAppInfo): self._bench_build_app(app) def _has_ui_changes(self, app_path, old_hash, new_hash) -> bool: + """Check if ui files have changed between the two commits.""" out = self._docker_exec( - f"git -C {app_path} diff --name-only {old_hash} {new_hash} -- '*.vue' '*.js' '*.jsx'" + f"git -C {app_path} diff --name-only {old_hash} {new_hash} -- '*.vue' '*.js' '*.jsx'", + publish=False, ) return bool(out.strip()) def _bench_build_app(self, app): + """Bench build with hardlink to avoid symlinks""" self._docker_exec(f"cd /home/frappe/frappe-bench && bench build --app {app} --hardlink") - def _docker_exec(self, command: str) -> str: + def _docker_exec(self, command: str, publish: bool = True) -> str: + """Execute a command inside the running container and return the output""" result = self.execute(f"docker exec {self.container_name} bash -c {shlex.quote(command)}") - return result.get("output", "") if isinstance(result, dict) else "" + output = result.get("output", "") if isinstance(result, dict) else "" + if publish: + self.output["build"].append(output) + self._publish_throttled_output(False) + return output + + def _publish_throttled_output(self, flush: bool) -> None: + if flush: + self.publish_data(self.output) + return + + now = datetime.now() + if (now - self.last_published).total_seconds() <= 1: + return + + self.last_published = now + self.publish_data(self.output) @step("Commit Image") def _commit_instant_image(self): @@ -861,6 +882,9 @@ def _push_instant_image(self): auth_config=auth_config, ): self.output["push"].append(line) + self._publish_throttled_output(False) + + self._publish_throttled_output(True) def _cleanup_container(self): with contextlib.suppress(Exception): From 099f034f9e18f28d9f65209b35caa4999b190f98 Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Mon, 25 May 2026 11:57:43 +0530 Subject: [PATCH 05/13] fix(build): Ensure correct hardlink flag --- agent/builder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent/builder.py b/agent/builder.py index a1387b0f..cabfc1d9 100644 --- a/agent/builder.py +++ b/agent/builder.py @@ -838,7 +838,7 @@ def _has_ui_changes(self, app_path, old_hash, new_hash) -> bool: def _bench_build_app(self, app): """Bench build with hardlink to avoid symlinks""" - self._docker_exec(f"cd /home/frappe/frappe-bench && bench build --app {app} --hardlink") + self._docker_exec(f"cd /home/frappe/frappe-bench && bench build --app {app} --hard-link") def _docker_exec(self, command: str, publish: bool = True) -> str: """Execute a command inside the running container and return the output""" From 67942259064d7f49c30d42867f75d22181f1291c Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Mon, 25 May 2026 12:09:18 +0530 Subject: [PATCH 06/13] fix(build): Send final outputs once step is finished --- agent/builder.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/agent/builder.py b/agent/builder.py index cabfc1d9..8fca8b43 100644 --- a/agent/builder.py +++ b/agent/builder.py @@ -814,6 +814,8 @@ def _start_base_container(self): def _pull_app_updates(self): for instant_build_app_info in self.instant_build_app_instructions: self._pull_app(instant_build_app_info) + self._publish_throttled_output(True) + return self.output["build"] def _pull_app(self, instant_build_app_info: InstantBuildAppInfo): app = instant_build_app_info["app"] @@ -885,6 +887,7 @@ def _push_instant_image(self): self._publish_throttled_output(False) self._publish_throttled_output(True) + return self.output["push"] def _cleanup_container(self): with contextlib.suppress(Exception): From 2e73a288c3935ad5abdac3c905a86e6450721dce Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Mon, 25 May 2026 17:21:08 +0530 Subject: [PATCH 07/13] chore(build): Rename as patch build --- agent/builder.py | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/agent/builder.py b/agent/builder.py index 8fca8b43..23003111 100644 --- a/agent/builder.py +++ b/agent/builder.py @@ -43,7 +43,7 @@ class AppInfo(TypedDict): branch: str -class InstantBuildAppInfo(TypedDict): +class PatchBuildAppInfo(TypedDict): app: str url: str hash: str @@ -767,7 +767,7 @@ def _cleanup_context(self, context_tar_filepath: str): return {"cleanup": True} -class InstantImageBuilder(Base, JobMixin): +class PatchImageBuilder(Base, JobMixin): def __init__( self, base_image: str, @@ -775,7 +775,7 @@ def __init__( image_tag: str, no_push: bool, registry: dict, - instant_build_app_instructions: List[InstantBuildAppInfo], + patch_build_app_instructions: List[PatchBuildAppInfo], build_name: str, ) -> None: super().__init__() @@ -785,22 +785,22 @@ def __init__( self.image_tag = image_tag self.no_push = no_push self.registry = registry - self.instant_build_app_instructions = instant_build_app_instructions - self.container_name = f"instant-build-{build_name}" + self.patch_build_app_instructions = patch_build_app_instructions + self.container_name = f"patch-build-{build_name}" self.output: Output = {"build": [], "push": []} self.last_published = datetime.now() def _get_image_name(self) -> str: return f"{self.image_repository}:{self.image_tag}" - @job("Run Instant Build") - def run_instant_build(self): + @job("Run Patch Build") + def run_patch_build(self): try: self._start_base_container() self._pull_app_updates() - self._commit_instant_image() + self._commit_patch_image() if not self.no_push: - self._push_instant_image() + self._push_patch_image() finally: self._cleanup_container() return self.data @@ -812,15 +812,15 @@ def _start_base_container(self): @step("Pull App Updates") def _pull_app_updates(self): - for instant_build_app_info in self.instant_build_app_instructions: - self._pull_app(instant_build_app_info) + for patch_build_app_info in self.patch_build_app_instructions: + self._pull_app(patch_build_app_info) self._publish_throttled_output(True) return self.output["build"] - def _pull_app(self, instant_build_app_info: InstantBuildAppInfo): - app = instant_build_app_info["app"] - url = instant_build_app_info["url"] - new_hash = instant_build_app_info["hash"] + def _pull_app(self, patch_build_app_info: PatchBuildAppInfo): + app = patch_build_app_info["app"] + url = patch_build_app_info["url"] + new_hash = patch_build_app_info["hash"] app_path = f"/home/frappe/frappe-bench/apps/{app}" old_hash = self._docker_exec(f"git -C {app_path} rev-parse HEAD", publish=False).strip() self._docker_exec(f"git -C {app_path} fetch --depth 1 {url} {new_hash}") @@ -864,11 +864,11 @@ def _publish_throttled_output(self, flush: bool) -> None: self.publish_data(self.output) @step("Commit Image") - def _commit_instant_image(self): + def _commit_patch_image(self): self.execute(f"docker commit {self.container_name} {self._get_image_name()}") @step("Push Docker Image") - def _push_instant_image(self): + def _push_patch_image(self): environment = os.environ.copy() client = docker.from_env(environment=environment, timeout=5 * 60) auth_config = { From 48e538c3c2c3c2438c9043d76dc0b8ed5f118c7f Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Mon, 25 May 2026 17:25:25 +0530 Subject: [PATCH 08/13] fix(build): Rename Instant builder to Patch builder on agent --- agent/web.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agent/web.py b/agent/web.py index ae5b437a..cda55734 100644 --- a/agent/web.py +++ b/agent/web.py @@ -17,7 +17,7 @@ from rq.job import JobStatus from agent.base import AgentException -from agent.builder import ImageBuilder, InstantImageBuilder +from agent.builder import ImageBuilder, PatchImageBuilder from agent.database import JSONEncoderForSQLQueryResult from agent.database_physical_backup import DatabasePhysicalBackup from agent.database_physical_restore import DatabasePhysicalRestore @@ -220,7 +220,7 @@ def build_image(): @application.route("/builder/instant_build", methods=["POST"]) def instant_build_image(): data = request.json - builder = InstantImageBuilder( + builder = PatchImageBuilder( base_image=data.get("base_image"), image_repository=data.get("image_repository"), image_tag=data.get("image_tag"), From 6ac19223935a2bfead146a2b53f4292b90c3bd8d Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Mon, 25 May 2026 17:26:38 +0530 Subject: [PATCH 09/13] fix(build): Rename Instant builder to Patch builder on agent (everywhere) --- agent/web.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/agent/web.py b/agent/web.py index cda55734..dcf885c5 100644 --- a/agent/web.py +++ b/agent/web.py @@ -217,8 +217,8 @@ def build_image(): return {"job": job} -@application.route("/builder/instant_build", methods=["POST"]) -def instant_build_image(): +@application.route("/builder/patch_build", methods=["POST"]) +def patch_build_image(): data = request.json builder = PatchImageBuilder( base_image=data.get("base_image"), @@ -226,7 +226,7 @@ def instant_build_image(): image_tag=data.get("image_tag"), no_push=data.get("no_push", False), registry=data.get("registry"), - instant_build_app_instructions=data.get("instant_build_app_instructions"), + patch_build_app_instructions=data.get("patch_build_app_instructions"), build_name=data.get("deploy_candidate_build"), ) job = builder.run_instant_build() From 66688f68f5590d66560df93ea3f55a38a6617663 Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Mon, 25 May 2026 17:30:47 +0530 Subject: [PATCH 10/13] fix(build): Call correct method to build --- agent/web.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent/web.py b/agent/web.py index dcf885c5..506dee93 100644 --- a/agent/web.py +++ b/agent/web.py @@ -229,7 +229,7 @@ def patch_build_image(): patch_build_app_instructions=data.get("patch_build_app_instructions"), build_name=data.get("deploy_candidate_build"), ) - job = builder.run_instant_build() + job = builder.run_patch_build() return {"job": job} From 40cb6472534b4b286ff692491a6fd72c28a3e1f9 Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Mon, 25 May 2026 17:43:04 +0530 Subject: [PATCH 11/13] feat(build): Reinstall app dependencies in case of changes to pyproject or requirements --- agent/builder.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/agent/builder.py b/agent/builder.py index 23003111..0e890426 100644 --- a/agent/builder.py +++ b/agent/builder.py @@ -827,19 +827,34 @@ def _pull_app(self, patch_build_app_info: PatchBuildAppInfo): self._docker_exec(f"git -C {app_path} reset --hard HEAD") self._docker_exec(f"git -C {app_path} clean -fd") self._docker_exec(f"git -C {app_path} checkout {new_hash}") + + if self._has_dependency_changes(app_path, old_hash, new_hash): + self._reinstall_app_deps(app) + if self._has_ui_changes(app_path, old_hash, new_hash): self._bench_build_app(app) def _has_ui_changes(self, app_path, old_hash, new_hash) -> bool: - """Check if ui files have changed between the two commits.""" + """If the two commits have UI changes""" out = self._docker_exec( f"git -C {app_path} diff --name-only {old_hash} {new_hash} -- '*.vue' '*.js' '*.jsx'", publish=False, ) return bool(out.strip()) + def _has_dependency_changes(self, app_path, old_hash, new_hash) -> bool: + """If the two commits have python dependency changes""" + out = self._docker_exec( + f"git -C {app_path} diff --name-only {old_hash} {new_hash} -- requirements.txt pyproject.toml", + publish=False, + ) + return bool(out.strip()) + + def _reinstall_app_deps(self, app): + pip = "/home/frappe/frappe-bench/env/bin/python -m pip" + self._docker_exec(f"{pip} install -e /home/frappe/frappe-bench/apps/{app}") + def _bench_build_app(self, app): - """Bench build with hardlink to avoid symlinks""" self._docker_exec(f"cd /home/frappe/frappe-bench && bench build --app {app} --hard-link") def _docker_exec(self, command: str, publish: bool = True) -> str: From 7d13404a208f2fa177ca08d5804f0c0bfe6dca2a Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Tue, 26 May 2026 22:29:30 +0530 Subject: [PATCH 12/13] fix(build): Ensure login with correct docker credentials --- agent/builder.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/agent/builder.py b/agent/builder.py index 0e890426..9619eba5 100644 --- a/agent/builder.py +++ b/agent/builder.py @@ -807,6 +807,13 @@ def run_patch_build(self): @step("Start Base Container") def _start_base_container(self): + """Docker login and pull base image""" + self.execute( + f"docker login " + f"-u {self.registry['username']} " + f"-p {self.registry['password']} " + f"{self.registry['url']}" + ) self.execute(f"docker pull {self.base_image}") self.execute(f"docker run -d --name {self.container_name} {self.base_image} tail -f /dev/null") From ed5babf2b8d79fced0ee84efe37cf96425e909d9 Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Tue, 26 May 2026 23:02:28 +0530 Subject: [PATCH 13/13] fix(build): Ensure supervisord is running in the container image for further operations --- agent/builder.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/agent/builder.py b/agent/builder.py index 9619eba5..bb9b9094 100644 --- a/agent/builder.py +++ b/agent/builder.py @@ -887,7 +887,9 @@ def _publish_throttled_output(self, flush: bool) -> None: @step("Commit Image") def _commit_patch_image(self): - self.execute(f"docker commit {self.container_name} {self._get_image_name()}") + self.execute( + f"docker commit --change='CMD [\"supervisord\"]' {self.container_name} {self._get_image_name()}" + ) @step("Push Docker Image") def _push_patch_image(self):