@milaboratories/pl-deployments 2.1.3 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ wd: ${r.opts.cwd}`),n.info(" spawning child process"),ct.spawn(r.cmd,r.args,r.o
4
4
  URL: ${r}
5
5
  Save to: ${e}`);const{body:i,statusCode:o}=await lt.request(r);if(t.statusCode=o,o!=200){const s=await ut.text(i);throw t.errorMsg=`failed to download archive: ${o}, response: ${s.slice(0,1e3)}`,n.error(t.errorMsg),new Error(t.errorMsg)}return t.tmpPath=e+".tmp",await B.Readable.toWeb(i).pipeTo(B.Writable.toWeb(F.createWriteStream(t.tmpPath))),t.wroteTmp=!0,t.tmpExisted=await w.fileExists(t.tmpPath),await g.rename(t.tmpPath,e),t.renamed=!0,t.newExisted=await w.fileExists(e),t}catch(i){const o=`downloadArchive: ${JSON.stringify(i)}, state: ${JSON.stringify(t)}`;throw n.error(o),new Error(o)}}const Ct=".ok";async function Et(n,r,e,t){if(n.info("extracting archive..."),n.info(` archive path: '${r}'`),n.info(` target dir: '${t}'`),!await w.fileExists(r)){const o=`Platforma Backend binary archive not found at '${r}'`;throw n.error(o),new Error(o)}const i=h.join(t,Ct);if(await w.fileExists(i)){n.info(`Platforma Backend binaries unpack skipped: '${t}' exists`);return}switch(await w.fileExists(t)&&(n.info(`Removing previous incompletely unpacked folder: '${t}'`),await g.rm(t,{recursive:!0})),n.info(` creating target dir '${t}'`),await g.mkdir(t,{recursive:!0}),n.info(`Unpacking Platforma Backend archive:
6
6
  Archive: ${r}
7
- Target dir: ${t}`),e){case"tgz":await ft.x({file:r,cwd:t,gzip:!0});break;case"zip":await ht(r,t);break;default:w.assertNever(e)}await g.writeFile(i,"ok"),n.info(" ... unpack done.")}const bt={linux:"tgz",macos:"tgz",windows:"zip"};function b(){return"1.33.1"}function At(){return{type:"Download",version:b()}}async function Ot(n,r,e){switch(e.type){case"Download":const t=await St(n,r,"pl",`pl-${e.version}`,C.arch(),C.platform());return h.join(t.baseName,"binaries",Ft[k(C.platform())]);case"Local":return e.path;default:w.assertNever(e)}}const Ft={linux:"platforma",macos:"platforma",windows:"platforma.exe"};function J(n){return h.join(n,"pl_pid")}async function Dt(n){if(!await w.fileExists(n))return;const r=await g.readFile(n);return Number(r.toString())}async function xt(n,r){await g.writeFile(n,JSON.stringify(r))}function kt(){return{}}function Rt(n,r,e){return n[r]=e,e}async function R(n,r){const e=kt();try{return await r((i,o)=>Rt(e,i,o),e)}catch(t){throw n.error(`error ${t} while doing traced operation, state: ${JSON.stringify(e)}`),t}}const L="config-local.yaml";class V{constructor(r,e,t,i,o,s,a,c){f(this,"instance");f(this,"pid");f(this,"nRuns",0);f(this,"lastRunHistory",{});f(this,"wasStopped",!1);this.logger=r,this.workingDir=e,this.startOptions=t,this.initialStartHistory=i,this.onClose=o,this.onError=s,this.onCloseAndError=a,this.onCloseAndErrorNoStop=c}async start(){await R(this.logger,async(r,e)=>{this.wasStopped=!1;const t=mt(this.logger,this.startOptions);t.on("error",o=>{this.logger.error(`error '${o}', while running platforma, started opts: ${JSON.stringify(this.debugInfo())}`),this.onError!==void 0&&this.onError(this),this.onCloseAndError!==void 0&&this.onCloseAndError(this),this.onCloseAndErrorNoStop!==void 0&&!this.wasStopped&&this.onCloseAndErrorNoStop(this)}),t.on("close",()=>{this.logger.warn(`platforma was closed, started opts: ${JSON.stringify(this.debugInfo())}`),this.onClose!==void 0&&this.onClose(this),this.onCloseAndError!==void 0&&this.onCloseAndError(this),this.onCloseAndErrorNoStop!==void 0&&!this.wasStopped&&this.onCloseAndErrorNoStop(this)}),r("started",!0);const i=r("pidFile",J(this.workingDir));r("pid",w.notEmpty(t.pid)),r("pidWritten",await xt(i,w.notEmpty(t.pid))),this.nRuns++,this.instance=t,this.pid=t.pid,this.lastRunHistory=e})}stop(){this.wasStopped=!0,M(w.notEmpty(this.pid))}async waitStopped(){await G(w.notEmpty(this.pid),15e3)}stopped(){return this.wasStopped}async isAlive(){return await x(w.notEmpty(this.pid))}debugInfo(){return{lastRunHistory:this.lastRunHistory,nRuns:this.nRuns,pid:this.pid,workingDir:this.workingDir,initialStartHistory:this.initialStartHistory,wasStopped:this.wasStopped}}}async function Nt(n,r){const e=Math.max(pt.cpus().length-2,1),t=W(r,e);return await R(n,async(i,o)=>{i("startOptions",{...t,config:"too wordy"});const s=h.resolve(t.workingDir);t.closeOld&&i("closeOld",await _t(n,s));const a=h.join(s,L);n.info(`writing configuration '${a}'...`),await g.writeFile(a,t.config);const c=h.join(s,"binaries"),l=await Ot(n,c,t.plBinary),u=i("binaryPath",h.join("binaries",l)),d=K(u,a,t,s,process.env);i("processOpts",{cmd:d.cmd,args:d.args,cwd:d.opts.cwd});const p=new V(n,t.workingDir,d,o,t.onClose,t.onError,t.onCloseAndError,t.onCloseAndErrorNoStop);return await p.start(),p})}async function _t(n,r){return await R(n,async(e,t)=>{const i=e("pidFilePath",J(r)),o=e("pid",await Dt(i)),s=e("wasAlive",await x(o));return o!==void 0&&s&&(e("stopped",M(o)),e("waitStopped",await G(o,1e4))),t})}function W(n,r){var i;const e={plBinary:At(),spawnOptions:{env:{GOMAXPROCS:String(r)}},closeOld:!0};if((i=n.spawnOptions)!=null&&i.env&&(e.spawnOptions.env={...e.spawnOptions.env,...n.spawnOptions.env}),n.spawnOptions){const o={...n.spawnOptions};delete o.env,e.spawnOptions={...e.spawnOptions,...o}}const t={...n};return delete t.spawnOptions,{...e,...t}}function K(n,r,e,t,i){var a;const o={cmd:n,args:["--config",r],opts:{env:{...i},cwd:t,stdio:["pipe","ignore","inherit"],windowsHide:!0}};(a=e.spawnOptions)!=null&&a.env&&(o.opts.env={...o.opts.env,...e.spawnOptions.env});const s={...e.spawnOptions};return delete s.env,o.opts={...o.opts,...s},o}const Bt={keepaliveInterval:6e4,keepaliveCountMax:10};class A{constructor(r,e){f(this,"config");f(this,"homeDir");f(this,"forwardedServers",[]);this.logger=r,this.client=e}static async init(r,e){const t={...Bt,...e},i=new A(r,new S.Client);return await i.connect(t),i}getForwardedServers(){return this.forwardedServers}getFullHostName(){var r,e;return`${(r=this.config)==null?void 0:r.host}:${(e=this.config)==null?void 0:e.port}`}getUserName(){var r;return(r=this.config)==null?void 0:r.username}async connect(r){return this.config=r,await Ut(this.client,r)}async exec(r){return new Promise((e,t)=>{this.client.exec(r,(i,o)=>{if(i)return t(new Error(`ssh.exec: ${r}: ${i}`));let s="",a="";o.on("close",c=>{c===0?e({stdout:s,stderr:a}):t(new Error(`Command ${r} exited with code ${c}, stdout: ${s}, stderr: ${a}`))}).on("data",c=>{s+=c.toString()}).stderr.on("data",c=>{a+=c.toString()})})})}static async getAuthTypes(r,e){return new Promise(t=>{let i="";const o=new S.Client;o.on("ready",()=>{o.end();const s=this.extractAuthMethods(i);t(s.length===0?["publickey","password"]:s)}),o.on("error",()=>{o.end(),t(["publickey","password"])}),o.connect({host:r,port:e,username:new Date().getTime().toString(),debug:s=>{i+=`${s}
7
+ Target dir: ${t}`),e){case"tgz":await ft.x({file:r,cwd:t,gzip:!0});break;case"zip":await ht(r,t);break;default:w.assertNever(e)}await g.writeFile(i,"ok"),n.info(" ... unpack done.")}const bt={linux:"tgz",macos:"tgz",windows:"zip"};function b(){return"1.33.4"}function At(){return{type:"Download",version:b()}}async function Ot(n,r,e){switch(e.type){case"Download":const t=await St(n,r,"pl",`pl-${e.version}`,C.arch(),C.platform());return h.join(t.baseName,"binaries",Ft[k(C.platform())]);case"Local":return e.path;default:w.assertNever(e)}}const Ft={linux:"platforma",macos:"platforma",windows:"platforma.exe"};function J(n){return h.join(n,"pl_pid")}async function Dt(n){if(!await w.fileExists(n))return;const r=await g.readFile(n);return Number(r.toString())}async function xt(n,r){await g.writeFile(n,JSON.stringify(r))}function kt(){return{}}function Rt(n,r,e){return n[r]=e,e}async function R(n,r){const e=kt();try{return await r((i,o)=>Rt(e,i,o),e)}catch(t){throw n.error(`error ${t} while doing traced operation, state: ${JSON.stringify(e)}`),t}}const L="config-local.yaml";class V{constructor(r,e,t,i,o,s,a,c){f(this,"instance");f(this,"pid");f(this,"nRuns",0);f(this,"lastRunHistory",{});f(this,"wasStopped",!1);this.logger=r,this.workingDir=e,this.startOptions=t,this.initialStartHistory=i,this.onClose=o,this.onError=s,this.onCloseAndError=a,this.onCloseAndErrorNoStop=c}async start(){await R(this.logger,async(r,e)=>{this.wasStopped=!1;const t=mt(this.logger,this.startOptions);t.on("error",o=>{this.logger.error(`error '${o}', while running platforma, started opts: ${JSON.stringify(this.debugInfo())}`),this.onError!==void 0&&this.onError(this),this.onCloseAndError!==void 0&&this.onCloseAndError(this),this.onCloseAndErrorNoStop!==void 0&&!this.wasStopped&&this.onCloseAndErrorNoStop(this)}),t.on("close",()=>{this.logger.warn(`platforma was closed, started opts: ${JSON.stringify(this.debugInfo())}`),this.onClose!==void 0&&this.onClose(this),this.onCloseAndError!==void 0&&this.onCloseAndError(this),this.onCloseAndErrorNoStop!==void 0&&!this.wasStopped&&this.onCloseAndErrorNoStop(this)}),r("started",!0);const i=r("pidFile",J(this.workingDir));r("pid",w.notEmpty(t.pid)),r("pidWritten",await xt(i,w.notEmpty(t.pid))),this.nRuns++,this.instance=t,this.pid=t.pid,this.lastRunHistory=e})}stop(){this.wasStopped=!0,M(w.notEmpty(this.pid))}async waitStopped(){await G(w.notEmpty(this.pid),15e3)}stopped(){return this.wasStopped}async isAlive(){return await x(w.notEmpty(this.pid))}debugInfo(){return{lastRunHistory:this.lastRunHistory,nRuns:this.nRuns,pid:this.pid,workingDir:this.workingDir,initialStartHistory:this.initialStartHistory,wasStopped:this.wasStopped}}}async function Nt(n,r){const e=Math.max(pt.cpus().length-2,1),t=W(r,e);return await R(n,async(i,o)=>{i("startOptions",{...t,config:"too wordy"});const s=h.resolve(t.workingDir);t.closeOld&&i("closeOld",await _t(n,s));const a=h.join(s,L);n.info(`writing configuration '${a}'...`),await g.writeFile(a,t.config);const c=h.join(s,"binaries"),l=await Ot(n,c,t.plBinary),u=i("binaryPath",h.join("binaries",l)),d=K(u,a,t,s,process.env);i("processOpts",{cmd:d.cmd,args:d.args,cwd:d.opts.cwd});const p=new V(n,t.workingDir,d,o,t.onClose,t.onError,t.onCloseAndError,t.onCloseAndErrorNoStop);return await p.start(),p})}async function _t(n,r){return await R(n,async(e,t)=>{const i=e("pidFilePath",J(r)),o=e("pid",await Dt(i)),s=e("wasAlive",await x(o));return o!==void 0&&s&&(e("stopped",M(o)),e("waitStopped",await G(o,1e4))),t})}function W(n,r){var i;const e={plBinary:At(),spawnOptions:{env:{GOMAXPROCS:String(r)}},closeOld:!0};if((i=n.spawnOptions)!=null&&i.env&&(e.spawnOptions.env={...e.spawnOptions.env,...n.spawnOptions.env}),n.spawnOptions){const o={...n.spawnOptions};delete o.env,e.spawnOptions={...e.spawnOptions,...o}}const t={...n};return delete t.spawnOptions,{...e,...t}}function K(n,r,e,t,i){var a;const o={cmd:n,args:["--config",r],opts:{env:{...i},cwd:t,stdio:["pipe","ignore","inherit"],windowsHide:!0}};(a=e.spawnOptions)!=null&&a.env&&(o.opts.env={...o.opts.env,...e.spawnOptions.env});const s={...e.spawnOptions};return delete s.env,o.opts={...o.opts,...s},o}const Bt={keepaliveInterval:6e4,keepaliveCountMax:10};class A{constructor(r,e){f(this,"config");f(this,"homeDir");f(this,"forwardedServers",[]);this.logger=r,this.client=e}static async init(r,e){const t={...Bt,...e},i=new A(r,new S.Client);return await i.connect(t),i}getForwardedServers(){return this.forwardedServers}getFullHostName(){var r,e;return`${(r=this.config)==null?void 0:r.host}:${(e=this.config)==null?void 0:e.port}`}getUserName(){var r;return(r=this.config)==null?void 0:r.username}async connect(r){return this.config=r,await Ut(this.client,r)}async exec(r){return new Promise((e,t)=>{this.client.exec(r,(i,o)=>{if(i)return t(new Error(`ssh.exec: ${r}: ${i}`));let s="",a="";o.on("close",c=>{c===0?e({stdout:s,stderr:a}):t(new Error(`Command ${r} exited with code ${c}, stdout: ${s}, stderr: ${a}`))}).on("data",c=>{s+=c.toString()}).stderr.on("data",c=>{a+=c.toString()})})})}static async getAuthTypes(r,e){return new Promise(t=>{let i="";const o=new S.Client;o.on("ready",()=>{o.end();const s=this.extractAuthMethods(i);t(s.length===0?["publickey","password"]:s)}),o.on("error",()=>{o.end(),t(["publickey","password"])}),o.connect({host:r,port:e,username:new Date().getTime().toString(),debug:s=>{i+=`${s}
8
8
  `}})})}static extractAuthMethods(r){const e=r.match(/Inbound: Received USERAUTH_FAILURE \((.+)\)/);return e&&e[1]?e[1].split(",").map(t=>t.trim()):[]}async forwardPort(r,e){const t=`ssh.forward:${r.localPort}:${r.remotePort}.id_${D.randomBytes(1).toString("hex")}`;e=e??this.config;const i=new w.RetryablePromise(o=>new Promise((s,a)=>{const c=new S.Client;c.on("ready",()=>{this.logger.info(`${t}.client.ready`),s(c)}),c.on("error",l=>{this.logger.info(`${t}.client.error: ${l}`),o.reset(),a(l)}),c.on("close",()=>{this.logger.info(`${t}.client.closed`),o.reset()}),c.connect(e)}));return await i.ensure(),new Promise((o,s)=>{const a=q.createServer({pauseOnConnect:!0},async c=>{const l=`${t}.sock_${D.randomBytes(1).toString("hex")}`;let u;try{u=await i.ensure()}catch(p){this.logger.info(`${l}.persistentClient.catch: ${p}`),c.end();return}u.setNoDelay(!0),c.setNoDelay(!0);let d;try{d=await It(this.logger,u,"127.0.0.1",0,"127.0.0.1",r.remotePort)}catch(p){this.logger.error(`${l}.forwardOut.err: ${p}`),c.end();return}c.pipe(d),d.pipe(c),c.resume(),d.on("error",p=>{this.logger.error(`${l}.stream.error: ${p}`),c.end(),d.end()}),d.on("close",()=>{c.end(),d.end()}),c.on("close",()=>{this.logger.info(`${l}.localSocket: closed`),c.end(),d.end()})});a.listen(r.localPort,"127.0.0.1",()=>{this.logger.info(`${t}.server: started listening`),this.forwardedServers.push(a),o({server:a})}),a.on("error",c=>{a.close(),s(new Error(`${t}.server: error: ${JSON.stringify(c)}`))}),a.on("close",()=>{this.logger.info(`${t}.server: closed ${JSON.stringify(r)}`),this.forwardedServers=this.forwardedServers.filter(c=>c!==a)})})}closeForwardedPorts(){this.logger.info("[SSH] Closing all forwarded ports..."),this.forwardedServers.forEach(r=>{const e=r.address();if(e&&typeof e!="string"){const t=e;this.logger.info(`[SSH] Closing port forward for server ${t.address}:${t.port}`)}r.close()}),this.forwardedServers=[]}static async checkHostAvailability(r){return new Promise(e=>{wt.lookup(r,t=>{e(!t)})})}static async isPassphraseRequiredForKey(r){return new Promise((e,t)=>{try{return S.utils.parseKey(r)instanceof Error&&e(!0),e(!1)}catch(i){console.log("Error parsing privateKey"),t(new Error(`ssh.isPassphraseRequiredForKey: err ${i}`))}})}async uploadFile(r,e){return await this.withSftp(async t=>new Promise((i,o)=>{t.fastPut(r,e,s=>{if(s){const a=new Error(`ssh.uploadFile: err: ${s}, localPath: ${r}, remotePath: ${e}`);return o(a)}i(!0)})}))}async withSftp(r){return new Promise((e,t)=>{this.client.sftp((i,o)=>{if(i)return t(new Error(`ssh.withSftp: sftp err: ${i}`));r(o).then(e).catch(s=>{t(new Error(`ssh.withSftp.callback: err ${s}`))}).finally(()=>{o==null||o.end()})})})}async writeFileOnTheServer(r,e,t=432){return this.withSftp(async i=>this.writeFile(i,r,e,t))}async getForderStructure(r,e,t={files:[],directories:[]}){return new Promise((i,o)=>{r.readdir(e,async(s,a)=>{if(s)return o(s);for(const c of a){const l=`${e}/${c.filename}`;if(c.attrs.isDirectory()){t.directories.push(l);try{await this.getForderStructure(r,l,t)}catch(u){return o(u instanceof Error?u:new Error(String(u)))}}else t.files.push(l)}i(t)})})}rmdir(r,e){return new Promise((t,i)=>{r.rmdir(e,o=>o?i(o):t(!0))})}unlink(r,e){return new Promise((t,i)=>{r.unlink(e,o=>o?i(o):t(!0))})}async deleteFolder(r){return this.withSftp(async e=>{try{const t=await this.getForderStructure(e,r);this.logger.info("ssh.deleteFolder list of files and directories"),this.logger.info(`ssh.deleteFolder list of files: ${t.files}`),this.logger.info(`ssh.deleteFolder list of directories: ${t.directories}`);for(const i of t.files)this.logger.info(`ssh.deleteFolder unlink file ${i}`),await this.unlink(e,i);t.directories.sort((i,o)=>o.length-i.length);for(const i of t.directories)this.logger.info(`ssh.deleteFolder rmdir ${i}`),await this.rmdir(e,i);return await this.rmdir(e,r),!0}catch(t){this.logger.error(t);const i=t instanceof Error?t.message:"";throw new Error(`ssh.deleteFolder: path: ${r}, message: ${i}`)}})}async readFile(r){return this.withSftp(async e=>new Promise((t,i)=>{e.readFile(r,(o,s)=>{if(o)return i(new Error(`ssh.readFile: ${o}`));t(s.toString())})}))}async chmod(r,e){return this.withSftp(async t=>new Promise((i,o)=>{t.chmod(r,e,s=>s?o(new Error(`ssh.chmod: ${s}, path: ${r}, mode: ${e}`)):i(void 0))}))}async checkFileExists(r){return this.withSftp(async e=>new Promise((t,i)=>{e.stat(r,(o,s)=>{if(o)return(o==null?void 0:o.code)===2?t(!1):i(new Error(`ssh.checkFileExists: err ${o}`));t(s.isFile())})}))}async checkPathExists(r){return this.withSftp(async e=>new Promise((t,i)=>{e.stat(r,(o,s)=>{if(o)return o.code===2?t({exists:!1,isFile:!1,isDirectory:!1}):i(new Error(`ssh.checkPathExists: ${o}`));t({exists:!0,isFile:s.isFile(),isDirectory:s.isDirectory()})})}))}async writeFile(r,e,t,i=432){return new Promise((o,s)=>{r.writeFile(e,t,{mode:i},a=>{if(a)return s(new Error(`ssh.writeFile: err ${a}, remotePath: ${e}`));o(!0)})})}uploadFileUsingExistingSftp(r,e,t,i=432){return new Promise((o,s)=>{g.readFile(e).then(async a=>this.writeFile(r,t,a,i).then(()=>{o(void 0)}).catch(c=>{const l=`uploadFileUsingExistingSftp: ${c}`;this.logger.error(l),s(new Error(l))}))})}async __uploadDirectory(r,e,t,i=432){return new Promise((o,s)=>{F.readdir(e,async(a,c)=>{if(a)return s(new Error(`ssh.__uploadDir: err ${a}, localDir: ${e}, remoteDir: ${t}`));try{await this.__createRemoteDirectory(r,t);for(const l of c){const u=h.join(e,l),d=`${t}/${l}`;F.lstatSync(u).isDirectory()?await this.__uploadDirectory(r,u,d,i):await this.uploadFileUsingExistingSftp(r,u,d,i)}o()}catch(l){const u=`ssh.__uploadDir: catched err ${l}`;this.logger.error(u),s(new Error(u))}})})}async uploadDirectory(r,e,t=432){return new Promise((i,o)=>{this.withSftp(async s=>{try{await this.__uploadDirectory(s,r,e,t),i()}catch(a){o(new Error(`ssh.uploadDirectory: ${a}`))}})})}__createRemoteDirectory(r,e){return new Promise((t,i)=>{const o=e.split("/");let s="";const a=c=>{if(c>=o.length)return t();s+=`${o[c]}/`,r.stat(s,l=>{l?r.mkdir(s,u=>{if(u)return i(new Error(`ssh.__createRemDir: err ${u}, remotePath: ${e}`));a(c+1)}):a(c+1)})};a(0)})}ensureRemoteDirCreated(r,e=493){return this.withSftp(async t=>{const i=r.split("/");let o="";for(const s of i){o+=`${s}/`;try{await new Promise((a,c)=>{t.stat(o,l=>{if(!l)return a();t.mkdir(o,{mode:e},u=>{if(u)return c(new Error(`ssh.createRemoteDir: err ${u}, remotePath: ${r}`));a()})})})}catch(a){throw console.error(`Failed to create directory: ${o}`,a),a}}})}async downloadFile(r,e){return this.withSftp(async t=>new Promise((i,o)=>{t.fastGet(r,e,s=>{if(s)return o(new Error(`ssh.downloadFile: err ${s}, remotePath: ${r}, localPath: ${e}`));i(!0)})}))}close(){this.closeForwardedPorts(),this.client.end()}}async function Ut(n,r,e,t){return new Promise((i,o)=>{n.on("ready",()=>{i(n)}),n.on("error",s=>{o(new Error(`ssh.connect: ${s}`))}),n.on("close",()=>{}),n.connect(r),n.setNoDelay(!0)})}async function It(n,r,e,t,i,o){return new Promise((s,a)=>{r.forwardOut(e,t,i,o,(c,l)=>c?(n.error(`forwardOut.error: ${c}`),a(c)):s(l))})}const Tt="minio-2024-12-18T13-15-44Z",jt="supervisord-0.7.3",qt="supervisord_0.7.3_Linux_64-bit";function y(n){return h.join(n,".platforma_ssh")}function O(n){return h.join(y(n),"binaries")}function Ht(n,r){return h.join(O(n),`pl-${b()}-${P(r)}`)}function Y(n,r){return h.join(Ht(n,r),"binaries")}function U(n,r){return h.join(Y(n,r),"platforma")}function Mt(n,r){return h.join(Y(n,r),"free-port")}function Z(n,r){return h.join(O(n),`minio-2024-12-18T13-15-44Z-${P(r)}`)}function Gt(n,r){return h.join(Z(n,r),"minio")}function zt(n,r){return h.join(O(n),`supervisord-0.7.3-${P(r)}`,qt)}function X(n,r){return h.join(zt(n,r),"supervisord")}function Q(n){return h.join(y(n),"supervisor.conf")}function I(n){return h.join(y(n),"connection.txt")}async function Jt(n,r,e){const t=await N(n,r,e,"--daemon");if(t.stderr)throw new Error(`Can not run ssh Platforma ${t.stderr}`)}async function Lt(n,r,e){const t=await N(n,r,e,"ctl shutdown");if(t.stderr)throw new Error(`Can not stop ssh Platforma ${t.stderr}`)}async function Vt(n,r,e,t){let i;try{i=await N(r,e,t,"ctl status")}catch(c){return{execError:String(c),allAlive:!1}}if(i.stderr)return n.info(`supervisord ctl status: stderr occurred: ${i.stderr}, stdout: ${i.stdout}`),{rawResult:i,allAlive:!1};const o=T(i.stdout,"platforma"),s=T(i.stdout,"minio"),a={rawResult:i,platforma:o,minio:s,allAlive:o&&s};return a.allAlive||(a.minio||n.warn("Minio is not running on the server"),a.platforma||n.warn("Platforma is not running on the server")),a}function Wt(n,r,e,t,i,o,s){const a=Object.entries(r).map(([u,d])=>`${u}="${d}"`).join(","),c=D.randomBytes(16).toString("hex"),l=e;return`
9
9
  [supervisord]
10
10
  logfile=${t}/supervisord.log
package/dist/index.mjs CHANGED
@@ -158,7 +158,7 @@ const St = {
158
158
  windows: "zip"
159
159
  };
160
160
  function R() {
161
- return "1.33.1";
161
+ return "1.33.4";
162
162
  }
163
163
  function Pt() {
164
164
  return { type: "Download", version: R() };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@milaboratories/pl-deployments",
3
- "version": "2.1.3",
4
- "pl-version": "1.33.1",
3
+ "version": "2.2.1",
4
+ "pl-version": "1.33.4",
5
5
  "description": "MiLaboratories Platforma Backend code service run wrapper",
6
6
  "engines": {
7
7
  "node": ">=20.16.0"
@@ -41,7 +41,7 @@
41
41
  "typescript": "~5.5.4",
42
42
  "utility-types": "^3.11.0",
43
43
  "vite": "^5.4.11",
44
- "vitest": "^2.1.8",
44
+ "vitest": "^2.1.9",
45
45
  "@milaboratories/platforma-build-configs": "1.0.3",
46
46
  "@milaboratories/eslint-config": "^1.0.4"
47
47
  },
@@ -53,8 +53,8 @@
53
53
  "undici": "~7.5.0",
54
54
  "yaml": "^2.7.0",
55
55
  "zod": "~3.23.8",
56
- "@milaboratories/pl-config": "^1.4.9",
57
- "@milaboratories/ts-helpers": "^1.2.0"
56
+ "@milaboratories/ts-helpers": "^1.2.0",
57
+ "@milaboratories/pl-config": "^1.4.9"
58
58
  },
59
59
  "scripts": {
60
60
  "type-check": "tsc --noEmit --composite false",
@@ -139,120 +139,137 @@ const mergeDefaultOpsCases: {
139
139
  };
140
140
  expected: LocalPlOptionsFull;
141
141
  }[] = [
142
- {
143
- name: 'should set default values when minimal input is provided',
144
- input: {
145
- ops: {
146
- workingDir: '/test',
142
+ {
143
+ name: 'should set default values when minimal input is provided',
144
+ input: {
145
+ ops: {
146
+ workingDir: '/test',
147
+ config: 'config',
148
+ plBinary: { type: 'Download', version: '1.29.2' },
149
+ },
150
+ numCpu: 4,
151
+ },
152
+ expected: {
153
+ workingDir: '/test',
147
154
  config: 'config',
148
155
  plBinary: { type: 'Download', version: '1.29.2' },
156
+ spawnOptions: {
157
+ env: {
158
+ GOMAXPROCS: '4',
159
+ },
160
+ },
161
+ closeOld: true,
149
162
  },
150
- numCpu: 4,
151
163
  },
152
- expected: {
153
- workingDir: '/test',
154
- config: 'config',
155
- plBinary: { type: 'Download', version: '1.29.2' },
156
- spawnOptions: {
157
- env: {
158
- GOMAXPROCS: '4',
164
+ {
165
+ name: 'should override outermost options when provided',
166
+ input: {
167
+ ops: {
168
+ workingDir: '/test',
169
+ config: 'config',
170
+ // we provided plBinary and closeOld, they should appear in the result
171
+ plBinary: { type: 'Local', path: '/custom/binary' },
172
+ closeOld: false,
159
173
  },
174
+ numCpu: 2,
160
175
  },
161
- closeOld: true,
162
- },
163
- },
164
- {
165
- name: 'should override outermost options when provided',
166
- input: {
167
- ops: {
168
- workingDir: '/test',
176
+ expected: {
177
+ workingDir: '/test',
169
178
  config: 'config',
170
- // we provided plBinary and closeOld, they should appear in the result
179
+ spawnOptions: {
180
+ env: {
181
+ GOMAXPROCS: '2',
182
+ },
183
+ },
171
184
  plBinary: { type: 'Local', path: '/custom/binary' },
172
185
  closeOld: false,
173
186
  },
174
- numCpu: 2,
175
187
  },
176
- expected: {
177
- workingDir: '/test',
178
- config: 'config',
179
- spawnOptions: {
180
- env: {
181
- GOMAXPROCS: '2',
188
+ {
189
+ name: 'should merge env variables when provided',
190
+ input: {
191
+ ops: {
192
+ workingDir: '/test',
193
+ config: 'config',
194
+ plBinary: { type: 'Download', version: '1.29.2' },
195
+ spawnOptions: {
196
+ env: {
197
+ NODE_ENV: 'test',
198
+ DEBUG: 'true',
199
+ },
200
+ },
182
201
  },
202
+ numCpu: 3,
183
203
  },
184
- plBinary: { type: 'Local', path: '/custom/binary' },
185
- closeOld: false,
186
- },
187
- },
188
- {
189
- name: 'should merge env variables when provided',
190
- input: {
191
- ops: {
192
- workingDir: '/test',
204
+ expected: {
205
+ workingDir: '/test',
193
206
  config: 'config',
194
207
  plBinary: { type: 'Download', version: '1.29.2' },
195
208
  spawnOptions: {
196
209
  env: {
210
+ GOMAXPROCS: '3',
197
211
  NODE_ENV: 'test',
198
212
  DEBUG: 'true',
199
213
  },
200
214
  },
215
+ closeOld: true,
201
216
  },
202
- numCpu: 3,
203
217
  },
204
- expected: {
205
- workingDir: '/test',
206
- config: 'config',
207
- plBinary: { type: 'Download', version: '1.29.2' },
208
- spawnOptions: {
209
- env: {
210
- GOMAXPROCS: '3',
211
- NODE_ENV: 'test',
212
- DEBUG: 'true',
218
+ {
219
+ name: 'should override other spawnOptions properties',
220
+ input: {
221
+ ops: {
222
+ workingDir: '/test',
223
+ config: 'config',
224
+ plBinary: { type: 'Download', version: '1.29.2' },
225
+ spawnOptions: {
226
+ stdio: 'inherit',
227
+ detached: true,
228
+ },
213
229
  },
230
+ numCpu: 2,
214
231
  },
215
- closeOld: true,
216
- },
217
- },
218
- {
219
- name: 'should override other spawnOptions properties',
220
- input: {
221
- ops: {
232
+ expected: {
222
233
  workingDir: '/test',
223
234
  config: 'config',
224
235
  plBinary: { type: 'Download', version: '1.29.2' },
225
236
  spawnOptions: {
237
+ env: {
238
+ GOMAXPROCS: '2',
239
+ },
226
240
  stdio: 'inherit',
227
241
  detached: true,
228
242
  },
243
+ closeOld: true,
229
244
  },
230
- numCpu: 2,
231
245
  },
232
- expected: {
233
- workingDir: '/test',
234
- config: 'config',
235
- plBinary: { type: 'Download', version: '1.29.2' },
236
- spawnOptions: {
237
- env: {
238
- GOMAXPROCS: '2',
246
+ {
247
+ name: 'should handle complex case with multiple overrides',
248
+ input: {
249
+ ops: {
250
+ workingDir: '/test',
251
+ config: 'config',
252
+ plBinary: { type: 'Local', path: '/custom/binary' },
253
+ closeOld: false,
254
+ spawnOptions: {
255
+ env: {
256
+ NODE_ENV: 'production',
257
+ LOG_LEVEL: 'debug',
258
+ },
259
+ cwd: '/custom/dir',
260
+ windowsHide: false,
261
+ },
239
262
  },
240
- stdio: 'inherit',
241
- detached: true,
263
+ numCpu: 6,
242
264
  },
243
- closeOld: true,
244
- },
245
- },
246
- {
247
- name: 'should handle complex case with multiple overrides',
248
- input: {
249
- ops: {
250
- workingDir: '/test',
265
+ expected: {
266
+ workingDir: '/test',
251
267
  config: 'config',
252
268
  plBinary: { type: 'Local', path: '/custom/binary' },
253
269
  closeOld: false,
254
270
  spawnOptions: {
255
271
  env: {
272
+ GOMAXPROCS: '6',
256
273
  NODE_ENV: 'production',
257
274
  LOG_LEVEL: 'debug',
258
275
  },
@@ -260,25 +277,8 @@ const mergeDefaultOpsCases: {
260
277
  windowsHide: false,
261
278
  },
262
279
  },
263
- numCpu: 6,
264
- },
265
- expected: {
266
- workingDir: '/test',
267
- config: 'config',
268
- plBinary: { type: 'Local', path: '/custom/binary' },
269
- closeOld: false,
270
- spawnOptions: {
271
- env: {
272
- GOMAXPROCS: '6',
273
- NODE_ENV: 'production',
274
- LOG_LEVEL: 'debug',
275
- },
276
- cwd: '/custom/dir',
277
- windowsHide: false,
278
- },
279
280
  },
280
- },
281
- ];
281
+ ];
282
282
 
283
283
  test.each(mergeDefaultOpsCases)('mergeDefaultOps: $name', ({ name, input, expected }) => {
284
284
  const result = mergeDefaultOps(input.ops, input.numCpu);
@@ -296,139 +296,139 @@ const plProcessOpsCases: {
296
296
  };
297
297
  expected: ProcessOptions;
298
298
  }[] = [
299
- {
300
- name: 'should set basic options with minimal input',
301
- input: {
302
- binaryPath: '/path/to/binary',
303
- configPath: '/path/to/config.yaml',
304
- ops: {
305
- workingDir: '/work/dir',
306
- config: 'config-content',
307
- plBinary: { type: 'Download', version: '1.29.2' },
308
- spawnOptions: {},
309
- closeOld: true,
299
+ {
300
+ name: 'should set basic options with minimal input',
301
+ input: {
302
+ binaryPath: '/path/to/binary',
303
+ configPath: '/path/to/config.yaml',
304
+ ops: {
305
+ workingDir: '/work/dir',
306
+ config: 'config-content',
307
+ plBinary: { type: 'Download', version: '1.29.2' },
308
+ spawnOptions: {},
309
+ closeOld: true,
310
+ },
311
+ workDir: '/work/dir',
310
312
  },
311
- workDir: '/work/dir',
312
- },
313
- expected: {
314
- cmd: '/path/to/binary',
315
- args: ['--config', '/path/to/config.yaml'],
316
- opts: {
317
- env: {},
318
- cwd: '/work/dir',
319
- stdio: ['pipe', 'ignore', 'inherit'],
320
- windowsHide: true,
313
+ expected: {
314
+ cmd: '/path/to/binary',
315
+ args: ['--config', '/path/to/config.yaml'],
316
+ opts: {
317
+ env: {},
318
+ cwd: '/work/dir',
319
+ stdio: ['pipe', 'ignore', 'inherit'],
320
+ windowsHide: true,
321
+ },
321
322
  },
322
323
  },
323
- },
324
- {
325
- name: 'should merge environment variables when provided',
326
- input: {
327
- binaryPath: '/path/to/binary',
328
- configPath: '/config.yaml',
329
- ops: {
330
- workingDir: '/work',
331
- config: 'content',
332
- plBinary: { type: 'Download', version: '1.29.2' },
333
- spawnOptions: {
324
+ {
325
+ name: 'should merge environment variables when provided',
326
+ input: {
327
+ binaryPath: '/path/to/binary',
328
+ configPath: '/config.yaml',
329
+ ops: {
330
+ workingDir: '/work',
331
+ config: 'content',
332
+ plBinary: { type: 'Download', version: '1.29.2' },
333
+ spawnOptions: {
334
+ env: {
335
+ DEBUG: 'true',
336
+ LOG_LEVEL: 'info',
337
+ },
338
+ },
339
+ closeOld: true,
340
+ },
341
+ workDir: '/work',
342
+ },
343
+ expected: {
344
+ cmd: '/path/to/binary',
345
+ args: ['--config', '/config.yaml'],
346
+ opts: {
334
347
  env: {
335
348
  DEBUG: 'true',
336
349
  LOG_LEVEL: 'info',
337
350
  },
351
+ cwd: '/work',
352
+ stdio: ['pipe', 'ignore', 'inherit'],
353
+ windowsHide: true,
338
354
  },
339
- closeOld: true,
340
355
  },
341
- workDir: '/work',
342
356
  },
343
- expected: {
344
- cmd: '/path/to/binary',
345
- args: ['--config', '/config.yaml'],
346
- opts: {
347
- env: {
348
- DEBUG: 'true',
349
- LOG_LEVEL: 'info',
357
+ {
358
+ name: 'should override spawn options when provided',
359
+ input: {
360
+ binaryPath: '/binary',
361
+ configPath: '/config.yaml',
362
+ ops: {
363
+ workingDir: '/work',
364
+ config: 'content',
365
+ plBinary: { type: 'Download', version: '1.29.2' },
366
+ spawnOptions: {
367
+ stdio: 'inherit',
368
+ detached: true,
369
+ shell: true,
370
+ },
371
+ closeOld: true,
350
372
  },
351
- cwd: '/work',
352
- stdio: ['pipe', 'ignore', 'inherit'],
353
- windowsHide: true,
373
+ workDir: '/work',
354
374
  },
355
- },
356
- },
357
- {
358
- name: 'should override spawn options when provided',
359
- input: {
360
- binaryPath: '/binary',
361
- configPath: '/config.yaml',
362
- ops: {
363
- workingDir: '/work',
364
- config: 'content',
365
- plBinary: { type: 'Download', version: '1.29.2' },
366
- spawnOptions: {
375
+ expected: {
376
+ cmd: '/binary',
377
+ args: ['--config', '/config.yaml'],
378
+ opts: {
379
+ env: {},
380
+ cwd: '/work',
367
381
  stdio: 'inherit',
368
382
  detached: true,
369
383
  shell: true,
384
+ windowsHide: true,
370
385
  },
371
- closeOld: true,
372
386
  },
373
- workDir: '/work',
374
387
  },
375
- expected: {
376
- cmd: '/binary',
377
- args: ['--config', '/config.yaml'],
378
- opts: {
379
- env: {},
380
- cwd: '/work',
381
- stdio: 'inherit',
382
- detached: true,
383
- shell: true,
384
- windowsHide: true,
388
+ {
389
+ name: 'should handle complex case with multiple options',
390
+ input: {
391
+ binaryPath: '/bin/platforma',
392
+ configPath: '/etc/platforma/config.yaml',
393
+ ops: {
394
+ workingDir: '/var/platforma',
395
+ config: 'yaml content',
396
+ plBinary: { type: 'Download', version: '1.29.2' },
397
+ spawnOptions: {
398
+ env: {
399
+ PL_DEBUG: 'true',
400
+ PL_MODE: 'development',
401
+ GOMAXPROCS: '4',
402
+ },
403
+ stdio: ['ignore', 'pipe', 'pipe'],
404
+ detached: false,
405
+ windowsHide: false,
406
+ uid: 1000,
407
+ gid: 1000,
408
+ },
409
+ closeOld: true,
410
+ },
411
+ workDir: '/var/platforma/runtime',
385
412
  },
386
- },
387
- },
388
- {
389
- name: 'should handle complex case with multiple options',
390
- input: {
391
- binaryPath: '/bin/platforma',
392
- configPath: '/etc/platforma/config.yaml',
393
- ops: {
394
- workingDir: '/var/platforma',
395
- config: 'yaml content',
396
- plBinary: { type: 'Download', version: '1.29.2' },
397
- spawnOptions: {
413
+ expected: {
414
+ cmd: '/bin/platforma',
415
+ args: ['--config', '/etc/platforma/config.yaml'],
416
+ opts: {
398
417
  env: {
399
418
  PL_DEBUG: 'true',
400
419
  PL_MODE: 'development',
401
420
  GOMAXPROCS: '4',
402
421
  },
422
+ cwd: '/var/platforma/runtime',
403
423
  stdio: ['ignore', 'pipe', 'pipe'],
404
424
  detached: false,
405
425
  windowsHide: false,
406
426
  uid: 1000,
407
427
  gid: 1000,
408
428
  },
409
- closeOld: true,
410
- },
411
- workDir: '/var/platforma/runtime',
412
- },
413
- expected: {
414
- cmd: '/bin/platforma',
415
- args: ['--config', '/etc/platforma/config.yaml'],
416
- opts: {
417
- env: {
418
- PL_DEBUG: 'true',
419
- PL_MODE: 'development',
420
- GOMAXPROCS: '4',
421
- },
422
- cwd: '/var/platforma/runtime',
423
- stdio: ['ignore', 'pipe', 'pipe'],
424
- detached: false,
425
- windowsHide: false,
426
- uid: 1000,
427
- gid: 1000,
428
429
  },
429
430
  },
430
- },
431
- ];
431
+ ];
432
432
 
433
433
  test.each(plProcessOpsCases)('plProcessOps: $name', ({ name, input, expected }) => {
434
434
  const result = plProcessOps(