@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 +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +5 -5
- package/src/local/pl.test.ts +192 -192
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.
|
|
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
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@milaboratories/pl-deployments",
|
|
3
|
-
"version": "2.1
|
|
4
|
-
"pl-version": "1.33.
|
|
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.
|
|
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/
|
|
57
|
-
"@milaboratories/
|
|
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",
|
package/src/local/pl.test.ts
CHANGED
|
@@ -139,120 +139,137 @@ const mergeDefaultOpsCases: {
|
|
|
139
139
|
};
|
|
140
140
|
expected: LocalPlOptionsFull;
|
|
141
141
|
}[] = [
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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
|
-
|
|
185
|
-
|
|
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
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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
|
-
|
|
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
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
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
|
-
|
|
241
|
-
detached: true,
|
|
263
|
+
numCpu: 6,
|
|
242
264
|
},
|
|
243
|
-
|
|
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
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
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
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
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
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
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
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
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
|
-
|
|
352
|
-
stdio: ['pipe', 'ignore', 'inherit'],
|
|
353
|
-
windowsHide: true,
|
|
373
|
+
workDir: '/work',
|
|
354
374
|
},
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
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
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
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
|
-
|
|
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(
|