@@ -3518,12 +3518,38 @@ pub async fn deploy_to_hosting(
35183518 // forced (no-cache) rebuild so the in-Dockerfile clone re-fetches the latest commit.
35193519 // If the app was deleted in Coolify (404), drop the stale mapping and fall through to
35203520 // create a fresh one — so a user who wipes the app in Coolify can just redeploy.
3521- //
3522- // NOTE (config drift): a redeploy reuses the app config Coolify stored at create time
3523- // (Dockerfile, ports_mappings, etc.). Changes to how Monastery *generates* those won't
3524- // reach an existing app via redeploy — the user must delete the app in Coolify and
3525- // redeploy (404 path) to pick them up. TODO: PATCH the app config on redeploy to sync.
35263521 if let Some ( existing_uuid) = lookup_deployment ( & state, req. project_id , req. connection_id ) . await ? {
3522+ // Refresh the app's stored Dockerfile FIRST. A Coolify redeploy rebuilds from the
3523+ // Dockerfile it saved at create time; because that Dockerfile was byte-identical
3524+ // every redeploy, the `git clone` layer stayed cached and the app kept serving the
3525+ // code from its first build. Re-sending a freshly-generated Dockerfile (new
3526+ // embedded cachebust) changes the clone layer's cache key so `force=true` actually
3527+ // re-clones the latest commit. Best-effort: if the update fails we still trigger the
3528+ // forced rebuild below (no worse than before).
3529+ let dockerfile_b64 = base64:: engine:: general_purpose:: STANDARD . encode ( clone_dockerfile. as_bytes ( ) ) ;
3530+ let patch_url = format ! ( "{}/api/v1/applications/{}" , base, existing_uuid) ;
3531+ match client
3532+ . patch ( & patch_url)
3533+ . header ( "Authorization" , format ! ( "Bearer {}" , api_token) )
3534+ . header ( "Content-Type" , "application/json" )
3535+ . json ( & serde_json:: json!( { "dockerfile" : dockerfile_b64 } ) )
3536+ . send ( )
3537+ . await
3538+ {
3539+ Ok ( r) if r. status ( ) . is_success ( ) => {
3540+ tracing:: info!( "Refreshed Coolify Dockerfile for app {} before redeploy" , existing_uuid) ;
3541+ }
3542+ Ok ( r) => {
3543+ tracing:: warn!(
3544+ "Coolify Dockerfile refresh returned HTTP {} — redeploying with force anyway" ,
3545+ r. status( ) . as_u16( )
3546+ ) ;
3547+ }
3548+ Err ( e) => {
3549+ tracing:: warn!( "Coolify Dockerfile refresh request failed ({}) — redeploying with force anyway" , e) ;
3550+ }
3551+ }
3552+
35273553 let deploy_url = format ! ( "{}/api/v1/deploy?uuid={}&force=true" , base, existing_uuid) ;
35283554 match client
35293555 . get ( & deploy_url)
@@ -4276,15 +4302,19 @@ fn generate_clone_dockerfile(
42764302 branch : & str ,
42774303) -> ( String , u16 ) {
42784304 let cachebust = chrono:: Utc :: now ( ) . timestamp ( ) ;
4279- // Clone step for node-based stages (git installed via apk).
4305+ // The cachebust is embedded DIRECTLY in the clone RUN command (not just as an `ARG`) so the
4306+ // layer's cache key changes every time the Dockerfile is regenerated — forcing Docker to
4307+ // re-run `git clone` and fetch the latest commit. An `ARG` alone does NOT work: Docker only
4308+ // busts cache at a build-arg's first *usage*, and the old Dockerfile never referenced it, so
4309+ // the clone layer was cached and redeploys kept shipping the code from the first build.
42804310 let node_clone = format ! (
4281- "RUN apk add --no-cache git && git -c http.sslVerify=false clone --depth 1 --single-branch --branch {b} \" {u}\" . && rm -rf .git" ,
4282- b = branch, u = clone_url
4311+ "RUN apk add --no-cache git && echo \" monastery-cachebust {cb} \" && git -c http.sslVerify=false clone --depth 1 --single-branch --branch {b} \" {u}\" . && rm -rf .git" ,
4312+ cb = cachebust , b = branch, u = clone_url
42834313 ) ;
42844314 // Clone step for the prebuilt alpine/git image (git already present).
42854315 let git_clone = format ! (
4286- "RUN git -c http.sslVerify=false clone --depth 1 --single-branch --branch {b} \" {u}\" . && rm -rf .git" ,
4287- b = branch, u = clone_url
4316+ "RUN echo \" monastery-cachebust {cb} \" && git -c http.sslVerify=false clone --depth 1 --single-branch --branch {b} \" {u}\" . && rm -rf .git" ,
4317+ cb = cachebust , b = branch, u = clone_url
42884318 ) ;
42894319
42904320 match framework {
0 commit comments