feat(builds): Pull + Commit + Push Builds #6498
Conversation
|
| Filename | Overview |
|---|---|
| press/press/doctype/deploy_candidate_build/deploy_candidate_build.py | Core patch-build logic: adds run_patch_build, send_patch_build_instructions, step sync, and dual-platform chaining. Several edge-cases (KeyError in _get_patch_app_updates, None base image) flagged in earlier review threads remain open; N+1 queries in _get_patch_app_updates are new. |
| press/press/doctype/release_pipeline/release_pipeline.py | Adds initiate_patch_deploy task and wires trigger_patch_deploy through prepare_deployment and create_release. prepare_deployment is missing a default value for trigger_patch_deploy (flagged in previous review thread), which can break in-flight pipelines on deploy. |
| press/press/doctype/release_group/release_group.py | Adds can_run_patch_build, _get_previous_candidate, and _has_active_benches helpers. Logic is well-covered by the companion test file; exposes can_run_patch_build result in deploy_information, adding a few extra DB queries per deploy-dialog load. |
| press/press/doctype/bench_update/bench_update.py | Adds trigger_patch_deploy parameter (defaults to False), routes to trigger_patch_deploy() when set, and raises a clear Exception on suspension errors instead of a bare KeyError. |
| press/press/doctype/deploy_candidate/deploy_candidate.py | Adds trigger_patch_deploy whitelisted method that guards against suspended builds and creates a patch DCB. User-visible message string is not wrapped with _(). |
| press/press/doctype/deploy_candidate/docker_output_parsers.py | Adds PatchBuildOutputParser to sync step outputs from agent job steps, and fixes UploadStepUpdater.process to guard against missing last_updated attribute when used from a patch build context. |
| press/press/doctype/release_group/test_can_run_patch_build.py | Good test coverage for all major can_run_patch_build conditions: app add/remove/source-change, dependency change, env var change, public group, archived bench, and dual-platform active-bench scenarios. |
| dashboard/src/components/group/UpdateReleaseGroupDialog.vue | Adds Deploy as Patch button (guarded by can_run_patch_build) that shows an info step before triggering the patch deploy API call. Navigation and state machine look correct. |
Sequence Diagram
sequenceDiagram
participant UI as UpdateReleaseGroupDialog
participant API as press.api.bench
participant BU as BenchUpdate
participant DC as DeployCandidate
participant DCB as DeployCandidateBuild
participant Agent as Agent (builder server)
UI->>API: "deploy_and_update(trigger_patch_deploy=True)"
API->>BU: "deploy(trigger_patch_deploy=True)"
BU->>DC: trigger_patch_deploy()
DC->>DCB: "create_build(patch_build=True).insert()"
DCB->>DCB: after_insert → run_patch_build()
DCB->>DCB: can_run_patch_build() guard
DCB->>DCB: send_patch_build_instructions(previous_candidate)
DCB->>Agent: run_patch_build(base_image, app_updates, registry, …)
Agent-->>DCB: job updates (process_run_patch_build)
DCB->>DCB: _sync_patch_build_step_statuses
DCB->>DCB: PatchBuildOutputParser.parse_and_update
alt build succeeded
DCB->>DCB: update_deploy_candidate_with_build()
alt second platform required
DCB->>DCB: _create_platform_patch_build_if_required_and_deploy()
DCB->>DCB: insert new patch DCB → run_patch_build() (arm64/x86_64)
else no second platform
DCB->>DCB: create_deploy()
end
else build failed
DCB->>DCB: handle_build_failure()
end
Reviews (12): Last reviewed commit: "Merge branch 'develop' into quick-builds" | Re-trigger Greptile
Codecov Report❌ Patch coverage is ❌ Your patch check has failed because the patch coverage (62.89%) is below the target coverage (75.00%). You can increase the patch coverage or adjust the target coverage. Additional details and impacted files@@ Coverage Diff @@
## develop #6498 +/- ##
============================================
- Coverage 61.00% 50.17% -10.84%
============================================
Files 110 955 +845
Lines 17375 79191 +61816
Branches 369 368 -1
============================================
+ Hits 10600 39733 +29133
- Misses 6749 39432 +32683
Partials 26 26
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
@greptileai rereview |
|
@greptileai rereview |
The second issue is not correct, since the secondary platform build will not create a new deploy candidate doctype instead will associate the deploy_Candidate_build doctype to the older deploy_candidate doctype |
|
@greptileai rereview |
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
|
@greptileai rereview |
| def _get_base_image_for_platform(self, previous_candidate: "DeployCandidate") -> str: | ||
| build_name = ( | ||
| previous_candidate.arm_build | ||
| if self.platform == "arm64" | ||
| else (previous_candidate.intel_build or previous_candidate.arm_build) | ||
| ) | ||
| return frappe.db.get_value("Deploy Candidate Build", build_name, "docker_image") |
There was a problem hiding this comment.
None base image passed to agent for arm64 patch builds on intel-only history
When a release group previously had only intel servers (so previous_candidate.arm_build is None) and an arm64 server is later added, can_run_patch_build still returns True (it checks _has_active_benches, which only requires the arm bench if the previous candidate already has arm_build set). After the intel patch build succeeds, _create_platform_patch_build_if_required_and_deploy creates an arm64 patch build whose run_patch_build then calls _get_base_image_for_platform("arm64"). With previous_candidate.arm_build is None, build_name is None, frappe.db.get_value returns None, and "base_image": None is sent to the agent — causing the agent request to fail or produce a cryptic error.
The root cause is that can_run_patch_build doesn't verify that the previous candidate has a build for every platform the current candidate requires. Adding a platform-requirements comparison (e.g. checking that previous_candidate.arm_build exists when the current candidate has requires_arm_build=True) would prevent this path from being reachable.
|
|
||
| @task(queue=_get_task_execution_queue()) | ||
| def prepare_deployment(self, apps, sites, run_will_fail_check) -> tuple[str, str]: | ||
| def prepare_deployment(self, apps, sites, run_will_fail_check, trigger_patch_deploy) -> tuple[str, str]: |
There was a problem hiding this comment.
Missing default value for
trigger_patch_deploy in prepare_deployment. This task is enqueued by create_release — if an old create_release serialized a prepare_deployment call with 3 positional arguments before this code was deployed, the new code would pick up that task and raise TypeError: missing 1 required positional argument: 'trigger_patch_deploy', silently breaking in-flight pipelines at deployment time. Every other newly-added parameter in this PR (create_release, create_deploy_candidate) correctly defaults to False; this one was missed.
| def prepare_deployment(self, apps, sites, run_will_fail_check, trigger_patch_deploy) -> tuple[str, str]: | |
| def prepare_deployment(self, apps, sites, run_will_fail_check, trigger_patch_deploy=False) -> tuple[str, str]: |
Codecov Report❌ Patch coverage is ❌ Your patch status has failed because the patch coverage (60.98%) is below the target coverage (75.00%). You can increase the patch coverage or adjust the target coverage. Additional details and impacted files@@ Coverage Diff @@
## develop #6498 +/- ##
===========================================
+ Coverage 49.43% 50.14% +0.71%
===========================================
Files 976 984 +8
Lines 81257 82293 +1036
Branches 379 522 +143
===========================================
+ Hits 40166 41268 +1102
+ Misses 41063 40993 -70
- Partials 28 32 +4
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Goes with frappe/agent#527
Greptile Summary
This PR introduces "patch builds" — a fast deploy path that pulls the latest app code into an existing Docker image (pull + commit + push) instead of rebuilding from scratch. It wires the feature through the full stack: a new
trigger_patch_deployflow onDeployCandidate, arun_patch_buildpath inDeployCandidateBuild, output parsing viaPatchBuildOutputParser, release-pipeline support via a newinitiate_patch_deploytask, and a "Deploy as Patch" button in the dashboard dialog.can_run_patch_buildguards the feature with thorough checks (same apps, sources, dependencies, packages, env vars, active benches for every required platform) and is backed by a comprehensive test suite._create_platform_patch_build_if_required_and_deploychains a second-platform patch build automatically when a dual-platform candidate is needed, mirroring how regular builds handle arm64/x86_64 pairs.PatchBuildOutputParsersyncs step statuses from agent job steps rather than streaming Docker output, and theUploadStepUpdateralready guards against a missing upload step forno_pushbuilds.Confidence Score: 4/5
Safe to merge after fixing the suspended-builds error path in bench_update.py — the current code silently discards the actionable message and returns a 500 to the caller.
The core patch-build machinery is well-structured and the guard logic is thoroughly tested. The one issue that stands out is in bench_update.py: when builds are suspended and the old (non-workflow) deploy path is used with trigger_patch_deploy=True, the informative suspension message is raised as a plain Python Exception, which Frappe's API layer swallows and converts to a generic 500. The user never sees the real reason.
press/press/doctype/bench_update/bench_update.py — the suspended-builds error case uses raise Exception instead of frappe.throw, losing the message on the non-new-deploy-flow path.
Important Files Changed
Sequence Diagram
sequenceDiagram participant UI as Dashboard (Vue) participant API as bench.deploy_and_update participant BU as BenchUpdate.deploy() participant DC as DeployCandidate.trigger_patch_deploy() participant DCB as DeployCandidateBuild.run_patch_build() participant Agent as Agent (builder/patch_build) participant AJ as AgentJob processor UI->>API: "deploy_and_update(trigger_patch_deploy=True)" API->>BU: "bench_update.deploy(trigger_patch_deploy=True)" BU->>DC: candidate.trigger_patch_deploy() DC-->>BU: "{error: False, name: build_name}" BU-->>API: build_name Note over DCB: after_insert → run_patch_build() DCB->>DCB: can_run_patch_build() guard DCB->>DCB: _get_previous_candidate() DCB->>Agent: run_patch_build(base_image, app_updates, …) Agent-->>AJ: job updates (step statuses + output) AJ->>DCB: process_run_patch_build() DCB->>DCB: _sync_patch_build_step_statuses() DCB->>DCB: patch_build_output_parser.parse_and_update() alt Build succeeded DCB->>DCB: update_deploy_candidate_with_build() alt Second platform required DCB->>DCB: insert new DeployCandidateBuild (other platform) else deploy_after_build DCB->>DCB: create_deploy() end else Build failed DCB->>DCB: handle_build_failure() endReviews (7): Last reviewed commit: "Merge branch 'develop' into quick-builds" | Re-trigger Greptile