@@ -55,11 +55,15 @@ export class Release {
5555 version : string ,
5656 composeYaml : string ,
5757 metadata : ReleaseMetadata ,
58- ) : Promise < void > {
58+ ) : Promise < { previousSymlink : string | null } > {
5959 const dir = this . releaseDir ( stackName , version ) ;
6060 const metaJson = JSON . stringify ( metadata , null , 2 ) ;
6161 const stackDir = this . stackDir ( stackName ) ;
6262
63+ // Read previous symlink target before overwriting — used to restore on failure
64+ const prevResult = await sshExec ( this . connection , `readlink "${ stackDir } /current" 2>/dev/null || echo ""` ) ;
65+ const previousSymlink = prevResult . stdout . trim ( ) || null ;
66+
6367 await sshExec ( this . connection , `mkdir -p "${ dir } "` ) ;
6468
6569 // Write compose and metadata via stdin (no shell escaping needed)
@@ -74,6 +78,19 @@ export class Release {
7478 await sshExec ( this . connection , `ln -sfn "${ dir } " "${ stackDir } /current"` ) ;
7579
7680 printDebug ( `Release ${ version } created at ${ dir } ` ) ;
81+ return { previousSymlink } ;
82+ }
83+
84+ /**
85+ * Read the compose file from the current release symlink.
86+ * Returns null if no release exists yet.
87+ */
88+ async getCurrentComposeContent ( stackName : string ) : Promise < string | null > {
89+ const result = await sshExec (
90+ this . connection ,
91+ `cat "${ this . stackDir ( stackName ) } /current/docker-compose.yml" 2>/dev/null` ,
92+ ) ;
93+ return result . exitCode === 0 && result . stdout . trim ( ) ? result . stdout : null ;
7794 }
7895
7996 /**
@@ -186,10 +203,28 @@ export class Release {
186203
187204 /**
188205 * Remove a single release directory.
206+ * If restoreTo is provided and the `current` symlink points to this version,
207+ * restores it to the given target (or removes the symlink if restoreTo is null).
189208 */
190- async removeRelease ( stackName : string , version : string ) : Promise < void > {
209+ async removeRelease ( stackName : string , version : string , restoreTo ?: string | null ) : Promise < void > {
191210 const dir = this . releaseDir ( stackName , version ) ;
192- await sshExec ( this . connection , `rm -rf "${ dir } "` ) ;
211+ const stackDir = this . stackDir ( stackName ) ;
212+
213+ if ( restoreTo !== undefined ) {
214+ await sshExec (
215+ this . connection ,
216+ `currentTarget=$(readlink "${ stackDir } /current" 2>/dev/null); ` +
217+ `rm -rf "${ dir } "; ` +
218+ `if [ "$currentTarget" = "${ dir } " ]; then ` +
219+ ( restoreTo
220+ ? `ln -sfn "${ restoreTo } " "${ stackDir } /current"; `
221+ : `rm -f "${ stackDir } /current"; ` ) +
222+ `fi` ,
223+ ) ;
224+ } else {
225+ await sshExec ( this . connection , `rm -rf "${ dir } "` ) ;
226+ }
227+
193228 printDebug ( `Removed release ${ version } ` ) ;
194229 }
195230
0 commit comments